[MM-11879] Upgrade to RNN v2
This commit is contained in:
commit
175c0df5d9
252 changed files with 3767 additions and 4141 deletions
3
Makefile
3
Makefile
|
|
@ -90,7 +90,6 @@ post-install:
|
|||
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-messageformat/package.json
|
||||
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json
|
||||
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
|
||||
@sed -i'' -e "s|super.onBackPressed();|this.moveTaskToBack(true);|g" node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java
|
||||
@sed -i'' -e "s|compile 'com.facebook.react:react-native:0.17.+'|compile 'com.facebook.react:react-native:+'|g" node_modules/react-native-bottom-sheet/android/build.gradle
|
||||
@if [ $(shell grep "const Platform" node_modules/react-native/Libraries/Lists/VirtualizedList.js | grep -civ grep) -eq 0 ]; then \
|
||||
sed $ -i'' -e "s|const ReactNative = require('ReactNative');|const ReactNative = require('ReactNative');`echo $\\\\\\r;`const Platform = require('Platform');|g" node_modules/react-native/Libraries/Lists/VirtualizedList.js; \
|
||||
|
|
@ -161,7 +160,7 @@ run-android: | check-device-android pre-run prepare-android-build ## Runs the ap
|
|||
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
|
||||
echo Starting React Native packager server; \
|
||||
npm start & echo Running Android app in development; \
|
||||
if [ ! -z ${VARIANT} ]; then \
|
||||
if [ ! -z ${VARIANT} ]; then \
|
||||
react-native run-android --no-packager --variant=${VARIANT}; \
|
||||
else \
|
||||
react-native run-android --no-packager; \
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ android {
|
|||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
missingDimensionStrategy "RNN.reactNativeVersion", "reactNative57_5"
|
||||
versionCode 207
|
||||
versionName "1.21.0"
|
||||
multiDexEnabled = true
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ package com.mattermost.rnbeta;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.reactnativenavigation.controllers.SplashActivity;
|
||||
|
||||
public class MainActivity extends SplashActivity {
|
||||
import com.reactnativenavigation.NavigationActivity;
|
||||
|
||||
public class MainActivity extends NavigationActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.launch_screen);
|
||||
|
||||
/**
|
||||
* Reference: https://stackoverflow.com/questions/7944338/resume-last-activity-when-launcher-icon-is-clicked
|
||||
|
|
@ -23,9 +25,4 @@ public class MainActivity extends SplashActivity {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSplashLayout() {
|
||||
return R.layout.launch_screen;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,26 @@ import com.mattermost.share.RealPathUtil;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import com.reactnativedocumentpicker.ReactNativeDocumentPicker;
|
||||
import com.oblador.keychain.KeychainPackage;
|
||||
import com.reactlibrary.RNReactNativeDocViewerPackage;
|
||||
|
|
@ -26,10 +40,6 @@ import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
|
|||
import com.reactnativecommunity.netinfo.NetInfoPackage;
|
||||
import com.reactnativecommunity.webview.RNCWebViewPackage;
|
||||
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import com.imagepicker.ImagePickerPackage;
|
||||
import com.gnet.bottomsheet.RNBottomSheetPackage;
|
||||
import com.learnium.RNDeviceInfo.RNDeviceInfo;
|
||||
|
|
@ -37,6 +47,8 @@ import com.psykar.cookiemanager.CookieManagerPackage;
|
|||
import com.oblador.vectoricons.VectorIconsPackage;
|
||||
import com.BV.LinearGradient.LinearGradientPackage;
|
||||
import com.reactnativenavigation.NavigationApplication;
|
||||
import com.reactnativenavigation.react.NavigationReactNativeHost;
|
||||
import com.reactnativenavigation.react.ReactGateway;
|
||||
import com.wix.reactnativenotifications.RNNotificationsPackage;
|
||||
import com.wix.reactnativenotifications.core.notification.INotificationsApplication;
|
||||
import com.wix.reactnativenotifications.core.notification.IPushNotification;
|
||||
|
|
@ -46,19 +58,9 @@ import com.wix.reactnativenotifications.core.AppLaunchHelper;
|
|||
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
|
||||
import com.wix.reactnativenotifications.core.JsIOHelper;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class MainApplication extends NavigationApplication implements INotificationsApplication, INotificationsDrawerApplication {
|
||||
public NotificationsLifecycleFacade notificationsLifecycleFacade;
|
||||
public static MainApplication instance;
|
||||
|
||||
public Boolean sharedExtensionIsOpened = false;
|
||||
|
||||
public long APP_START_TIME;
|
||||
|
|
@ -69,6 +71,19 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
public long PROCESS_PACKAGES_START;
|
||||
public long PROCESS_PACKAGES_END;
|
||||
|
||||
private Bundle mManagedConfig = null;
|
||||
|
||||
@Override
|
||||
protected ReactGateway createReactGateway() {
|
||||
ReactNativeHost host = new NavigationReactNativeHost(this, isDebug(), createAdditionalReactPackages()) {
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
};
|
||||
return new ReactGateway(this, isDebug(), host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return BuildConfig.DEBUG;
|
||||
|
|
@ -107,45 +122,30 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
instance = this;
|
||||
|
||||
registerActivityLifecycleCallbacks(new ManagedActivityLifecycleCallbacks());
|
||||
|
||||
// Delete any previous temp files created by the app
|
||||
File tempFolder = new File(getApplicationContext().getCacheDir(), "mmShare");
|
||||
RealPathUtil.deleteTempFiles(tempFolder);
|
||||
Log.i("ReactNative", "Cleaning temp cache " + tempFolder.getAbsolutePath());
|
||||
|
||||
// Create an object of the custom facade impl
|
||||
notificationsLifecycleFacade = NotificationsLifecycleFacade.getInstance();
|
||||
// Attach it to react-native-navigation
|
||||
setActivityCallbacks(notificationsLifecycleFacade);
|
||||
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
|
||||
// Uncomment to listen to react markers for build that has telemetry enabled
|
||||
// addReactMarkerListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clearHostOnActivityDestroy(Activity activity) {
|
||||
// This solves the issue where the splash screen does not go away
|
||||
// after the app is killed by the OS cause of memory or a long time in the background
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade defaultFacade, AppLaunchHelper defaultAppLaunchHelper) {
|
||||
return new CustomPushNotification(
|
||||
context,
|
||||
bundle,
|
||||
notificationsLifecycleFacade, // Instead of defaultFacade!!!
|
||||
defaultFacade,
|
||||
defaultAppLaunchHelper,
|
||||
new JsIOHelper()
|
||||
);
|
||||
|
|
@ -156,6 +156,51 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
return new CustomPushNotificationDrawer(context, defaultAppLaunchHelper);
|
||||
}
|
||||
|
||||
public ReactContext getRunningReactContext() {
|
||||
final ReactGateway reactGateway = getReactGateway();
|
||||
|
||||
if (reactGateway == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reactGateway
|
||||
.getReactNativeHost()
|
||||
.getReactInstanceManager()
|
||||
.getCurrentReactContext();
|
||||
}
|
||||
|
||||
public synchronized Bundle loadManagedConfig(Context ctx) {
|
||||
if (ctx != null) {
|
||||
RestrictionsManager myRestrictionsMgr =
|
||||
(RestrictionsManager) ctx.getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
|
||||
mManagedConfig = myRestrictionsMgr.getApplicationRestrictions();
|
||||
myRestrictionsMgr = null;
|
||||
|
||||
if (mManagedConfig!= null && mManagedConfig.size() > 0) {
|
||||
return mManagedConfig;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized Bundle getManagedConfig() {
|
||||
if (mManagedConfig!= null && mManagedConfig.size() > 0) {
|
||||
return mManagedConfig;
|
||||
}
|
||||
|
||||
ReactContext ctx = getRunningReactContext();
|
||||
|
||||
if (ctx != null) {
|
||||
return loadManagedConfig(ctx);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addReactMarkerListener() {
|
||||
ReactMarker.addListener(new ReactMarker.MarkerListener() {
|
||||
@Override
|
||||
|
|
@ -169,7 +214,7 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
PROCESS_PACKAGES_END = System.currentTimeMillis();
|
||||
} else if (name.toString() == ReactMarkerConstants.CONTENT_APPEARED.toString()) {
|
||||
CONTENT_APPEARED = System.currentTimeMillis();
|
||||
ReactContext ctx = getReactGateway().getReactContext();
|
||||
ReactContext ctx = getRunningReactContext();
|
||||
|
||||
if (ctx != null) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.app.Activity;
|
||||
import android.app.Application.ActivityLifecycleCallbacks;
|
||||
import android.content.Context;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
public class ManagedActivityLifecycleCallbacks implements ActivityLifecycleCallbacks {
|
||||
private static final String TAG = ManagedActivityLifecycleCallbacks.class.getSimpleName();
|
||||
|
||||
private final IntentFilter restrictionsFilter =
|
||||
new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
|
||||
|
||||
private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
|
||||
@Override public void onReceive(Context ctx, Intent intent) {
|
||||
if (ctx != null) {
|
||||
Bundle managedConfig = MainApplication.instance.loadManagedConfig(ctx);
|
||||
|
||||
// Check current configuration settings, change your app's UI and
|
||||
// functionality as necessary.
|
||||
Log.i(TAG, "Managed Configuration Changed");
|
||||
sendConfigChanged(managedConfig);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
MattermostManagedModule managedModule = MattermostManagedModule.getInstance();
|
||||
if (managedModule != null && managedModule.isBlurAppScreenEnabled() && activity != null) {
|
||||
activity.getWindow().setFlags(LayoutParams.FLAG_SECURE,
|
||||
LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
Bundle managedConfig = MainApplication.instance.getManagedConfig();
|
||||
if (managedConfig != null && activity != null) {
|
||||
activity.registerReceiver(restrictionsReceiver, restrictionsFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
ReactContext ctx = MainApplication.instance.getRunningReactContext();
|
||||
Bundle managedConfig = MainApplication.instance.getManagedConfig();
|
||||
|
||||
if (ctx != null) {
|
||||
Bundle newConfig = MainApplication.instance.loadManagedConfig(ctx);
|
||||
if (!equalBundles(newConfig, managedConfig)) {
|
||||
Log.i(TAG, "onResumed Managed Configuration Changed");
|
||||
sendConfigChanged(newConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
Bundle managedConfig = MainApplication.instance.getManagedConfig();
|
||||
|
||||
if (managedConfig != null) {
|
||||
try {
|
||||
activity.unregisterReceiver(restrictionsReceiver);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Just ignore this cause the receiver wasn't registered for this activity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
}
|
||||
|
||||
private void sendConfigChanged(Bundle config) {
|
||||
WritableMap result = Arguments.createMap();
|
||||
if (config != null) {
|
||||
result = Arguments.fromBundle(config);
|
||||
}
|
||||
ReactContext ctx = MainApplication.instance.getRunningReactContext();
|
||||
|
||||
if (ctx != null) {
|
||||
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("managedConfigDidChange", result);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean equalBundles(Bundle one, Bundle two) {
|
||||
if (one == null || two == null)
|
||||
return false;
|
||||
|
||||
if(one.size() != two.size())
|
||||
return false;
|
||||
|
||||
Set<String> setOne = new ArraySet<String>();
|
||||
setOne.addAll(one.keySet());
|
||||
setOne.addAll(two.keySet());
|
||||
Object valueOne;
|
||||
Object valueTwo;
|
||||
|
||||
for(String key : setOne) {
|
||||
if (!one.containsKey(key) || !two.containsKey(key))
|
||||
return false;
|
||||
|
||||
valueOne = one.get(key);
|
||||
valueTwo = two.get(key);
|
||||
if(valueOne instanceof Bundle && valueTwo instanceof Bundle &&
|
||||
!equalBundles((Bundle) valueOne, (Bundle) valueTwo)) {
|
||||
return false;
|
||||
}
|
||||
else if(valueOne == null) {
|
||||
if(valueTwo != null)
|
||||
return false;
|
||||
}
|
||||
else if(!valueOne.equals(valueTwo))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ public class MattermostManagedModule extends ReactContextBaseJavaModule {
|
|||
@ReactMethod
|
||||
public void getConfig(final Promise promise) {
|
||||
try {
|
||||
Bundle config = NotificationsLifecycleFacade.getInstance().getManagedConfig();
|
||||
Bundle config = MainApplication.instance.getManagedConfig();
|
||||
|
||||
if (config != null) {
|
||||
Object result = Arguments.fromBundle(config);
|
||||
|
|
|
|||
|
|
@ -1,251 +0,0 @@
|
|||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.ArraySet;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import com.reactnativenavigation.NavigationApplication;
|
||||
import com.reactnativenavigation.controllers.ActivityCallbacks;
|
||||
import com.reactnativenavigation.react.ReactGateway;
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
|
||||
public class NotificationsLifecycleFacade extends ActivityCallbacks implements AppLifecycleFacade {
|
||||
private static final String TAG = NotificationsLifecycleFacade.class.getSimpleName();
|
||||
private static NotificationsLifecycleFacade instance;
|
||||
|
||||
private Bundle managedConfig = null;
|
||||
private Activity mVisibleActivity;
|
||||
private Set<AppVisibilityListener> mListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private final IntentFilter restrictionsFilter =
|
||||
new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
|
||||
|
||||
private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
|
||||
@Override public void onReceive(Context context, Intent intent) {
|
||||
if (context != null) {
|
||||
// Get the current configuration bundle
|
||||
RestrictionsManager myRestrictionsMgr =
|
||||
(RestrictionsManager) context
|
||||
.getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
managedConfig = myRestrictionsMgr.getApplicationRestrictions();
|
||||
|
||||
// Check current configuration settings, change your app's UI and
|
||||
// functionality as necessary.
|
||||
Log.i("ReactNative", "Managed Configuration Changed");
|
||||
sendConfigChanged(managedConfig);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static NotificationsLifecycleFacade getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new NotificationsLifecycleFacade();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
MattermostManagedModule managedModule = MattermostManagedModule.getInstance();
|
||||
if (managedModule != null && managedModule.isBlurAppScreenEnabled() && activity != null) {
|
||||
activity.getWindow().setFlags(LayoutParams.FLAG_SECURE,
|
||||
LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
if (managedConfig != null && managedConfig.size() > 0 && activity != null) {
|
||||
activity.registerReceiver(restrictionsReceiver, restrictionsFilter);
|
||||
}
|
||||
|
||||
if (activity != null) {
|
||||
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
switchToVisible(activity);
|
||||
|
||||
ReactContext ctx = getRunningReactContext();
|
||||
if (managedConfig != null && managedConfig.size() > 0 && ctx != null) {
|
||||
|
||||
RestrictionsManager myRestrictionsMgr =
|
||||
(RestrictionsManager) ctx
|
||||
.getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
|
||||
Bundle newConfig = myRestrictionsMgr.getApplicationRestrictions();
|
||||
if (!equalBundles(newConfig ,managedConfig)) {
|
||||
Log.i("ReactNative", "onResumed Managed Configuration Changed");
|
||||
managedConfig = newConfig;
|
||||
sendConfigChanged(managedConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
switchToInvisible(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
switchToInvisible(activity);
|
||||
if (managedConfig != null && managedConfig.size() > 0) {
|
||||
try {
|
||||
activity.unregisterReceiver(restrictionsReceiver);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Just ignore this cause the receiver wasn't registered for this activity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
switchToInvisible(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReactInitialized() {
|
||||
return NavigationApplication.instance.isReactContextInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactContext getRunningReactContext() {
|
||||
final ReactGateway reactGateway = NavigationApplication.instance.getReactGateway();
|
||||
if (reactGateway == null || !reactGateway.isInitialized()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reactGateway.getReactContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppVisible() {
|
||||
return mVisibleActivity != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addVisibilityListener(AppVisibilityListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeVisibilityListener(AppVisibilityListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (mVisibleActivity != null) {
|
||||
Intent intent = new Intent("onConfigurationChanged");
|
||||
intent.putExtra("newConfig", newConfig);
|
||||
mVisibleActivity.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void switchToVisible(Activity activity) {
|
||||
if (mVisibleActivity == null) {
|
||||
mVisibleActivity = activity;
|
||||
Log.v(TAG, "Activity is now visible ("+activity+")");
|
||||
for (AppVisibilityListener listener : mListeners) {
|
||||
listener.onAppVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void switchToInvisible(Activity activity) {
|
||||
if (mVisibleActivity == activity) {
|
||||
mVisibleActivity = null;
|
||||
Log.v(TAG, "Activity is now NOT visible ("+activity+")");
|
||||
for (AppVisibilityListener listener : mListeners) {
|
||||
listener.onAppNotVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void LoadManagedConfig(ReactContext ctx) {
|
||||
if (ctx != null) {
|
||||
RestrictionsManager myRestrictionsMgr =
|
||||
(RestrictionsManager) ctx
|
||||
.getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
|
||||
managedConfig = myRestrictionsMgr.getApplicationRestrictions();
|
||||
myRestrictionsMgr = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Bundle getManagedConfig() {
|
||||
if (managedConfig!= null && managedConfig.size() > 0) {
|
||||
return managedConfig;
|
||||
}
|
||||
|
||||
ReactContext ctx = getRunningReactContext();
|
||||
|
||||
if (ctx != null) {
|
||||
LoadManagedConfig(ctx);
|
||||
return managedConfig;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void sendConfigChanged(Bundle config) {
|
||||
Object result = Arguments.fromBundle(config);
|
||||
ReactContext ctx = getRunningReactContext();
|
||||
if (ctx != null) {
|
||||
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).
|
||||
emit("managedConfigDidChange", result);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean equalBundles(Bundle one, Bundle two) {
|
||||
if (one == null || two == null)
|
||||
return false;
|
||||
|
||||
if(one.size() != two.size())
|
||||
return false;
|
||||
|
||||
Set<String> setOne = new ArraySet<String>();
|
||||
setOne.addAll(one.keySet());
|
||||
setOne.addAll(two.keySet());
|
||||
Object valueOne;
|
||||
Object valueTwo;
|
||||
|
||||
for(String key : setOne) {
|
||||
if (!one.containsKey(key) || !two.containsKey(key))
|
||||
return false;
|
||||
|
||||
valueOne = one.get(key);
|
||||
valueTwo = two.get(key);
|
||||
if(valueOne instanceof Bundle && valueTwo instanceof Bundle &&
|
||||
!equalBundles((Bundle) valueOne, (Bundle) valueTwo)) {
|
||||
return false;
|
||||
}
|
||||
else if(valueOne == null) {
|
||||
if(valueTwo != null)
|
||||
return false;
|
||||
}
|
||||
else if(!valueOne.equals(valueTwo))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ buildscript {
|
|||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
||||
|
|
@ -37,6 +39,7 @@ subprojects {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
maven {
|
||||
|
|
@ -47,5 +50,8 @@ allprojects {
|
|||
// Local Maven repo containing AARs with JSC library built for Android
|
||||
url "$rootDir/../node_modules/jsc-android/dist"
|
||||
}
|
||||
maven {
|
||||
url "https://jitpack.io"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ project(':jail-monkey').projectDir = new File(rootProject.projectDir, '../node_m
|
|||
include ':react-native-local-auth'
|
||||
project(':react-native-local-auth').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-local-auth/android')
|
||||
include ':react-native-navigation'
|
||||
project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app/')
|
||||
project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/lib/android/app/')
|
||||
include ':react-native-image-picker'
|
||||
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
|
||||
include ':react-native-bottom-sheet'
|
||||
|
|
|
|||
361
app/actions/navigation.js
Normal file
361
app/actions/navigation.js
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Platform} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import merge from 'deepmerge';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
|
||||
export function resetToChannel(passProps = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const theme = getTheme(getState());
|
||||
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Channel',
|
||||
passProps,
|
||||
options: {
|
||||
layout: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
statusBar: {
|
||||
visible: true,
|
||||
},
|
||||
topBar: {
|
||||
visible: false,
|
||||
height: 0,
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
title: '',
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function resetToSelectServer(allowOtherServers) {
|
||||
return (dispatch, getState) => {
|
||||
const theme = getTheme(getState());
|
||||
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'SelectServer',
|
||||
passProps: {
|
||||
allowOtherServers,
|
||||
},
|
||||
options: {
|
||||
statusBar: {
|
||||
visible: true,
|
||||
},
|
||||
topBar: {
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
title: '',
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
visible: false,
|
||||
height: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function resetToTeams(name, title, passProps = {}, options = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const theme = getTheme(getState());
|
||||
const defaultOptions = {
|
||||
layout: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
statusBar: {
|
||||
visible: true,
|
||||
},
|
||||
topBar: {
|
||||
visible: true,
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
text: title,
|
||||
},
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
title: '',
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name,
|
||||
passProps,
|
||||
options: merge(defaultOptions, options),
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function goToScreen(name, title, passProps = {}, options = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const theme = getTheme(state);
|
||||
const defaultOptions = {
|
||||
layout: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
topBar: {
|
||||
animate: true,
|
||||
visible: true,
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
title: '',
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
text: title,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Navigation.push(componentId, {
|
||||
component: {
|
||||
name,
|
||||
passProps,
|
||||
options: merge(defaultOptions, options),
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function popTopScreen() {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
|
||||
Navigation.pop(componentId);
|
||||
};
|
||||
}
|
||||
|
||||
export function popToRoot() {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
|
||||
Navigation.popToRoot(componentId).catch(() => {
|
||||
// RNN returns a promise rejection if there are no screens
|
||||
// atop the root screen to pop. We'll do nothing in this
|
||||
// case but we will catch the rejection here so that the
|
||||
// caller doesn't have to.
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function showModal(name, title, passProps = {}, options = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const theme = getTheme(getState());
|
||||
const defaultOptions = {
|
||||
layout: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
statusBar: {
|
||||
visible: true,
|
||||
},
|
||||
topBar: {
|
||||
animate: true,
|
||||
visible: true,
|
||||
backButton: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
title: '',
|
||||
},
|
||||
background: {
|
||||
color: theme.sidebarHeaderBg,
|
||||
},
|
||||
title: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
text: title,
|
||||
},
|
||||
leftButtonColor: theme.sidebarHeaderTextColor,
|
||||
rightButtonColor: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
};
|
||||
|
||||
Navigation.showModal({
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name,
|
||||
passProps,
|
||||
options: merge(defaultOptions, options),
|
||||
},
|
||||
}],
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function showModalOverCurrentContext(name, passProps = {}, options = {}) {
|
||||
return (dispatch) => {
|
||||
const title = '';
|
||||
const animationsEnabled = (Platform.OS === 'android').toString();
|
||||
const defaultOptions = {
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
layout: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
topBar: {
|
||||
visible: false,
|
||||
height: 0,
|
||||
},
|
||||
animations: {
|
||||
showModal: {
|
||||
enabled: animationsEnabled,
|
||||
alpha: {
|
||||
from: 0,
|
||||
to: 1,
|
||||
duration: 250,
|
||||
},
|
||||
},
|
||||
dismissModal: {
|
||||
enabled: animationsEnabled,
|
||||
alpha: {
|
||||
from: 1,
|
||||
to: 0,
|
||||
duration: 250,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const mergeOptions = merge(defaultOptions, options);
|
||||
|
||||
dispatch(showModal(name, title, passProps, mergeOptions));
|
||||
};
|
||||
}
|
||||
|
||||
export function showSearchModal(initialValue = '') {
|
||||
return (dispatch) => {
|
||||
const name = 'Search';
|
||||
const title = '';
|
||||
const passProps = {initialValue};
|
||||
const options = {
|
||||
topBar: {
|
||||
visible: false,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
|
||||
dispatch(showModal(name, title, passProps, options));
|
||||
};
|
||||
}
|
||||
|
||||
export function dismissModal(options = {}) {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
|
||||
Navigation.dismissModal(componentId, options);
|
||||
};
|
||||
}
|
||||
|
||||
export function dismissAllModals(options = {}) {
|
||||
return () => {
|
||||
Navigation.dismissAllModals(options).catch(() => {
|
||||
// RNN returns a promise rejection if there are no modals to
|
||||
// dismiss. We'll do nothing in this case but we will catch
|
||||
// the rejection here so that the caller doesn't have to.
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function peek(name, passProps = {}, options = {}) {
|
||||
return () => {
|
||||
const componentId = EphemeralStore.getTopComponentId();
|
||||
const defaultOptions = {
|
||||
preview: {
|
||||
commit: false,
|
||||
},
|
||||
};
|
||||
|
||||
Navigation.push(componentId, {
|
||||
component: {
|
||||
name,
|
||||
passProps,
|
||||
options: merge(defaultOptions, options),
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function setButtons(componentId, buttons = {leftButtons: [], rightButtons: []}) {
|
||||
return () => {
|
||||
Navigation.mergeOptions(componentId, {
|
||||
topBar: {
|
||||
...buttons,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function showOverlay(name, passProps, options = {}) {
|
||||
return () => {
|
||||
const defaultOptions = {
|
||||
overlay: {
|
||||
interceptTouchOutside: false,
|
||||
},
|
||||
};
|
||||
|
||||
Navigation.showOverlay({
|
||||
component: {
|
||||
name,
|
||||
passProps,
|
||||
options: merge(defaultOptions, options),
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function dismissOverlay(componentId) {
|
||||
return () => {
|
||||
return Navigation.dismissOverlay(componentId).catch(() => {
|
||||
// RNN returns a promise rejection if there is no modal with
|
||||
// this componentId to dismiss. We'll do nothing in this case
|
||||
// but we will catch the rejection here so that the caller
|
||||
// doesn't have to.
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export function handleSearchDraftChanged(text) {
|
||||
|
|
@ -13,25 +11,3 @@ export function handleSearchDraftChanged(text) {
|
|||
}, getState);
|
||||
};
|
||||
}
|
||||
|
||||
export function showSearchModal(navigator, initialValue = '') {
|
||||
return (dispatch, getState) => {
|
||||
const theme = getTheme(getState());
|
||||
|
||||
const options = {
|
||||
screen: 'Search',
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
overrideBackPress: true,
|
||||
passProps: {
|
||||
initialValue,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
};
|
||||
|
||||
navigator.showModal(options);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,14 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`profile_picture_button should match snapshot 1`] = `
|
||||
<AttachmentButton
|
||||
<Connect(AttachmentButton)
|
||||
blurTextBox={[MockFunction]}
|
||||
browseFileTypes="public.item"
|
||||
canBrowseFiles={true}
|
||||
canBrowsePhotoLibrary={true}
|
||||
canBrowseVideoLibrary={true}
|
||||
canTakePhoto={true}
|
||||
canTakeVideo={true}
|
||||
extraOptions={
|
||||
Array [
|
||||
null,
|
||||
]
|
||||
}
|
||||
maxFileCount={5}
|
||||
maxFileSize={20971520}
|
||||
navigator={
|
||||
Object {
|
||||
"dismissModal": [MockFunction],
|
||||
"push": [MockFunction],
|
||||
"setButtons": [MockFunction],
|
||||
"setOnNavigatorEvent": [MockFunction],
|
||||
}
|
||||
}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
@ -53,6 +38,5 @@ exports[`profile_picture_button should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
uploadFiles={[MockFunction]}
|
||||
validMimeTypes={Array []}
|
||||
/>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@ const {View: AnimatedView} = Animated;
|
|||
|
||||
export default class AnnouncementBanner extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
bannerColor: PropTypes.string,
|
||||
bannerDismissed: PropTypes.bool,
|
||||
bannerEnabled: PropTypes.bool,
|
||||
bannerText: PropTypes.string,
|
||||
bannerTextColor: PropTypes.string,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
|
@ -52,23 +54,16 @@ export default class AnnouncementBanner extends PureComponent {
|
|||
}
|
||||
|
||||
handlePress = () => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
|
||||
navigator.push({
|
||||
screen: 'ExpandedAnnouncementBanner',
|
||||
title: this.context.intl.formatMessage({
|
||||
id: 'mobile.announcement_banner.title',
|
||||
defaultMessage: 'Announcement',
|
||||
}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
const screen = 'ExpandedAnnouncementBanner';
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.announcement_banner.title',
|
||||
defaultMessage: 'Announcement',
|
||||
});
|
||||
|
||||
actions.goToScreen(screen, title);
|
||||
};
|
||||
|
||||
toggleBanner = (show = true) => {
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@ jest.useFakeTimers();
|
|||
|
||||
describe('AnnouncementBanner', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
goToScreen: jest.fn(),
|
||||
},
|
||||
bannerColor: '#ddd',
|
||||
bannerDismissed: false,
|
||||
bannerEnabled: true,
|
||||
bannerText: 'Banner Text',
|
||||
bannerTextColor: '#fff',
|
||||
navigator: {},
|
||||
theme: Preferences.THEMES.default,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
import AnnouncementBanner from './announcement_banner';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -23,4 +26,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(AnnouncementBanner);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Clipboard, Platform, Text} from 'react-native';
|
||||
import {Clipboard, Text} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
|
@ -14,10 +14,12 @@ import BottomSheet from 'app/utils/bottom_sheet';
|
|||
|
||||
export default class AtMention extends React.PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
isSearchResult: PropTypes.bool,
|
||||
mentionName: PropTypes.string.isRequired,
|
||||
mentionStyle: CustomPropTypes.Style,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onPostPress: PropTypes.func,
|
||||
textStyle: CustomPropTypes.Style,
|
||||
teammateNameDisplay: PropTypes.string,
|
||||
|
|
@ -48,29 +50,15 @@ export default class AtMention extends React.PureComponent {
|
|||
}
|
||||
|
||||
goToUserProfile = () => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
const options = {
|
||||
screen: 'UserProfile',
|
||||
title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
userId: this.state.user.id,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
const screen = 'UserProfile';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const passProps = {
|
||||
userId: this.state.user.id,
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
navigator.push(options);
|
||||
} else {
|
||||
navigator.showModal(options);
|
||||
}
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
};
|
||||
|
||||
getUserDetailsFromMentionName(props) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getUsersByUsername} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
import AtMention from './at_mention';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -17,4 +20,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(AtMention);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AtMention);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ const ShareExtension = NativeModules.MattermostShare;
|
|||
|
||||
export default class AttachmentButton extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
blurTextBox: PropTypes.func.isRequired,
|
||||
browseFileTypes: PropTypes.string,
|
||||
validMimeTypes: PropTypes.array,
|
||||
|
|
@ -39,7 +42,6 @@ export default class AttachmentButton extends PureComponent {
|
|||
fileCount: PropTypes.number,
|
||||
maxFileCount: PropTypes.number.isRequired,
|
||||
maxFileSize: PropTypes.number.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onShowFileMaxWarning: PropTypes.func,
|
||||
onShowFileSizeWarning: PropTypes.func,
|
||||
onShowUnsupportedMimeTypeWarning: PropTypes.func,
|
||||
|
|
@ -340,21 +342,6 @@ export default class AttachmentButton extends PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
handleFileAttachmentOption = (action) => {
|
||||
this.props.navigator.dismissModal({
|
||||
animationType: 'none',
|
||||
});
|
||||
|
||||
// Have to wait to launch the library attachment action.
|
||||
// If we call the action after dismissModal with no delay then the
|
||||
// Wix navigator will dismiss the library attachment modal as well.
|
||||
setTimeout(() => {
|
||||
if (typeof action === 'function') {
|
||||
action();
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
showFileAttachmentOptions = () => {
|
||||
const {
|
||||
canBrowseFiles,
|
||||
|
|
@ -366,6 +353,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
maxFileCount,
|
||||
onShowFileMaxWarning,
|
||||
extraOptions,
|
||||
actions,
|
||||
} = this.props;
|
||||
|
||||
if (fileCount === maxFileCount) {
|
||||
|
|
@ -378,7 +366,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
|
||||
if (canTakePhoto) {
|
||||
items.push({
|
||||
action: () => this.handleFileAttachmentOption(this.attachPhotoFromCamera),
|
||||
action: this.attachPhotoFromCamera,
|
||||
text: {
|
||||
id: t('mobile.file_upload.camera_photo'),
|
||||
defaultMessage: 'Take Photo',
|
||||
|
|
@ -389,7 +377,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
|
||||
if (canTakeVideo) {
|
||||
items.push({
|
||||
action: () => this.handleFileAttachmentOption(this.attachVideoFromCamera),
|
||||
action: this.attachVideoFromCamera,
|
||||
text: {
|
||||
id: t('mobile.file_upload.camera_video'),
|
||||
defaultMessage: 'Take Video',
|
||||
|
|
@ -400,7 +388,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
|
||||
if (canBrowsePhotoLibrary) {
|
||||
items.push({
|
||||
action: () => this.handleFileAttachmentOption(this.attachFileFromLibrary),
|
||||
action: this.attachFileFromLibrary,
|
||||
text: {
|
||||
id: t('mobile.file_upload.library'),
|
||||
defaultMessage: 'Photo Library',
|
||||
|
|
@ -411,7 +399,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
|
||||
if (canBrowseVideoLibrary && Platform.OS === 'android') {
|
||||
items.push({
|
||||
action: () => this.handleFileAttachmentOption(this.attachVideoFromLibraryAndroid),
|
||||
action: this.attachVideoFromLibraryAndroid,
|
||||
text: {
|
||||
id: t('mobile.file_upload.video'),
|
||||
defaultMessage: 'Video Library',
|
||||
|
|
@ -422,7 +410,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
|
||||
if (canBrowseFiles) {
|
||||
items.push({
|
||||
action: () => this.handleFileAttachmentOption(this.attachFileFromFiles),
|
||||
action: this.attachFileFromFiles,
|
||||
text: {
|
||||
id: t('mobile.file_upload.browse'),
|
||||
defaultMessage: 'Browse Files',
|
||||
|
|
@ -439,21 +427,7 @@ export default class AttachmentButton extends PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
this.props.navigator.showModal({
|
||||
screen: 'OptionsModal',
|
||||
title: '',
|
||||
animationType: 'none',
|
||||
passProps: {
|
||||
items,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
});
|
||||
actions.showModalOverCurrentContext('OptionsModal', {items});
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
@ -13,8 +13,10 @@ jest.mock('react-intl');
|
|||
|
||||
describe('AttachmentButton', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
showModalOverCurrentContext: jest.fn(),
|
||||
},
|
||||
theme: Preferences.THEMES.default,
|
||||
navigator: {},
|
||||
blurTextBox: jest.fn(),
|
||||
maxFileSize: 10,
|
||||
uploadFiles: jest.fn(),
|
||||
19
app/components/attachment_button/index.js
Normal file
19
app/components/attachment_button/index.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
|
||||
import AttachmentButton from './attachment_button';
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(AttachmentButton);
|
||||
|
|
@ -18,6 +18,7 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
setAutocompleteSelector: PropTypes.func.isRequired,
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
label: PropTypes.string,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
|
|
@ -28,7 +29,6 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
showRequiredAsterisk: PropTypes.bool,
|
||||
teammateNameDisplay: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onSelected: PropTypes.func,
|
||||
helpText: PropTypes.node,
|
||||
errorText: PropTypes.node,
|
||||
|
|
@ -96,22 +96,12 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
|
||||
goToSelectorScreen = preventDoubleTap(() => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {navigator, theme, actions, dataSource, options, placeholder} = this.props;
|
||||
const {actions, dataSource, options, placeholder} = this.props;
|
||||
const screen = 'SelectorScreen';
|
||||
const title = placeholder || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'});
|
||||
|
||||
actions.setAutocompleteSelector(dataSource, this.handleSelect, options);
|
||||
|
||||
navigator.push({
|
||||
backButtonTitle: '',
|
||||
screen: 'SelectorScreen',
|
||||
title: placeholder || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
});
|
||||
actions.goToScreen(screen, title);
|
||||
});
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {connect} from 'react-redux';
|
|||
|
||||
import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
import {setAutocompleteSelector} from 'app/actions/views/post';
|
||||
|
||||
import AutocompleteSelector from './autocomplete_selector';
|
||||
|
|
@ -21,6 +22,7 @@ function mapDispatchToProps(dispatch) {
|
|||
return {
|
||||
actions: bindActionCreators({
|
||||
setAutocompleteSelector,
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Platform,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
|
|
@ -21,11 +20,13 @@ import {t} from 'app/utils/i18n';
|
|||
|
||||
class ChannelIntro extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
creator: PropTypes.object,
|
||||
currentChannel: PropTypes.object.isRequired,
|
||||
currentChannelMembers: PropTypes.array.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
|
@ -34,28 +35,14 @@ class ChannelIntro extends PureComponent {
|
|||
};
|
||||
|
||||
goToUserProfile = (userId) => {
|
||||
const {intl, navigator, theme} = this.props;
|
||||
const options = {
|
||||
screen: 'UserProfile',
|
||||
title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
userId,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
const {actions, intl} = this.props;
|
||||
const screen = 'UserProfile';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const passProps = {
|
||||
userId,
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
navigator.push(options);
|
||||
} else {
|
||||
navigator.showModal(options);
|
||||
}
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
};
|
||||
|
||||
getDisplayName = (member) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import {createSelector} from 'reselect';
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ import {getCurrentUserId, getUser, makeGetProfilesInChannel} from 'mattermost-re
|
|||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
import {getChannelMembersForDm} from 'app/selectors/channel';
|
||||
|
||||
import ChannelIntro from './channel_intro';
|
||||
|
|
@ -52,4 +54,12 @@ function makeMapStateToProps() {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps)(ChannelIntro);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(ChannelIntro);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ export default class ClientUpgradeListener extends PureComponent {
|
|||
actions: PropTypes.shape({
|
||||
logError: PropTypes.func.isRequired,
|
||||
setLastUpgradeCheck: PropTypes.func.isRequired,
|
||||
showModal: PropTypes.func.isRequired,
|
||||
dismissModal: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
currentVersion: PropTypes.string,
|
||||
downloadLink: PropTypes.string,
|
||||
|
|
@ -35,7 +37,6 @@ export default class ClientUpgradeListener extends PureComponent {
|
|||
lastUpgradeCheck: PropTypes.number,
|
||||
latestVersion: PropTypes.string,
|
||||
minVersion: PropTypes.string,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
|
@ -140,27 +141,26 @@ export default class ClientUpgradeListener extends PureComponent {
|
|||
};
|
||||
|
||||
handleLearnMore = () => {
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
this.props.navigator.dismissModal({animationType: 'none'});
|
||||
|
||||
this.props.navigator.showModal({
|
||||
screen: 'ClientUpgrade',
|
||||
title: intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'}),
|
||||
navigatorStyle: {
|
||||
navBarHidden: false,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
},
|
||||
navigatorButtons: {
|
||||
actions.dismissModal();
|
||||
|
||||
const screen = 'ClientUpgrade';
|
||||
const title = intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'});
|
||||
const passProps = {
|
||||
upgradeType: this.state.upgradeType,
|
||||
};
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-upgrade',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
},
|
||||
passProps: {
|
||||
upgradeType: this.state.upgradeType,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
actions.showModal(screen, title, passProps, options);
|
||||
|
||||
this.toggleUpgradeMessage(false);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ import {bindActionCreators} from 'redux';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {logError} from 'mattermost-redux/actions/errors';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {showModal, dismissModal} from 'app/actions/navigation';
|
||||
import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade';
|
||||
import getClientUpgrade from 'app/selectors/client_upgrade';
|
||||
import {isLandscape} from 'app/selectors/device';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import ClientUpgradeListener from './client_upgrade_listener';
|
||||
|
||||
|
|
@ -32,6 +33,8 @@ function mapDispatchToProps(dispatch) {
|
|||
actions: bindActionCreators({
|
||||
logError,
|
||||
setLastUpgradeCheck,
|
||||
showModal,
|
||||
dismissModal,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ export default class CombinedSystemMessage extends React.PureComponent {
|
|||
currentUserId: PropTypes.string.isRequired,
|
||||
currentUsername: PropTypes.string.isRequired,
|
||||
messageData: PropTypes.array.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
showJoinLeave: PropTypes.bool.isRequired,
|
||||
textStyles: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired,
|
||||
|
|
@ -266,7 +265,6 @@ export default class CombinedSystemMessage extends React.PureComponent {
|
|||
const {
|
||||
currentUserId,
|
||||
currentUsername,
|
||||
navigator,
|
||||
textStyles,
|
||||
theme,
|
||||
} = this.props;
|
||||
|
|
@ -285,7 +283,6 @@ export default class CombinedSystemMessage extends React.PureComponent {
|
|||
<LastUsers
|
||||
actor={actor}
|
||||
expandedLocale={postTypeMessage[postType].many_expanded}
|
||||
navigator={navigator}
|
||||
postType={postType}
|
||||
style={style}
|
||||
textStyles={textStyles}
|
||||
|
|
@ -314,7 +311,6 @@ export default class CombinedSystemMessage extends React.PureComponent {
|
|||
return (
|
||||
<Markdown
|
||||
baseTextStyle={style.baseText}
|
||||
navigator={navigator}
|
||||
textStyles={textStyles}
|
||||
value={formattedMessage}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ export default class LastUsers extends React.PureComponent {
|
|||
static propTypes = {
|
||||
actor: PropTypes.string,
|
||||
expandedLocale: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
postType: PropTypes.string.isRequired,
|
||||
style: PropTypes.object.isRequired,
|
||||
textStyles: PropTypes.object,
|
||||
|
|
@ -88,7 +87,6 @@ export default class LastUsers extends React.PureComponent {
|
|||
const {
|
||||
actor,
|
||||
expandedLocale,
|
||||
navigator,
|
||||
style,
|
||||
textStyles,
|
||||
usernames,
|
||||
|
|
@ -106,7 +104,6 @@ export default class LastUsers extends React.PureComponent {
|
|||
return (
|
||||
<Markdown
|
||||
baseTextStyle={style.baseText}
|
||||
navigator={navigator}
|
||||
textStyles={textStyles}
|
||||
value={formattedMessage}
|
||||
/>
|
||||
|
|
@ -116,7 +113,6 @@ export default class LastUsers extends React.PureComponent {
|
|||
renderCollapsedView = () => {
|
||||
const {
|
||||
actor,
|
||||
navigator,
|
||||
postType,
|
||||
style,
|
||||
textStyles,
|
||||
|
|
@ -134,7 +130,6 @@ export default class LastUsers extends React.PureComponent {
|
|||
defaultMessage={'{firstUser} and '}
|
||||
values={{firstUser}}
|
||||
baseTextStyle={style.baseText}
|
||||
navigator={navigator}
|
||||
style={style.baseText}
|
||||
textStyles={textStyles}
|
||||
theme={theme}
|
||||
|
|
@ -155,7 +150,6 @@ export default class LastUsers extends React.PureComponent {
|
|||
defaultMessage={typeMessage[postType].defaultMessage}
|
||||
values={{actor}}
|
||||
baseTextStyle={style.baseText}
|
||||
navigator={navigator}
|
||||
style={style.baseText}
|
||||
textStyles={textStyles}
|
||||
theme={theme}
|
||||
|
|
|
|||
415
app/components/edit_channel_info/edit_channel_info.js
Normal file
415
app/components/edit_channel_info/edit_channel_info.js
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Platform,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
Text,
|
||||
findNodeHandle,
|
||||
} from 'react-native';
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||||
|
||||
import ErrorText from 'app/components/error_text';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import Loading from 'app/components/loading';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getShortenedURL} from 'app/utils/url';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
||||
export default class EditChannelInfo extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
dismissModal: PropTypes.func.isRequired,
|
||||
popTopScreen: PropTypes.func.isRequired,
|
||||
}),
|
||||
theme: PropTypes.object.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
channelType: PropTypes.string,
|
||||
enableRightButton: PropTypes.func,
|
||||
saving: PropTypes.bool.isRequired,
|
||||
editing: PropTypes.bool,
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
displayName: PropTypes.string,
|
||||
currentTeamUrl: PropTypes.string,
|
||||
channelURL: PropTypes.string,
|
||||
purpose: PropTypes.string,
|
||||
header: PropTypes.string,
|
||||
onDisplayNameChange: PropTypes.func,
|
||||
onChannelURLChange: PropTypes.func,
|
||||
onPurposeChange: PropTypes.func,
|
||||
onHeaderChange: PropTypes.func,
|
||||
oldDisplayName: PropTypes.string,
|
||||
oldChannelURL: PropTypes.string,
|
||||
oldHeader: PropTypes.string,
|
||||
oldPurpose: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.nameInput = React.createRef();
|
||||
this.urlInput = React.createRef();
|
||||
this.purposeInput = React.createRef();
|
||||
this.headerInput = React.createRef();
|
||||
this.lastText = React.createRef();
|
||||
this.scroll = React.createRef();
|
||||
}
|
||||
|
||||
blur = () => {
|
||||
if (this.nameInput?.current) {
|
||||
this.nameInput.current.blur();
|
||||
}
|
||||
|
||||
// TODO: uncomment below once the channel URL field is added
|
||||
// if (this.urlInput?.current) {
|
||||
// this.urlInput.current.blur();
|
||||
// }
|
||||
|
||||
if (this.purposeInput?.current) {
|
||||
this.purposeInput.current.blur();
|
||||
}
|
||||
if (this.headerInput?.current) {
|
||||
this.headerInput.current.blur();
|
||||
}
|
||||
|
||||
if (this.scroll?.current) {
|
||||
this.scroll.current.scrollToPosition(0, 0, true);
|
||||
}
|
||||
};
|
||||
|
||||
close = (goBack = false) => {
|
||||
const {actions} = this.props;
|
||||
if (goBack) {
|
||||
actions.popTopScreen();
|
||||
} else {
|
||||
actions.dismissModal();
|
||||
}
|
||||
};
|
||||
|
||||
canUpdate = (displayName, channelURL, purpose, header) => {
|
||||
const {
|
||||
oldDisplayName,
|
||||
oldChannelURL,
|
||||
oldPurpose,
|
||||
oldHeader,
|
||||
} = this.props;
|
||||
|
||||
return displayName !== oldDisplayName || channelURL !== oldChannelURL ||
|
||||
purpose !== oldPurpose || header !== oldHeader;
|
||||
};
|
||||
|
||||
enableRightButton = (enable = false) => {
|
||||
this.props.enableRightButton(enable);
|
||||
};
|
||||
|
||||
onDisplayNameChangeText = (displayName) => {
|
||||
const {editing, onDisplayNameChange} = this.props;
|
||||
onDisplayNameChange(displayName);
|
||||
|
||||
if (editing) {
|
||||
const {channelURL, purpose, header} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
return;
|
||||
}
|
||||
|
||||
const displayNameExists = displayName && displayName.length >= 2;
|
||||
this.props.enableRightButton(displayNameExists);
|
||||
};
|
||||
|
||||
onDisplayURLChangeText = (channelURL) => {
|
||||
const {editing, onChannelURLChange} = this.props;
|
||||
onChannelURLChange(channelURL);
|
||||
|
||||
if (editing) {
|
||||
const {displayName, purpose, header} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
onPurposeChangeText = (purpose) => {
|
||||
const {editing, onPurposeChange} = this.props;
|
||||
onPurposeChange(purpose);
|
||||
|
||||
if (editing) {
|
||||
const {displayName, channelURL, header} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
onHeaderChangeText = (header) => {
|
||||
const {editing, onHeaderChange} = this.props;
|
||||
onHeaderChange(header);
|
||||
|
||||
if (editing) {
|
||||
const {displayName, channelURL, purpose} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
scrollToEnd = () => {
|
||||
if (this.scroll?.current && this.lastText?.current) {
|
||||
this.scroll.current.scrollToFocusedInput(findNodeHandle(this.lastText.current));
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
theme,
|
||||
editing,
|
||||
channelType,
|
||||
currentTeamUrl,
|
||||
deviceWidth,
|
||||
deviceHeight,
|
||||
displayName,
|
||||
channelURL,
|
||||
header,
|
||||
purpose,
|
||||
} = this.props;
|
||||
const {error, saving} = this.props;
|
||||
const fullUrl = currentTeamUrl + '/channels';
|
||||
const shortUrl = getShortenedURL(fullUrl, 35);
|
||||
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const displayHeaderOnly = channelType === General.DM_CHANNEL ||
|
||||
channelType === General.GM_CHANNEL;
|
||||
|
||||
if (saving) {
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<Loading/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
let displayError;
|
||||
if (error) {
|
||||
displayError = (
|
||||
<View style={[style.errorContainer, {width: deviceWidth}]}>
|
||||
<View style={style.errorWrapper}>
|
||||
<ErrorText error={error}/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<KeyboardAwareScrollView
|
||||
ref={this.scroll}
|
||||
style={style.container}
|
||||
>
|
||||
{displayError}
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[style.scrollView, {height: deviceHeight + (Platform.OS === 'android' ? 200 : 0)}]}>
|
||||
{!displayHeaderOnly && (
|
||||
<View>
|
||||
<View>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='channel_modal.name'
|
||||
defaultMessage='Name'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.nameInput}
|
||||
value={displayName}
|
||||
onChangeText={this.onDisplayNameChangeText}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('channel_modal.nameEx'), defaultMessage: 'E.g.: "Bugs", "Marketing", "客户支持"'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{/*TODO: Hide channel url field until it's added to CreateChannel */}
|
||||
{false && editing && !displayHeaderOnly && (
|
||||
<View>
|
||||
<View style={style.titleContainer30}>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='rename_channel.url'
|
||||
defaultMessage='URL'
|
||||
/>
|
||||
<Text style={style.optional}>
|
||||
{shortUrl}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.urlInput}
|
||||
value={channelURL}
|
||||
onChangeText={this.onDisplayURLChangeText}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('rename_channel.handleHolder'), defaultMessage: 'lowercase alphanumeric characters'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{!displayHeaderOnly && (
|
||||
<View>
|
||||
<View style={style.titleContainer30}>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='channel_modal.purpose'
|
||||
defaultMessage='Purpose'
|
||||
/>
|
||||
<FormattedText
|
||||
style={style.optional}
|
||||
id='channel_modal.optional'
|
||||
defaultMessage='(optional)'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.purposeInput}
|
||||
value={purpose}
|
||||
onChangeText={this.onPurposeChangeText}
|
||||
style={[style.input, {height: 110}]}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('channel_modal.purposeEx'), defaultMessage: 'E.g.: "A channel to file bugs and improvements"'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
multiline={true}
|
||||
blurOnSubmit={false}
|
||||
textAlignVertical='top'
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
<View>
|
||||
<FormattedText
|
||||
style={style.helpText}
|
||||
id='channel_modal.descriptionHelp'
|
||||
defaultMessage='Describe how this channel should be used.'
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
<View style={style.titleContainer15}>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='channel_modal.header'
|
||||
defaultMessage='Header'
|
||||
/>
|
||||
<FormattedText
|
||||
style={style.optional}
|
||||
id='channel_modal.optional'
|
||||
defaultMessage='(optional)'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.headerInput}
|
||||
value={header}
|
||||
onChangeText={this.onHeaderChangeText}
|
||||
style={[style.input, {height: 110}]}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('channel_modal.headerEx'), defaultMessage: 'E.g.: "[Link Title](http://example.com)"'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
multiline={true}
|
||||
blurOnSubmit={false}
|
||||
onFocus={this.scrollToEnd}
|
||||
textAlignVertical='top'
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
<View ref={this.lastText}>
|
||||
<FormattedText
|
||||
style={style.helpText}
|
||||
id='channel_modal.headerHelp'
|
||||
defaultMessage={'Set text that will appear in the header of the channel beside the channel name. For example, include frequently used links by typing [Link Title](http://example.com).'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</KeyboardAwareScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
paddingTop: 10,
|
||||
},
|
||||
errorContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
},
|
||||
errorWrapper: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
inputContainer: {
|
||||
marginTop: 10,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
input: {
|
||||
color: '#333',
|
||||
fontSize: 14,
|
||||
height: 40,
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
titleContainer30: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 30,
|
||||
},
|
||||
titleContainer15: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 15,
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: theme.centerChannelColor,
|
||||
marginLeft: 15,
|
||||
},
|
||||
optional: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 14,
|
||||
marginLeft: 5,
|
||||
},
|
||||
helpText: {
|
||||
fontSize: 14,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
marginTop: 10,
|
||||
marginHorizontal: 15,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
@ -1,414 +1,23 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {
|
||||
Platform,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
Text,
|
||||
findNodeHandle,
|
||||
} from 'react-native';
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||||
dismissModal,
|
||||
popTopScreen,
|
||||
} from 'app/actions/navigation';
|
||||
|
||||
import ErrorText from 'app/components/error_text';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import Loading from 'app/components/loading';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
|
||||
import EditChannelInfo from './edit_channel_info';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getShortenedURL} from 'app/utils/url';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
||||
export default class EditChannelInfo extends PureComponent {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
channelType: PropTypes.string,
|
||||
enableRightButton: PropTypes.func,
|
||||
saving: PropTypes.bool.isRequired,
|
||||
editing: PropTypes.bool,
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
displayName: PropTypes.string,
|
||||
currentTeamUrl: PropTypes.string,
|
||||
channelURL: PropTypes.string,
|
||||
purpose: PropTypes.string,
|
||||
header: PropTypes.string,
|
||||
onDisplayNameChange: PropTypes.func,
|
||||
onChannelURLChange: PropTypes.func,
|
||||
onPurposeChange: PropTypes.func,
|
||||
onHeaderChange: PropTypes.func,
|
||||
oldDisplayName: PropTypes.string,
|
||||
oldChannelURL: PropTypes.string,
|
||||
oldHeader: PropTypes.string,
|
||||
oldPurpose: PropTypes.string,
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
dismissModal,
|
||||
popTopScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.nameInput = React.createRef();
|
||||
this.urlInput = React.createRef();
|
||||
this.purposeInput = React.createRef();
|
||||
this.headerInput = React.createRef();
|
||||
this.lastText = React.createRef();
|
||||
this.scroll = React.createRef();
|
||||
}
|
||||
|
||||
blur = () => {
|
||||
if (this.nameInput?.current) {
|
||||
this.nameInput.current.blur();
|
||||
}
|
||||
|
||||
// TODO: uncomment below once the channel URL field is added
|
||||
// if (this.urlInput?.current) {
|
||||
// this.urlInput.current.blur();
|
||||
// }
|
||||
|
||||
if (this.purposeInput?.current) {
|
||||
this.purposeInput.current.blur();
|
||||
}
|
||||
if (this.headerInput?.current) {
|
||||
this.headerInput.current.blur();
|
||||
}
|
||||
|
||||
if (this.scroll?.current) {
|
||||
this.scroll.current.scrollToPosition(0, 0, true);
|
||||
}
|
||||
};
|
||||
|
||||
close = (goBack = false) => {
|
||||
if (goBack) {
|
||||
this.props.navigator.pop({animated: true});
|
||||
} else {
|
||||
this.props.navigator.dismissModal({
|
||||
animationType: 'slide-down',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
canUpdate = (displayName, channelURL, purpose, header) => {
|
||||
const {
|
||||
oldDisplayName,
|
||||
oldChannelURL,
|
||||
oldPurpose,
|
||||
oldHeader,
|
||||
} = this.props;
|
||||
|
||||
return displayName !== oldDisplayName || channelURL !== oldChannelURL ||
|
||||
purpose !== oldPurpose || header !== oldHeader;
|
||||
};
|
||||
|
||||
enableRightButton = (enable = false) => {
|
||||
this.props.enableRightButton(enable);
|
||||
};
|
||||
|
||||
onDisplayNameChangeText = (displayName) => {
|
||||
const {editing, onDisplayNameChange} = this.props;
|
||||
onDisplayNameChange(displayName);
|
||||
|
||||
if (editing) {
|
||||
const {channelURL, purpose, header} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
return;
|
||||
}
|
||||
|
||||
const displayNameExists = displayName && displayName.length >= 2;
|
||||
this.props.enableRightButton(displayNameExists);
|
||||
};
|
||||
|
||||
onDisplayURLChangeText = (channelURL) => {
|
||||
const {editing, onChannelURLChange} = this.props;
|
||||
onChannelURLChange(channelURL);
|
||||
|
||||
if (editing) {
|
||||
const {displayName, purpose, header} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
onPurposeChangeText = (purpose) => {
|
||||
const {editing, onPurposeChange} = this.props;
|
||||
onPurposeChange(purpose);
|
||||
|
||||
if (editing) {
|
||||
const {displayName, channelURL, header} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
onHeaderChangeText = (header) => {
|
||||
const {editing, onHeaderChange} = this.props;
|
||||
onHeaderChange(header);
|
||||
|
||||
if (editing) {
|
||||
const {displayName, channelURL, purpose} = this.props;
|
||||
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
|
||||
this.enableRightButton(canUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
scrollToEnd = () => {
|
||||
if (this.scroll?.current && this.lastText?.current) {
|
||||
this.scroll.current.scrollToFocusedInput(findNodeHandle(this.lastText.current));
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
theme,
|
||||
editing,
|
||||
channelType,
|
||||
currentTeamUrl,
|
||||
deviceWidth,
|
||||
deviceHeight,
|
||||
displayName,
|
||||
channelURL,
|
||||
header,
|
||||
purpose,
|
||||
} = this.props;
|
||||
const {error, saving} = this.props;
|
||||
const fullUrl = currentTeamUrl + '/channels';
|
||||
const shortUrl = getShortenedURL(fullUrl, 35);
|
||||
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const displayHeaderOnly = channelType === General.DM_CHANNEL ||
|
||||
channelType === General.GM_CHANNEL;
|
||||
|
||||
if (saving) {
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<Loading/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
let displayError;
|
||||
if (error) {
|
||||
displayError = (
|
||||
<View style={[style.errorContainer, {width: deviceWidth}]}>
|
||||
<View style={style.errorWrapper}>
|
||||
<ErrorText error={error}/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<KeyboardAwareScrollView
|
||||
ref={this.scroll}
|
||||
style={style.container}
|
||||
>
|
||||
{displayError}
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[style.scrollView, {height: deviceHeight + (Platform.OS === 'android' ? 200 : 0)}]}>
|
||||
{!displayHeaderOnly && (
|
||||
<View>
|
||||
<View>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='channel_modal.name'
|
||||
defaultMessage='Name'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.nameInput}
|
||||
value={displayName}
|
||||
onChangeText={this.onDisplayNameChangeText}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('channel_modal.nameEx'), defaultMessage: 'E.g.: "Bugs", "Marketing", "客户支持"'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{/*TODO: Hide channel url field until it's added to CreateChannel */}
|
||||
{false && editing && !displayHeaderOnly && (
|
||||
<View>
|
||||
<View style={style.titleContainer30}>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='rename_channel.url'
|
||||
defaultMessage='URL'
|
||||
/>
|
||||
<Text style={style.optional}>
|
||||
{shortUrl}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.urlInput}
|
||||
value={channelURL}
|
||||
onChangeText={this.onDisplayURLChangeText}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('rename_channel.handleHolder'), defaultMessage: 'lowercase alphanumeric characters'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{!displayHeaderOnly && (
|
||||
<View>
|
||||
<View style={style.titleContainer30}>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='channel_modal.purpose'
|
||||
defaultMessage='Purpose'
|
||||
/>
|
||||
<FormattedText
|
||||
style={style.optional}
|
||||
id='channel_modal.optional'
|
||||
defaultMessage='(optional)'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.purposeInput}
|
||||
value={purpose}
|
||||
onChangeText={this.onPurposeChangeText}
|
||||
style={[style.input, {height: 110}]}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('channel_modal.purposeEx'), defaultMessage: 'E.g.: "A channel to file bugs and improvements"'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
multiline={true}
|
||||
blurOnSubmit={false}
|
||||
textAlignVertical='top'
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
<View>
|
||||
<FormattedText
|
||||
style={style.helpText}
|
||||
id='channel_modal.descriptionHelp'
|
||||
defaultMessage='Describe how this channel should be used.'
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
<View style={style.titleContainer15}>
|
||||
<FormattedText
|
||||
style={style.title}
|
||||
id='channel_modal.header'
|
||||
defaultMessage='Header'
|
||||
/>
|
||||
<FormattedText
|
||||
style={style.optional}
|
||||
id='channel_modal.optional'
|
||||
defaultMessage='(optional)'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.headerInput}
|
||||
value={header}
|
||||
onChangeText={this.onHeaderChangeText}
|
||||
style={[style.input, {height: 110}]}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: t('channel_modal.headerEx'), defaultMessage: 'E.g.: "[Link Title](http://example.com)"'}}
|
||||
placeholderTextColor={changeOpacity('#000', 0.5)}
|
||||
multiline={true}
|
||||
blurOnSubmit={false}
|
||||
onFocus={this.scrollToEnd}
|
||||
textAlignVertical='top'
|
||||
underlineColorAndroid='transparent'
|
||||
disableFullscreenUI={true}
|
||||
/>
|
||||
</View>
|
||||
<View ref={this.lastText}>
|
||||
<FormattedText
|
||||
style={style.helpText}
|
||||
id='channel_modal.headerHelp'
|
||||
defaultMessage={'Set text that will appear in the header of the channel beside the channel name. For example, include frequently used links by typing [Link Title](http://example.com).'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</KeyboardAwareScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
paddingTop: 10,
|
||||
},
|
||||
errorContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
},
|
||||
errorWrapper: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
inputContainer: {
|
||||
marginTop: 10,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
input: {
|
||||
color: '#333',
|
||||
fontSize: 14,
|
||||
height: 40,
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
titleContainer30: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 30,
|
||||
},
|
||||
titleContainer15: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 15,
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: theme.centerChannelColor,
|
||||
marginLeft: 15,
|
||||
},
|
||||
optional: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 14,
|
||||
marginLeft: 5,
|
||||
},
|
||||
helpText: {
|
||||
fontSize: 14,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
marginTop: 10,
|
||||
marginHorizontal: 15,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(EditChannelInfo);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ export default class FileAttachment extends PureComponent {
|
|||
onLongPress: PropTypes.func,
|
||||
onPreviewPress: PropTypes.func,
|
||||
theme: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -93,7 +92,6 @@ export default class FileAttachment extends PureComponent {
|
|||
deviceWidth,
|
||||
file,
|
||||
theme,
|
||||
navigator,
|
||||
onLongPress,
|
||||
} = this.props;
|
||||
const {data} = file;
|
||||
|
|
@ -120,7 +118,6 @@ export default class FileAttachment extends PureComponent {
|
|||
ref={this.setDocumentRef}
|
||||
canDownloadFiles={canDownloadFiles}
|
||||
file={file}
|
||||
navigator={navigator}
|
||||
onLongPress={onLongPress}
|
||||
theme={theme}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import {DeviceTypes} from 'app/constants/';
|
|||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
|
||||
import FileAttachmentIcon from './file_attachment_icon';
|
||||
import FileAttachmentIcon from 'app/components/file_attachment_list/file_attachment_icon';
|
||||
|
||||
const {DOCUMENTS_PATH} = DeviceTypes;
|
||||
const DOWNLOADING_OFFSET = 28;
|
||||
|
|
@ -38,13 +38,15 @@ const circularProgressWidth = 4;
|
|||
|
||||
export default class FileAttachmentDocument extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
backgroundColor: PropTypes.string,
|
||||
canDownloadFiles: PropTypes.bool.isRequired,
|
||||
iconHeight: PropTypes.number,
|
||||
iconWidth: PropTypes.number,
|
||||
file: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onLongPress: PropTypes.func,
|
||||
wrapperHeight: PropTypes.number,
|
||||
wrapperWidth: PropTypes.number,
|
||||
|
|
@ -192,7 +194,7 @@ export default class FileAttachmentDocument extends PureComponent {
|
|||
};
|
||||
|
||||
previewTextFile = (file, delay = 2000) => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {data} = file;
|
||||
const prefix = Platform.OS === 'android' ? 'file:/' : '';
|
||||
const path = `${DOCUMENTS_PATH}/${data.id}-${file.caption}`;
|
||||
|
|
@ -200,21 +202,13 @@ export default class FileAttachmentDocument extends PureComponent {
|
|||
setTimeout(async () => {
|
||||
try {
|
||||
const content = await readFile;
|
||||
navigator.push({
|
||||
screen: 'TextPreview',
|
||||
title: file.caption,
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
content,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
});
|
||||
const screen = 'TextPreview';
|
||||
const title = file.caption;
|
||||
const passProps = {
|
||||
content,
|
||||
};
|
||||
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
this.setState({downloading: false, progress: 0});
|
||||
} catch (error) {
|
||||
RNFetchBlob.fs.unlink(path);
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
import FileAttachmentDocument from './file_attachment_document';
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(FileAttachmentDocument);
|
||||
|
|
@ -20,14 +20,16 @@ import FileAttachment from './file_attachment';
|
|||
|
||||
export default class FileAttachmentList extends Component {
|
||||
static propTypes = {
|
||||
actions: PropTypes.object.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
loadFilesForPostIfNecessary: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
canDownloadFiles: PropTypes.bool.isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
fileIds: PropTypes.array.isRequired,
|
||||
files: PropTypes.array,
|
||||
isFailed: PropTypes.bool,
|
||||
navigator: PropTypes.object,
|
||||
onLongPress: PropTypes.func,
|
||||
postId: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
|
|
@ -122,11 +124,12 @@ export default class FileAttachmentList extends Component {
|
|||
};
|
||||
|
||||
handlePreviewPress = preventDoubleTap((idx) => {
|
||||
previewImageAtIndex(this.props.navigator, this.items, idx, this.galleryFiles);
|
||||
const {actions} = this.props;
|
||||
previewImageAtIndex(this.items, idx, this.galleryFiles, actions.showModalOverCurrentContext);
|
||||
});
|
||||
|
||||
renderItems = () => {
|
||||
const {canDownloadFiles, deviceWidth, fileIds, files, navigator} = this.props;
|
||||
const {canDownloadFiles, deviceWidth, fileIds, files} = this.props;
|
||||
|
||||
if (!files.length && fileIds.length > 0) {
|
||||
return fileIds.map((id, idx) => (
|
||||
|
|
@ -156,7 +159,6 @@ export default class FileAttachmentList extends Component {
|
|||
file={f}
|
||||
id={file.id}
|
||||
index={idx}
|
||||
navigator={navigator}
|
||||
onCaptureRef={this.handleCaptureRef}
|
||||
onPreviewPress={this.handlePreviewPress}
|
||||
onLongPress={this.props.onLongPress}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ describe('PostAttachmentOpenGraph', () => {
|
|||
const baseProps = {
|
||||
actions: {
|
||||
loadFilesForPostIfNecessary,
|
||||
showModalOverCurrentContext: jest.fn(),
|
||||
},
|
||||
canDownloadFiles: true,
|
||||
deviceHeight: 680,
|
||||
|
|
@ -71,6 +72,7 @@ describe('PostAttachmentOpenGraph', () => {
|
|||
files: [],
|
||||
actions: {
|
||||
loadFilesForPostIfNecessary: loadFilesForPostIfNecessaryMock,
|
||||
showModalOverCurrentContext: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/gene
|
|||
import {makeGetFilesForPost} from 'mattermost-redux/selectors/entities/files';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
import {loadFilesForPostIfNecessary} from 'app/actions/views/channel';
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ function mapDispatchToProps(dispatch) {
|
|||
return {
|
||||
actions: bindActionCreators({
|
||||
loadFilesForPostIfNecessary,
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ class FormattedMarkdownText extends React.PureComponent {
|
|||
baseTextStyle: CustomPropTypes.Style,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onPostPress: PropTypes.func,
|
||||
style: CustomPropTypes.Style,
|
||||
textStyles: PropTypes.object,
|
||||
|
|
@ -115,7 +114,6 @@ class FormattedMarkdownText extends React.PureComponent {
|
|||
<AtMention
|
||||
mentionStyle={this.props.textStyles.mention}
|
||||
mentionName={mentionName}
|
||||
navigator={this.props.navigator}
|
||||
onPostPress={this.props.onPostPress}
|
||||
textStyle={this.computeTextStyle(this.props.baseTextStyle, context)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {showModal} from 'app/actions/navigation';
|
||||
|
||||
import InteractiveDialogController from './interactive_dialog_controller';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -12,4 +15,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(InteractiveDialogController);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
showModal,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(InteractiveDialogController);
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
|||
|
||||
export default class InteractiveDialogController extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
showModal: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
triggerId: PropTypes.string,
|
||||
dialog: PropTypes.object,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
|
|
@ -22,7 +24,7 @@ export default class InteractiveDialogController extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const triggerId = this.props.triggerId;
|
||||
const {actions, triggerId} = this.props;
|
||||
if (!triggerId) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -41,33 +43,24 @@ export default class InteractiveDialogController extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
const theme = this.props.theme;
|
||||
|
||||
this.props.navigator.showModal({
|
||||
backButtonTitle: '',
|
||||
screen: 'InteractiveDialog',
|
||||
title: dialogData.dialog.title,
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
navigatorButtons: {
|
||||
const screen = 'InteractiveDialog';
|
||||
const title = dialogData.dialog.title;
|
||||
const passProps = {};
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-dialog',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
rightButtons: [
|
||||
{
|
||||
id: 'submit-dialog',
|
||||
showAsAction: 'always',
|
||||
title: dialogData.dialog.submit_label,
|
||||
},
|
||||
],
|
||||
rightButtons: [{
|
||||
id: 'submit-dialog',
|
||||
showAsAction: 'always',
|
||||
text: dialogData.dialog.submit_label,
|
||||
}],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
actions.showModal(screen, title, passProps, options);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -12,24 +12,31 @@ export default class Hashtag extends React.PureComponent {
|
|||
hashtag: PropTypes.string.isRequired,
|
||||
linkStyle: CustomPropTypes.Style.isRequired,
|
||||
onHashtagPress: PropTypes.func,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
popToRoot: PropTypes.func.isRequired,
|
||||
showSearchModal: PropTypes.func.isRequired,
|
||||
dismissAllModals: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
handlePress = () => {
|
||||
if (this.props.onHashtagPress) {
|
||||
this.props.onHashtagPress(this.props.hashtag);
|
||||
const {
|
||||
onHashtagPress,
|
||||
hashtag,
|
||||
actions,
|
||||
} = this.props;
|
||||
|
||||
if (onHashtagPress) {
|
||||
onHashtagPress(hashtag);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Close thread view, permalink view, etc
|
||||
this.props.navigator.dismissAllModals();
|
||||
this.props.navigator.popToRoot();
|
||||
actions.dismissAllModals();
|
||||
actions.popToRoot();
|
||||
|
||||
this.props.actions.showSearchModal(this.props.navigator, '#' + this.props.hashtag);
|
||||
actions.showSearchModal('#' + this.props.hashtag);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,10 @@ describe('Hashtag', () => {
|
|||
const baseProps = {
|
||||
hashtag: 'test',
|
||||
linkStyle: {color: 'red'},
|
||||
navigator: {
|
||||
dismissAllModals: jest.fn(),
|
||||
popToRoot: jest.fn(),
|
||||
},
|
||||
actions: {
|
||||
showSearchModal: jest.fn(),
|
||||
dismissAllModals: jest.fn(),
|
||||
popToRoot: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -35,9 +33,9 @@ describe('Hashtag', () => {
|
|||
|
||||
wrapper.find(Text).simulate('press');
|
||||
|
||||
expect(props.navigator.dismissAllModals).toHaveBeenCalled();
|
||||
expect(props.navigator.popToRoot).toHaveBeenCalled();
|
||||
expect(props.actions.showSearchModal).toHaveBeenCalledWith(props.navigator, '#test');
|
||||
expect(props.actions.dismissAllModals).toHaveBeenCalled();
|
||||
expect(props.actions.popToRoot).toHaveBeenCalled();
|
||||
expect(props.actions.showSearchModal).toHaveBeenCalledWith('#test');
|
||||
});
|
||||
|
||||
test('should call onHashtagPress if provided', () => {
|
||||
|
|
@ -50,8 +48,8 @@ describe('Hashtag', () => {
|
|||
|
||||
wrapper.find(Text).simulate('press');
|
||||
|
||||
expect(props.navigator.dismissAllModals).not.toBeCalled();
|
||||
expect(props.navigator.popToRoot).not.toBeCalled();
|
||||
expect(props.actions.dismissAllModals).not.toBeCalled();
|
||||
expect(props.actions.popToRoot).not.toBeCalled();
|
||||
expect(props.actions.showSearchModal).not.toBeCalled();
|
||||
|
||||
expect(props.onHashtagPress).toBeCalled();
|
||||
|
|
|
|||
|
|
@ -4,14 +4,20 @@
|
|||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
|
||||
import {showSearchModal} from 'app/actions/views/search';
|
||||
import {
|
||||
popToRoot,
|
||||
showSearchModal,
|
||||
dismissAllModals,
|
||||
} from 'app/actions/navigation';
|
||||
|
||||
import Hashtag from './hashtag';
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
popToRoot,
|
||||
showSearchModal,
|
||||
dismissAllModals,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ export default class Markdown extends PureComponent {
|
|||
isSearchResult: PropTypes.bool,
|
||||
mentionKeys: PropTypes.array.isRequired,
|
||||
minimumHashtagLength: PropTypes.number.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onChannelLinkPress: PropTypes.func,
|
||||
onHashtagPress: PropTypes.func,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
|
|
@ -176,7 +175,6 @@ export default class Markdown extends PureComponent {
|
|||
<MarkdownTableImage
|
||||
source={src}
|
||||
textStyle={[this.computeTextStyle(this.props.baseTextStyle, context), this.props.textStyles.link]}
|
||||
navigator={this.props.navigator}
|
||||
>
|
||||
{reactChildren}
|
||||
</MarkdownTableImage>
|
||||
|
|
@ -188,7 +186,6 @@ export default class Markdown extends PureComponent {
|
|||
linkDestination={linkDestination}
|
||||
imagesMetadata={this.props.imagesMetadata}
|
||||
isReplyPost={this.props.isReplyPost}
|
||||
navigator={this.props.navigator}
|
||||
source={src}
|
||||
errorTextStyle={[this.computeTextStyle(this.props.baseTextStyle, context), this.props.textStyles.error]}
|
||||
>
|
||||
|
|
@ -209,7 +206,6 @@ export default class Markdown extends PureComponent {
|
|||
isSearchResult={this.props.isSearchResult}
|
||||
mentionName={mentionName}
|
||||
onPostPress={this.props.onPostPress}
|
||||
navigator={this.props.navigator}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -250,7 +246,6 @@ export default class Markdown extends PureComponent {
|
|||
hashtag={hashtag}
|
||||
linkStyle={this.props.textStyles.link}
|
||||
onHashtagPress={this.props.onHashtagPress}
|
||||
navigator={this.props.navigator}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -295,7 +290,6 @@ export default class Markdown extends PureComponent {
|
|||
|
||||
return (
|
||||
<MarkdownCodeBlock
|
||||
navigator={this.props.navigator}
|
||||
content={content}
|
||||
language={props.language}
|
||||
textStyle={this.props.textStyles.codeBlock}
|
||||
|
|
@ -371,7 +365,6 @@ export default class Markdown extends PureComponent {
|
|||
renderTable = ({children, numColumns}) => {
|
||||
return (
|
||||
<MarkdownTable
|
||||
navigator={this.props.navigator}
|
||||
numColumns={numColumns}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
import MarkdownCodeBlock from './markdown_code_block';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -13,4 +16,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(MarkdownCodeBlock);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MarkdownCodeBlock);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ const MAX_LINES = 4;
|
|||
|
||||
export default class MarkdownCodeBlock extends React.PureComponent {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
language: PropTypes.string,
|
||||
content: PropTypes.string.isRequired,
|
||||
|
|
@ -40,10 +42,14 @@ export default class MarkdownCodeBlock extends React.PureComponent {
|
|||
};
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions, language, content} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'Code';
|
||||
const passProps = {
|
||||
content,
|
||||
};
|
||||
|
||||
const languageDisplayName = getDisplayNameForLanguage(this.props.language);
|
||||
const languageDisplayName = getDisplayNameForLanguage(language);
|
||||
let title;
|
||||
if (languageDisplayName) {
|
||||
title = intl.formatMessage(
|
||||
|
|
@ -62,21 +68,7 @@ export default class MarkdownCodeBlock extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
navigator.push({
|
||||
screen: 'Code',
|
||||
title,
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
content: this.props.content,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
});
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
});
|
||||
|
||||
handleLongPress = async () => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
|
||||
import MarkdownImage from './markdown_image';
|
||||
|
|
@ -16,4 +19,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(MarkdownImage);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MarkdownImage);
|
||||
|
|
|
|||
|
|
@ -33,13 +33,15 @@ const VIEWPORT_IMAGE_REPLY_OFFSET = 13;
|
|||
|
||||
export default class MarkdownImage extends React.Component {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
children: PropTypes.node,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
imagesMetadata: PropTypes.object,
|
||||
linkDestination: PropTypes.string,
|
||||
isReplyPost: PropTypes.bool,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
serverURL: PropTypes.string.isRequired,
|
||||
source: PropTypes.string.isRequired,
|
||||
errorTextStyle: CustomPropTypes.Style,
|
||||
|
|
@ -175,6 +177,7 @@ export default class MarkdownImage extends React.Component {
|
|||
originalWidth,
|
||||
uri,
|
||||
} = this.state;
|
||||
const {actions} = this.props;
|
||||
const link = this.getSource();
|
||||
let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?'));
|
||||
const extension = filename.split('.').pop();
|
||||
|
|
@ -195,7 +198,8 @@ export default class MarkdownImage extends React.Component {
|
|||
localPath: uri,
|
||||
},
|
||||
}];
|
||||
previewImageAtIndex(this.props.navigator, [this.refs.item], 0, files);
|
||||
|
||||
previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext);
|
||||
};
|
||||
|
||||
loadImageSize = (source) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
import MarkdownTable from './markdown_table';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -13,4 +16,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(MarkdownTable);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MarkdownTable);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ const MAX_HEIGHT = 300;
|
|||
|
||||
export default class MarkdownTable extends React.PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
numColumns: PropTypes.number.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
@ -44,27 +46,19 @@ export default class MarkdownTable extends React.PureComponent {
|
|||
};
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
|
||||
navigator.push({
|
||||
screen: 'Table',
|
||||
title: this.context.intl.formatMessage({
|
||||
id: 'mobile.routes.table',
|
||||
defaultMessage: 'Table',
|
||||
}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
renderRows: this.renderRows,
|
||||
tableWidth: this.getTableWidth(),
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'Table';
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.routes.table',
|
||||
defaultMessage: 'Table',
|
||||
});
|
||||
const passProps = {
|
||||
renderRows: this.renderRows,
|
||||
tableWidth: this.getTableWidth(),
|
||||
};
|
||||
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
});
|
||||
|
||||
handleContainerLayout = (e) => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
import MarkdownTableImage from './markdown_table_image';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -15,4 +18,12 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(MarkdownTableImage);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goToScreen,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MarkdownTableImage);
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ import {preventDoubleTap} from 'app/utils/tap';
|
|||
|
||||
export default class MarkdownTableImage extends React.PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
source: PropTypes.string.isRequired,
|
||||
textStyle: CustomPropTypes.Style.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
serverURL: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
@ -24,26 +26,18 @@ export default class MarkdownTableImage extends React.PureComponent {
|
|||
};
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
|
||||
navigator.push({
|
||||
screen: 'TableImage',
|
||||
title: this.context.intl.formatMessage({
|
||||
id: 'mobile.routes.tableImage',
|
||||
defaultMessage: 'Image',
|
||||
}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
imageSource: this.getImageSource(),
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'TableImage';
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.routes.tableImage',
|
||||
defaultMessage: 'Image',
|
||||
});
|
||||
const passProps = {
|
||||
imageSource: this.getImageSource(),
|
||||
};
|
||||
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
});
|
||||
|
||||
getImageSource = () => {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export default class ActionMenu extends PureComponent {
|
|||
options: PropTypes.arrayOf(PropTypes.object),
|
||||
postId: PropTypes.string.isRequired,
|
||||
selected: PropTypes.object,
|
||||
navigator: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
|
@ -63,7 +62,6 @@ export default class ActionMenu extends PureComponent {
|
|||
name,
|
||||
dataSource,
|
||||
options,
|
||||
navigator,
|
||||
} = this.props;
|
||||
const {selected} = this.state;
|
||||
|
||||
|
|
@ -73,7 +71,6 @@ export default class ActionMenu extends PureComponent {
|
|||
dataSource={dataSource}
|
||||
options={options}
|
||||
selected={selected}
|
||||
navigator={navigator}
|
||||
onSelected={this.handleSelect}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,14 +10,12 @@ import ActionButton from './action_button';
|
|||
export default class AttachmentActions extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.array,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
postId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
actions,
|
||||
navigator,
|
||||
postId,
|
||||
} = this.props;
|
||||
|
||||
|
|
@ -43,7 +41,6 @@ export default class AttachmentActions extends PureComponent {
|
|||
defaultOption={action.default_option}
|
||||
options={action.options}
|
||||
postId={postId}
|
||||
navigator={navigator}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export default class AttachmentFields extends PureComponent {
|
|||
blockStyles: PropTypes.object.isRequired,
|
||||
fields: PropTypes.array,
|
||||
metadata: PropTypes.object,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
textStyles: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
|
|
@ -27,7 +26,6 @@ export default class AttachmentFields extends PureComponent {
|
|||
blockStyles,
|
||||
fields,
|
||||
metadata,
|
||||
navigator,
|
||||
onPermalinkPress,
|
||||
textStyles,
|
||||
theme,
|
||||
|
|
@ -88,7 +86,6 @@ export default class AttachmentFields extends PureComponent {
|
|||
blockStyles={blockStyles}
|
||||
imagesMetadata={metadata?.images}
|
||||
value={(field.value || '')}
|
||||
navigator={navigator}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@ const VIEWPORT_IMAGE_CONTAINER_OFFSET = 10;
|
|||
|
||||
export default class AttachmentImage extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
imageMetadata: PropTypes.object,
|
||||
imageUrl: PropTypes.string,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
|
@ -47,7 +49,7 @@ export default class AttachmentImage extends PureComponent {
|
|||
}
|
||||
|
||||
handlePreviewImage = () => {
|
||||
const {imageUrl, navigator} = this.props;
|
||||
const {actions, imageUrl} = this.props;
|
||||
const {
|
||||
imageUri: uri,
|
||||
originalHeight,
|
||||
|
|
@ -73,7 +75,7 @@ export default class AttachmentImage extends PureComponent {
|
|||
localPath: uri,
|
||||
},
|
||||
}];
|
||||
previewImageAtIndex(navigator, [this.refs.item], 0, files);
|
||||
previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext);
|
||||
};
|
||||
|
||||
setImageDimensions = (imageUri, dimensions, originalWidth, originalHeight) => {
|
||||
19
app/components/message_attachments/attachment_image/index.js
Normal file
19
app/components/message_attachments/attachment_image/index.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
|
||||
import AttachmentImage from './attachment_image';
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(AttachmentImage);
|
||||
|
|
@ -13,7 +13,6 @@ export default class AttachmentPreText extends PureComponent {
|
|||
baseTextStyle: CustomPropTypes.Style.isRequired,
|
||||
blockStyles: PropTypes.object.isRequired,
|
||||
metadata: PropTypes.object,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
textStyles: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
|
|
@ -24,7 +23,6 @@ export default class AttachmentPreText extends PureComponent {
|
|||
baseTextStyle,
|
||||
blockStyles,
|
||||
metadata,
|
||||
navigator,
|
||||
onPermalinkPress,
|
||||
value,
|
||||
textStyles,
|
||||
|
|
@ -42,7 +40,6 @@ export default class AttachmentPreText extends PureComponent {
|
|||
blockStyles={blockStyles}
|
||||
imagesMetadata={metadata?.images}
|
||||
value={value}
|
||||
navigator={navigator}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export default class AttachmentText extends PureComponent {
|
|||
deviceHeight: PropTypes.number.isRequired,
|
||||
hasThumbnail: PropTypes.bool,
|
||||
metadata: PropTypes.object,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
textStyles: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
|
|
@ -68,7 +67,6 @@ export default class AttachmentText extends PureComponent {
|
|||
blockStyles,
|
||||
hasThumbnail,
|
||||
metadata,
|
||||
navigator,
|
||||
onPermalinkPress,
|
||||
value,
|
||||
textStyles,
|
||||
|
|
@ -97,7 +95,6 @@ export default class AttachmentText extends PureComponent {
|
|||
blockStyles={blockStyles}
|
||||
imagesMetadata={metadata?.images}
|
||||
value={value}
|
||||
navigator={navigator}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ export default class AttachmentTitle extends PureComponent {
|
|||
link: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
openLink = () => {
|
||||
|
|
@ -28,7 +27,6 @@ export default class AttachmentTitle extends PureComponent {
|
|||
link,
|
||||
value,
|
||||
theme,
|
||||
navigator,
|
||||
} = this.props;
|
||||
|
||||
if (!value) {
|
||||
|
|
@ -57,7 +55,6 @@ export default class AttachmentTitle extends PureComponent {
|
|||
disableChannelLink={true}
|
||||
autolinkedUrlSchemes={[]}
|
||||
mentionKeys={[]}
|
||||
navigator={navigator}
|
||||
theme={theme}
|
||||
value={value}
|
||||
baseTextStyle={style.title}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export default class MessageAttachments extends PureComponent {
|
|||
deviceWidth: PropTypes.number.isRequired,
|
||||
postId: PropTypes.string.isRequired,
|
||||
metadata: PropTypes.object,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onHashtagPress: PropTypes.func,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
theme: PropTypes.object,
|
||||
|
|
@ -33,7 +32,6 @@ export default class MessageAttachments extends PureComponent {
|
|||
deviceHeight,
|
||||
deviceWidth,
|
||||
metadata,
|
||||
navigator,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
postId,
|
||||
|
|
@ -52,7 +50,6 @@ export default class MessageAttachments extends PureComponent {
|
|||
deviceWidth={deviceWidth}
|
||||
key={'att_' + i}
|
||||
metadata={metadata}
|
||||
navigator={navigator}
|
||||
onHashtagPress={onHashtagPress}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
postId={postId}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ export default class MessageAttachment extends PureComponent {
|
|||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
metadata: PropTypes.object,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
postId: PropTypes.string.isRequired,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
theme: PropTypes.object,
|
||||
|
|
@ -46,7 +45,6 @@ export default class MessageAttachment extends PureComponent {
|
|||
deviceHeight,
|
||||
deviceWidth,
|
||||
metadata,
|
||||
navigator,
|
||||
onPermalinkPress,
|
||||
postId,
|
||||
textStyles,
|
||||
|
|
@ -70,7 +68,6 @@ export default class MessageAttachment extends PureComponent {
|
|||
baseTextStyle={baseTextStyle}
|
||||
blockStyles={blockStyles}
|
||||
metadata={metadata}
|
||||
navigator={navigator}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
textStyles={textStyles}
|
||||
value={attachment.pretext}
|
||||
|
|
@ -86,7 +83,6 @@ export default class MessageAttachment extends PureComponent {
|
|||
link={attachment.title_link}
|
||||
theme={theme}
|
||||
value={attachment.title}
|
||||
navigator={navigator}
|
||||
/>
|
||||
<AttachmentThumbnail url={attachment.thumb_url}/>
|
||||
<AttachmentText
|
||||
|
|
@ -95,7 +91,6 @@ export default class MessageAttachment extends PureComponent {
|
|||
deviceHeight={deviceHeight}
|
||||
hasThumbnail={Boolean(attachment.thumb_url)}
|
||||
metadata={metadata}
|
||||
navigator={navigator}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
textStyles={textStyles}
|
||||
value={attachment.text}
|
||||
|
|
@ -105,14 +100,12 @@ export default class MessageAttachment extends PureComponent {
|
|||
blockStyles={blockStyles}
|
||||
fields={attachment.fields}
|
||||
metadata={metadata}
|
||||
navigator={navigator}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
textStyles={textStyles}
|
||||
theme={theme}
|
||||
/>
|
||||
<AttachmentActions
|
||||
actions={attachment.actions}
|
||||
navigator={navigator}
|
||||
postId={postId}
|
||||
/>
|
||||
<AttachmentImage
|
||||
|
|
@ -120,7 +113,6 @@ export default class MessageAttachment extends PureComponent {
|
|||
deviceWidth={deviceWidth}
|
||||
imageUrl={attachment.image_url}
|
||||
imageMetadata={metadata?.images?.[attachment.image_url]}
|
||||
navigator={navigator}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {getUser, getCurrentUserId} from 'mattermost-redux/selectors/entities/use
|
|||
import {getMyPreferences, getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {isPostFlagged, isSystemMessage} from 'mattermost-redux/utils/post_utils';
|
||||
|
||||
import {goToScreen, showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
import {insertToDraft, setPostTooltipVisible} from 'app/actions/views/channel';
|
||||
|
||||
import Post from './post';
|
||||
|
|
@ -93,6 +94,8 @@ function mapDispatchToProps(dispatch) {
|
|||
removePost,
|
||||
setPostTooltipVisible,
|
||||
insertToDraft,
|
||||
goToScreen,
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ export default class Post extends PureComponent {
|
|||
createPost: PropTypes.func.isRequired,
|
||||
insertToDraft: PropTypes.func.isRequired,
|
||||
removePost: PropTypes.func.isRequired,
|
||||
goToScreen: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
channelIsReadOnly: PropTypes.bool,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
|
|
@ -49,7 +51,6 @@ export default class Post extends PureComponent {
|
|||
isSearchResult: PropTypes.bool,
|
||||
commentedOnPost: PropTypes.object,
|
||||
managedConfig: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onHashtagPress: PropTypes.func,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
shouldRenderReplyButton: PropTypes.bool,
|
||||
|
|
@ -88,30 +89,16 @@ export default class Post extends PureComponent {
|
|||
|
||||
goToUserProfile = () => {
|
||||
const {intl} = this.context;
|
||||
const {navigator, post, theme} = this.props;
|
||||
const options = {
|
||||
screen: 'UserProfile',
|
||||
title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}),
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
passProps: {
|
||||
userId: post.user_id,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
const {actions, post} = this.props;
|
||||
const screen = 'UserProfile';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const passProps = {
|
||||
userId: post.user_id,
|
||||
};
|
||||
|
||||
Keyboard.dismiss();
|
||||
requestAnimationFrame(() => {
|
||||
if (Platform.OS === 'ios') {
|
||||
navigator.push(options);
|
||||
} else {
|
||||
navigator.showModal(options);
|
||||
}
|
||||
actions.goToScreen(screen, title, passProps);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -120,7 +107,8 @@ export default class Post extends PureComponent {
|
|||
};
|
||||
|
||||
handleFailedPostPress = () => {
|
||||
const options = {
|
||||
const screen = 'OptionsModal';
|
||||
const passProps = {
|
||||
title: {
|
||||
id: t('mobile.post.failed_title'),
|
||||
defaultMessage: 'Unable to send your message:',
|
||||
|
|
@ -151,22 +139,7 @@ export default class Post extends PureComponent {
|
|||
}],
|
||||
};
|
||||
|
||||
this.props.navigator.showModal({
|
||||
screen: 'OptionsModal',
|
||||
title: '',
|
||||
animationType: 'none',
|
||||
passProps: {
|
||||
items: options.items,
|
||||
title: options.title,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
});
|
||||
this.props.actions.showModalOverCurrentContext(screen, passProps);
|
||||
};
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
|
|
@ -352,7 +325,6 @@ export default class Post extends PureComponent {
|
|||
channelIsReadOnly={channelIsReadOnly}
|
||||
isLastPost={isLastPost}
|
||||
isSearchResult={isSearchResult}
|
||||
navigator={this.props.navigator}
|
||||
onFailedPostPress={this.handleFailedPostPress}
|
||||
onHashtagPress={onHashtagPress}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ export default class PostAddChannelMember extends React.PureComponent {
|
|||
userIds: PropTypes.array.isRequired,
|
||||
usernames: PropTypes.array.isRequired,
|
||||
noGroupsUsernames: PropTypes.array,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onPostPress: PropTypes.func,
|
||||
textStyles: PropTypes.object,
|
||||
};
|
||||
|
|
@ -90,7 +89,6 @@ export default class PostAddChannelMember extends React.PureComponent {
|
|||
mentionStyle={this.props.textStyles.mention}
|
||||
mentionName={usernames[0]}
|
||||
onPostPress={this.props.onPostPress}
|
||||
navigator={this.props.navigator}
|
||||
/>
|
||||
);
|
||||
} else if (usernames.length > 1) {
|
||||
|
|
@ -119,7 +117,6 @@ export default class PostAddChannelMember extends React.PureComponent {
|
|||
mentionStyle={this.props.textStyles.mention}
|
||||
mentionName={username}
|
||||
onPostPress={this.props.onPostPress}
|
||||
navigator={this.props.navigator}
|
||||
/>
|
||||
);
|
||||
}).reduce((acc, el, idx, arr) => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import {bindActionCreators} from 'redux';
|
|||
|
||||
import {getOpenGraphMetadata} from 'mattermost-redux/actions/posts';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
|
||||
import PostAttachmentOpenGraph from './post_attachment_opengraph';
|
||||
|
|
@ -20,6 +22,7 @@ function mapDispatchToProps(dispatch) {
|
|||
return {
|
||||
actions: bindActionCreators({
|
||||
getOpenGraphMetadata,
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
getOpenGraphMetadata: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
imagesMetadata: PropTypes.object,
|
||||
isReplyPost: PropTypes.bool,
|
||||
link: PropTypes.string.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
openGraphData: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
@ -195,6 +195,7 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
originalWidth,
|
||||
originalHeight,
|
||||
} = this.state;
|
||||
const {actions} = this.props;
|
||||
const filename = this.getFilename(link);
|
||||
|
||||
const files = [{
|
||||
|
|
@ -209,7 +210,7 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
},
|
||||
}];
|
||||
|
||||
previewImageAtIndex(this.props.navigator, [this.refs.item], 0, files);
|
||||
previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext);
|
||||
};
|
||||
|
||||
renderDescription = () => {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ describe('PostAttachmentOpenGraph', () => {
|
|||
const baseProps = {
|
||||
actions: {
|
||||
getOpenGraphMetadata: jest.fn(),
|
||||
showModalOverCurrentContext: jest.fn(),
|
||||
},
|
||||
deviceHeight: 600,
|
||||
deviceWidth: 400,
|
||||
|
|
@ -36,7 +37,6 @@ describe('PostAttachmentOpenGraph', () => {
|
|||
},
|
||||
isReplyPost: false,
|
||||
link: 'https://mattermost.com/',
|
||||
navigator: {},
|
||||
theme: Preferences.THEMES.default,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {General, Posts} from 'mattermost-redux/constants';
|
||||
|
|
@ -21,6 +22,8 @@ import {
|
|||
} from 'mattermost-redux/utils/post_utils';
|
||||
import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
|
||||
import {hasEmojisOnly} from 'app/utils/emoji_utils';
|
||||
|
|
@ -102,4 +105,12 @@ function makeMapStateToProps() {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps, null, null, {forwardRef: true})(PostBody);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps, null, {forwardRef: true})(PostBody);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ const SHOW_MORE_HEIGHT = 60;
|
|||
|
||||
export default class PostBody extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
canDelete: PropTypes.bool,
|
||||
channelIsReadOnly: PropTypes.bool.isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
|
|
@ -56,7 +59,6 @@ export default class PostBody extends PureComponent {
|
|||
metadata: PropTypes.object,
|
||||
managedConfig: PropTypes.object,
|
||||
message: PropTypes.string,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onFailedPostPress: PropTypes.func,
|
||||
onHashtagPress: PropTypes.func,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
|
|
@ -130,31 +132,25 @@ export default class PostBody extends PureComponent {
|
|||
openLongPost = preventDoubleTap(() => {
|
||||
const {
|
||||
managedConfig,
|
||||
navigator,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
post,
|
||||
actions,
|
||||
} = this.props;
|
||||
|
||||
const screen = 'LongPost';
|
||||
const passProps = {
|
||||
postId: post.id,
|
||||
managedConfig,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
};
|
||||
const options = {
|
||||
screen: 'LongPost',
|
||||
animationType: 'none',
|
||||
backButtonTitle: '',
|
||||
overrideBackPress: true,
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
screenBackgroundColor: changeOpacity('#000', 0.2),
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
passProps: {
|
||||
postId: post.id,
|
||||
managedConfig,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
layout: {
|
||||
backgroundColor: changeOpacity('#000', 0.2),
|
||||
},
|
||||
};
|
||||
|
||||
navigator.showModal(options);
|
||||
actions.showModalOverCurrentContext(screen, passProps, options);
|
||||
});
|
||||
|
||||
showPostOptions = () => {
|
||||
|
|
@ -168,10 +164,10 @@ export default class PostBody extends PureComponent {
|
|||
isPostEphemeral,
|
||||
isSystemMessage,
|
||||
managedConfig,
|
||||
navigator,
|
||||
post,
|
||||
showAddReaction,
|
||||
location,
|
||||
actions,
|
||||
} = this.props;
|
||||
|
||||
if (isSystemMessage && (!canDelete || hasBeenDeleted)) {
|
||||
|
|
@ -182,32 +178,22 @@ export default class PostBody extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
screen: 'PostOptions',
|
||||
animationType: 'none',
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
navBarTransparent: true,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
passProps: {
|
||||
canDelete,
|
||||
channelIsReadOnly,
|
||||
hasBeenDeleted,
|
||||
isFlagged,
|
||||
isSystemMessage,
|
||||
post,
|
||||
managedConfig,
|
||||
showAddReaction,
|
||||
location,
|
||||
},
|
||||
const screen = 'PostOptions';
|
||||
const passProps = {
|
||||
canDelete,
|
||||
channelIsReadOnly,
|
||||
hasBeenDeleted,
|
||||
isFlagged,
|
||||
isSystemMessage,
|
||||
post,
|
||||
managedConfig,
|
||||
showAddReaction,
|
||||
location,
|
||||
};
|
||||
|
||||
Keyboard.dismiss();
|
||||
requestAnimationFrame(() => {
|
||||
navigator.showModal(options);
|
||||
actions.showModalOverCurrentContext(screen, passProps);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -231,7 +217,6 @@ export default class PostBody extends PureComponent {
|
|||
return (
|
||||
<PostAddChannelMember
|
||||
baseTextStyle={messageStyle}
|
||||
navigator={navigator}
|
||||
onPostPress={onPress}
|
||||
textStyles={textStyles}
|
||||
postId={postProps.add_channel_member.post_id}
|
||||
|
|
@ -246,7 +231,6 @@ export default class PostBody extends PureComponent {
|
|||
const {
|
||||
fileIds,
|
||||
isFailed,
|
||||
navigator,
|
||||
post,
|
||||
showLongPost,
|
||||
} = this.props;
|
||||
|
|
@ -267,7 +251,6 @@ export default class PostBody extends PureComponent {
|
|||
isFailed={isFailed}
|
||||
onLongPress={this.showPostOptions}
|
||||
postId={post.id}
|
||||
navigator={navigator}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -281,7 +264,6 @@ export default class PostBody extends PureComponent {
|
|||
isSystemMessage,
|
||||
message,
|
||||
metadata,
|
||||
navigator,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
post,
|
||||
|
|
@ -304,7 +286,6 @@ export default class PostBody extends PureComponent {
|
|||
<PostBodyAdditionalContent
|
||||
baseTextStyle={messageStyle}
|
||||
blockStyles={blockStyles}
|
||||
navigator={navigator}
|
||||
message={message}
|
||||
metadata={metadata}
|
||||
postId={post.id}
|
||||
|
|
@ -321,7 +302,6 @@ export default class PostBody extends PureComponent {
|
|||
const {
|
||||
hasReactions,
|
||||
isSearchResult,
|
||||
navigator,
|
||||
post,
|
||||
showLongPost,
|
||||
} = this.props;
|
||||
|
|
@ -337,7 +317,6 @@ export default class PostBody extends PureComponent {
|
|||
return (
|
||||
<Reactions
|
||||
postId={post.id}
|
||||
navigator={navigator}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -356,7 +335,6 @@ export default class PostBody extends PureComponent {
|
|||
isSystemMessage,
|
||||
message,
|
||||
metadata,
|
||||
navigator,
|
||||
onFailedPostPress,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
|
|
@ -395,7 +373,6 @@ export default class PostBody extends PureComponent {
|
|||
allUsernames={allUsernames}
|
||||
linkStyle={textStyles.link}
|
||||
messageData={messageData}
|
||||
navigator={navigator}
|
||||
textStyles={textStyles}
|
||||
theme={theme}
|
||||
/>
|
||||
|
|
@ -424,7 +401,6 @@ export default class PostBody extends PureComponent {
|
|||
isEdited={hasBeenEdited}
|
||||
isReplyPost={isReplyPost}
|
||||
isSearchResult={isSearchResult}
|
||||
navigator={navigator}
|
||||
onHashtagPress={onHashtagPress}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
onPostPress={onPress}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ import PostBody from './post_body.js';
|
|||
|
||||
describe('PostBody', () => {
|
||||
const baseProps = {
|
||||
actions: {
|
||||
showModalOverCurrentContext: jest.fn(),
|
||||
},
|
||||
canDelete: true,
|
||||
channelIsReadOnly: false,
|
||||
deviceHeight: 1920,
|
||||
|
|
@ -30,7 +33,6 @@ describe('PostBody', () => {
|
|||
isSystemMessage: false,
|
||||
managedConfig: {},
|
||||
message: 'Hello, World!',
|
||||
navigator: {},
|
||||
onFailedPostPress: jest.fn(),
|
||||
onHashtagPress: jest.fn(),
|
||||
onPermalinkPress: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
|||
import {getOpenGraphMetadataForUrl} from 'mattermost-redux/selectors/entities/posts';
|
||||
import {getBool, getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
import {extractFirstLink} from 'app/utils/url';
|
||||
|
|
@ -75,6 +76,7 @@ function mapDispatchToProps(dispatch) {
|
|||
return {
|
||||
actions: bindActionCreators({
|
||||
getRedirectLocation,
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
getRedirectLocation: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
baseTextStyle: CustomPropTypes.Style,
|
||||
blockStyles: PropTypes.object,
|
||||
|
|
@ -48,7 +49,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
isReplyPost: PropTypes.bool,
|
||||
link: PropTypes.string,
|
||||
message: PropTypes.string.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
onHashtagPress: PropTypes.func,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
openGraphData: PropTypes.object,
|
||||
|
|
@ -182,7 +182,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
};
|
||||
|
||||
generateStaticEmbed = (isYouTube, isImage) => {
|
||||
const {isReplyPost, link, metadata, navigator, openGraphData, showLinkPreviews, theme} = this.props;
|
||||
const {isReplyPost, link, metadata, openGraphData, showLinkPreviews, theme} = this.props;
|
||||
|
||||
if (isYouTube || (isImage && !openGraphData)) {
|
||||
return null;
|
||||
|
|
@ -206,7 +206,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
<PostAttachmentOpenGraph
|
||||
isReplyPost={isReplyPost}
|
||||
link={link}
|
||||
navigator={navigator}
|
||||
openGraphData={openGraphData}
|
||||
imagesMetadata={metadata && metadata.images}
|
||||
theme={theme}
|
||||
|
|
@ -343,7 +342,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
deviceHeight,
|
||||
deviceWidth,
|
||||
metadata,
|
||||
navigator,
|
||||
onHashtagPress,
|
||||
onPermalinkPress,
|
||||
textStyles,
|
||||
|
|
@ -364,7 +362,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
deviceHeight={deviceHeight}
|
||||
deviceWidth={deviceWidth}
|
||||
metadata={metadata}
|
||||
navigator={navigator}
|
||||
postId={postId}
|
||||
textStyles={textStyles}
|
||||
theme={theme}
|
||||
|
|
@ -413,7 +410,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
handlePreviewImage = (imageRef) => {
|
||||
const {shortenedLink} = this.state;
|
||||
let {link} = this.props;
|
||||
const {navigator} = this.props;
|
||||
const {actions} = this.props;
|
||||
if (shortenedLink) {
|
||||
link = shortenedLink;
|
||||
}
|
||||
|
|
@ -435,7 +432,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
|
|||
},
|
||||
}];
|
||||
|
||||
previewImageAtIndex(navigator, [imageRef], 0, files);
|
||||
previewImageAtIndex([imageRef], 0, files, actions.showModalOverCurrentContext);
|
||||
};
|
||||
|
||||
playYouTubeVideo = () => {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {getConfig, getCurrentUrl} from 'mattermost-redux/selectors/entities/gene
|
|||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {makePreparePostIdsForPostList, START_OF_NEW_MESSAGES} from 'mattermost-redux/utils/post_list';
|
||||
|
||||
import {showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
import {handleSelectChannelByName, loadChannelsByTeamName, refreshChannelWithRetry} from 'app/actions/views/channel';
|
||||
import {setDeepLinkURL} from 'app/actions/views/root';
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ function mapDispatchToProps(dispatch) {
|
|||
refreshChannelWithRetry,
|
||||
selectFocusedPostId,
|
||||
setDeepLinkURL,
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ export default class PostList extends PureComponent {
|
|||
refreshChannelWithRetry: PropTypes.func.isRequired,
|
||||
selectFocusedPostId: PropTypes.func.isRequired,
|
||||
setDeepLinkURL: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
channelId: PropTypes.string,
|
||||
deepLinkURL: PropTypes.string,
|
||||
|
|
@ -51,7 +52,6 @@ export default class PostList extends PureComponent {
|
|||
isSearchResult: PropTypes.bool,
|
||||
lastPostIndex: PropTypes.number.isRequired,
|
||||
lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types
|
||||
navigator: PropTypes.object,
|
||||
onLoadMoreUp: PropTypes.func,
|
||||
onHashtagPress: PropTypes.func,
|
||||
onPermalinkPress: PropTypes.func,
|
||||
|
|
@ -250,7 +250,6 @@ export default class PostList extends PureComponent {
|
|||
isSearchResult: this.props.isSearchResult,
|
||||
location: this.props.location,
|
||||
managedConfig: mattermostManaged.getCachedConfig(),
|
||||
navigator: this.props.navigator,
|
||||
onHashtagPress: this.props.onHashtagPress,
|
||||
onPermalinkPress: this.handlePermalinkPress,
|
||||
onPress: this.props.onPostPress,
|
||||
|
|
@ -307,29 +306,24 @@ export default class PostList extends PureComponent {
|
|||
};
|
||||
|
||||
showPermalinkView = (postId) => {
|
||||
const {actions, navigator} = this.props;
|
||||
const {actions} = this.props;
|
||||
|
||||
actions.selectFocusedPostId(postId);
|
||||
|
||||
if (!this.showingPermalink) {
|
||||
const screen = 'Permalink';
|
||||
const passProps = {
|
||||
isPermalink: true,
|
||||
onClose: this.handleClosePermalink,
|
||||
};
|
||||
const options = {
|
||||
screen: 'Permalink',
|
||||
animationType: 'none',
|
||||
backButtonTitle: '',
|
||||
overrideBackPress: true,
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
screenBackgroundColor: changeOpacity('#000', 0.2),
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
passProps: {
|
||||
isPermalink: true,
|
||||
onClose: this.handleClosePermalink,
|
||||
layout: {
|
||||
backgroundColor: changeOpacity('#000', 0.2),
|
||||
},
|
||||
};
|
||||
|
||||
this.showingPermalink = true;
|
||||
navigator.showModal(options);
|
||||
actions.showModalOverCurrentContext(screen, passProps, options);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ describe('PostList', () => {
|
|||
refreshChannelWithRetry: jest.fn(),
|
||||
selectFocusedPostId: jest.fn(),
|
||||
setDeepLinkURL: jest.fn(),
|
||||
showModalOverCurrentContext: jest.fn(),
|
||||
},
|
||||
deepLinkURL: '',
|
||||
navigator: {
|
||||
showModal: jest.fn(),
|
||||
},
|
||||
lastPostIndex: -1,
|
||||
postIds: ['post-id-1', 'post-id-2'],
|
||||
serverURL,
|
||||
|
|
@ -47,7 +45,7 @@ describe('PostList', () => {
|
|||
wrapper.setProps({deepLinkURL: deepLinks.permalink});
|
||||
expect(baseProps.actions.setDeepLinkURL).toHaveBeenCalled();
|
||||
expect(baseProps.actions.selectFocusedPostId).toHaveBeenCalled();
|
||||
expect(baseProps.navigator.showModal).toHaveBeenCalled();
|
||||
expect(baseProps.actions.showModalOverCurrentContext).toHaveBeenCalled();
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,23 +16,11 @@ exports[`PostTextBox should match, full snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<AttachmentButton
|
||||
<Connect(AttachmentButton)
|
||||
blurTextBox={[Function]}
|
||||
browseFileTypes="public.item"
|
||||
canBrowseFiles={true}
|
||||
canBrowsePhotoLibrary={true}
|
||||
canBrowseVideoLibrary={true}
|
||||
canTakePhoto={true}
|
||||
canTakeVideo={true}
|
||||
extraOptions={null}
|
||||
fileCount={0}
|
||||
maxFileCount={5}
|
||||
maxFileSize={1024}
|
||||
navigator={
|
||||
Object {
|
||||
"showModal": [MockFunction],
|
||||
}
|
||||
}
|
||||
onShowFileMaxWarning={[Function]}
|
||||
onShowFileSizeWarning={[Function]}
|
||||
theme={
|
||||
|
|
@ -64,7 +52,6 @@ exports[`PostTextBox should match, full snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
uploadFiles={[Function]}
|
||||
validMimeTypes={Array []}
|
||||
/>
|
||||
<View
|
||||
style={
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@ describe('PostTextBox', () => {
|
|||
files: [],
|
||||
maxFileSize: 1024,
|
||||
maxMessageLength: 4000,
|
||||
navigator: {
|
||||
showModal: jest.fn(),
|
||||
},
|
||||
rootId: '',
|
||||
theme: Preferences.THEMES.default,
|
||||
uploadFileRequestStatus: 'NOT_STARTED',
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ export default class PostTextBoxBase extends PureComponent {
|
|||
files: PropTypes.array,
|
||||
maxFileSize: PropTypes.number.isRequired,
|
||||
maxMessageLength: PropTypes.number.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
rootId: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
uploadFileRequestStatus: PropTypes.string.isRequired,
|
||||
|
|
@ -189,7 +188,7 @@ export default class PostTextBoxBase extends PureComponent {
|
|||
};
|
||||
|
||||
getAttachmentButton = () => {
|
||||
const {canUploadFiles, channelIsReadOnly, files, maxFileSize, navigator, theme} = this.props;
|
||||
const {canUploadFiles, channelIsReadOnly, files, maxFileSize, theme} = this.props;
|
||||
let attachmentButton = null;
|
||||
|
||||
if (canUploadFiles && !channelIsReadOnly) {
|
||||
|
|
@ -197,7 +196,6 @@ export default class PostTextBoxBase extends PureComponent {
|
|||
<AttachmentButton
|
||||
blurTextBox={this.blur}
|
||||
theme={theme}
|
||||
navigator={navigator}
|
||||
fileCount={files.length}
|
||||
maxFileSize={maxFileSize}
|
||||
maxFileCount={MAX_FILE_COUNT}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,8 @@ import ProfilePictureButton from './profile_picture_button.js';
|
|||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
describe('profile_picture_button', () => {
|
||||
const navigator = {
|
||||
setOnNavigatorEvent: jest.fn(),
|
||||
setButtons: jest.fn(),
|
||||
dismissModal: jest.fn(),
|
||||
push: jest.fn(),
|
||||
};
|
||||
|
||||
const baseProps = {
|
||||
theme: Preferences.THEMES.default,
|
||||
navigator,
|
||||
currentUser: {
|
||||
first_name: 'Dwight',
|
||||
last_name: 'Schrute',
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
|||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {showModal, showModalOverCurrentContext} from 'app/actions/navigation';
|
||||
import {addReaction} from 'app/actions/views/emoji';
|
||||
|
||||
import Reactions from './reactions';
|
||||
|
|
@ -63,6 +64,8 @@ function mapDispatchToProps(dispatch) {
|
|||
addReaction,
|
||||
getReactionsForPost,
|
||||
removeReaction,
|
||||
showModal,
|
||||
showModalOverCurrentContext,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ export default class Reactions extends PureComponent {
|
|||
addReaction: PropTypes.func.isRequired,
|
||||
getReactionsForPost: PropTypes.func.isRequired,
|
||||
removeReaction: PropTypes.func.isRequired,
|
||||
showModal: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
position: PropTypes.oneOf(['right', 'left']),
|
||||
postId: PropTypes.string.isRequired,
|
||||
reactions: PropTypes.object,
|
||||
|
|
@ -50,25 +51,18 @@ export default class Reactions extends PureComponent {
|
|||
}
|
||||
|
||||
handleAddReaction = preventDoubleTap(() => {
|
||||
const {actions, theme} = this.props;
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {navigator, theme} = this.props;
|
||||
const screen = 'AddReaction';
|
||||
const title = formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'});
|
||||
|
||||
MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((source) => {
|
||||
navigator.showModal({
|
||||
screen: 'AddReaction',
|
||||
title: formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
passProps: {
|
||||
closeButton: source,
|
||||
onEmojiPress: this.handleAddReactionToPost,
|
||||
},
|
||||
});
|
||||
const passProps = {
|
||||
closeButton: source,
|
||||
onEmojiPress: this.handleAddReactionToPost,
|
||||
};
|
||||
|
||||
actions.showModal(screen, title, passProps);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -87,28 +81,18 @@ export default class Reactions extends PureComponent {
|
|||
};
|
||||
|
||||
showReactionList = () => {
|
||||
const {navigator, postId} = this.props;
|
||||
const {actions, postId} = this.props;
|
||||
|
||||
const options = {
|
||||
screen: 'ReactionList',
|
||||
animationType: 'none',
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
navBarTransparent: true,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
passProps: {
|
||||
postId,
|
||||
},
|
||||
const screen = 'ReactionList';
|
||||
const passProps = {
|
||||
postId,
|
||||
};
|
||||
|
||||
navigator.showModal(options);
|
||||
actions.showModalOverCurrentContext(screen, passProps);
|
||||
}
|
||||
|
||||
renderReactions = () => {
|
||||
const {currentUserId, navigator, reactions, theme, postId} = this.props;
|
||||
const {currentUserId, reactions, theme, postId} = this.props;
|
||||
const highlightedReactions = [];
|
||||
const reactionsByName = Object.values(reactions).reduce((acc, reaction) => {
|
||||
if (acc.has(reaction.emoji_name)) {
|
||||
|
|
@ -131,7 +115,6 @@ export default class Reactions extends PureComponent {
|
|||
count={reactionsByName.get(r).length}
|
||||
emojiName={r}
|
||||
highlight={highlightedReactions.includes(r)}
|
||||
navigator={navigator}
|
||||
onPress={this.handleReactionPress}
|
||||
onLongPress={this.showReactionList}
|
||||
postId={postId}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {resetToTeams} from 'app/actions/navigation';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {removeProtocol} from 'app/utils/url';
|
||||
|
||||
import Root from './root';
|
||||
|
|
@ -17,10 +18,17 @@ function mapStateToProps(state) {
|
|||
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
currentChannelId: getCurrentChannelId(state),
|
||||
currentUrl: removeProtocol(getCurrentUrl(state)),
|
||||
locale,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Root);
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
resetToTeams,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Root);
|
||||
|
|
|
|||
|
|
@ -9,15 +9,16 @@ import {Platform} from 'react-native';
|
|||
import {Client4} from 'mattermost-redux/client';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {NavigationTypes, ViewTypes} from 'app/constants';
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {getTranslations} from 'app/i18n';
|
||||
|
||||
export default class Root extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
resetToTeams: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
children: PropTypes.node,
|
||||
navigator: PropTypes.object,
|
||||
excludeEvents: PropTypes.bool,
|
||||
currentChannelId: PropTypes.string,
|
||||
currentUrl: PropTypes.string,
|
||||
locale: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
|
|
@ -27,8 +28,6 @@ export default class Root extends PureComponent {
|
|||
Client4.setAcceptLanguage(this.props.locale);
|
||||
|
||||
if (!this.props.excludeEvents) {
|
||||
EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
||||
EventEmitter.on(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
|
||||
EventEmitter.on(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
|
||||
EventEmitter.on(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
|
||||
}
|
||||
|
|
@ -42,30 +41,11 @@ export default class Root extends PureComponent {
|
|||
|
||||
componentWillUnmount() {
|
||||
if (!this.props.excludeEvents) {
|
||||
EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
||||
EventEmitter.off(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
|
||||
EventEmitter.off(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
|
||||
EventEmitter.off(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
|
||||
}
|
||||
}
|
||||
|
||||
handleInAppNotification = (notification) => {
|
||||
const {data} = notification;
|
||||
const {currentChannelId, navigator} = this.props;
|
||||
|
||||
if (data && data.channel_id !== currentChannelId) {
|
||||
navigator.showInAppNotification({
|
||||
screen: 'Notification',
|
||||
position: 'top',
|
||||
autoDismissTimerSec: 5,
|
||||
dismissWithSwipe: true,
|
||||
passProps: {
|
||||
notification,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleNoTeams = () => {
|
||||
if (!this.refs.provider) {
|
||||
setTimeout(this.handleNoTeams, 200);
|
||||
|
|
@ -83,28 +63,24 @@ export default class Root extends PureComponent {
|
|||
}
|
||||
|
||||
navigateToTeamsPage = (screen) => {
|
||||
const {currentUrl, navigator, theme} = this.props;
|
||||
const {currentUrl, theme, actions} = this.props;
|
||||
const {intl} = this.refs.provider.getChildContext();
|
||||
|
||||
let navigatorButtons;
|
||||
let passProps = {theme};
|
||||
const options = {topBar: {}};
|
||||
if (Platform.OS === 'android') {
|
||||
navigatorButtons = {
|
||||
rightButtons: [{
|
||||
title: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}),
|
||||
id: 'logout',
|
||||
buttonColor: theme.sidebarHeaderTextColor,
|
||||
showAsAction: 'always',
|
||||
}],
|
||||
};
|
||||
options.topBar.rightButtons = [{
|
||||
id: 'logout',
|
||||
text: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}),
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
showAsAction: 'always',
|
||||
}];
|
||||
} else {
|
||||
navigatorButtons = {
|
||||
leftButtons: [{
|
||||
title: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}),
|
||||
id: 'logout',
|
||||
buttonColor: theme.sidebarHeaderTextColor,
|
||||
}],
|
||||
};
|
||||
options.topBar.leftButtons = [{
|
||||
id: 'logout',
|
||||
text: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}),
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
}];
|
||||
}
|
||||
|
||||
if (screen === 'SelectTeam') {
|
||||
|
|
@ -115,34 +91,11 @@ export default class Root extends PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
navigator.resetTo({
|
||||
screen,
|
||||
title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}),
|
||||
animated: false,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
navigatorButtons,
|
||||
passProps,
|
||||
});
|
||||
const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'});
|
||||
|
||||
actions.resetToTeams(screen, title, passProps, options);
|
||||
}
|
||||
|
||||
handleNotificationTapped = async () => {
|
||||
const {navigator} = this.props;
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
navigator.dismissModal({animation: 'none'});
|
||||
}
|
||||
|
||||
navigator.popToRoot({
|
||||
animated: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const locale = this.props.locale;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ export default class SafeAreaIos extends PureComponent {
|
|||
forceTop: PropTypes.number,
|
||||
keyboardOffset: PropTypes.number.isRequired,
|
||||
navBarBackgroundColor: PropTypes.string,
|
||||
navigator: PropTypes.object,
|
||||
headerComponent: PropTypes.node,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
@ -35,10 +34,6 @@ export default class SafeAreaIos extends PureComponent {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (props.navigator) {
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
}
|
||||
|
||||
this.state = {
|
||||
keyboard: false,
|
||||
safeAreaInsets: {
|
||||
|
|
@ -110,15 +105,6 @@ export default class SafeAreaIos extends PureComponent {
|
|||
this.setState({keyboard: true});
|
||||
};
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
switch (event.id) {
|
||||
case 'willAppear':
|
||||
case 'didDisappear':
|
||||
this.getSafeAreaInsets();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
renderTopBar = () => {
|
||||
const {safeAreaInsets, statusBarHeight} = this.state;
|
||||
const {headerComponent, excludeHeader, forceTop, navBarBackgroundColor, theme} = this.props;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
exports[`ChannelItem should match snapshot 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -98,17 +97,16 @@ exports[`ChannelItem should match snapshot 1`] = `
|
|||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
||||
exports[`ChannelItem should match snapshot for current user i.e currentUser (you) 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -211,17 +209,16 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
|
|||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
||||
exports[`ChannelItem should match snapshot for current user i.e currentUser (you) when isSearchResult 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -324,17 +321,16 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
|
|||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
||||
exports[`ChannelItem should match snapshot for deactivated user and is currentChannel 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -437,17 +433,16 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh
|
|||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
||||
exports[`ChannelItem should match snapshot for deactivated user and is searchResult 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -539,7 +534,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes
|
|||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
||||
|
|
@ -551,11 +546,10 @@ exports[`ChannelItem should match snapshot for showUnreadForMsgs 1`] = `null`;
|
|||
|
||||
exports[`ChannelItem should match snapshot with draft 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -647,17 +641,16 @@ exports[`ChannelItem should match snapshot with draft 1`] = `
|
|||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
||||
exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
|
||||
<AnimatedComponent>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.85}
|
||||
delayPressOut={100}
|
||||
onLongPress={[Function]}
|
||||
<TouchablePreview
|
||||
onPress={[Function]}
|
||||
onPressIn={[Function]}
|
||||
touchableComponent={[Function]}
|
||||
underlayColor="rgba(69,120,191,0.5)"
|
||||
>
|
||||
<View
|
||||
|
|
@ -775,6 +768,6 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
|
|||
/>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchablePreview>
|
||||
</AnimatedComponent>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Animated,
|
||||
Platform,
|
||||
TouchableHighlight,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import ChannelIcon from 'app/components/channel_icon';
|
||||
|
|
@ -32,7 +32,6 @@ export default class ChannelItem extends PureComponent {
|
|||
isUnread: PropTypes.bool,
|
||||
hasDraft: PropTypes.bool,
|
||||
mentions: PropTypes.number.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onSelectChannel: PropTypes.func.isRequired,
|
||||
shouldHideChannel: PropTypes.bool,
|
||||
showUnreadForMsgs: PropTypes.bool.isRequired,
|
||||
|
|
@ -40,6 +39,7 @@ export default class ChannelItem extends PureComponent {
|
|||
unreadMsgs: PropTypes.number.isRequired,
|
||||
isSearchResult: PropTypes.bool,
|
||||
isBot: PropTypes.bool.isRequired,
|
||||
previewChannel: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -58,28 +58,25 @@ export default class ChannelItem extends PureComponent {
|
|||
});
|
||||
});
|
||||
|
||||
onPreview = () => {
|
||||
const {channelId, navigator} = this.props;
|
||||
if (Platform.OS === 'ios' && navigator && this.previewRef) {
|
||||
onPreview = ({reactTag}) => {
|
||||
const {channelId, previewChannel} = this.props;
|
||||
if (previewChannel) {
|
||||
const {intl} = this.context;
|
||||
|
||||
navigator.push({
|
||||
screen: 'ChannelPeek',
|
||||
previewCommit: false,
|
||||
previewView: this.previewRef,
|
||||
previewActions: [{
|
||||
id: 'action-mark-as-read',
|
||||
title: intl.formatMessage({id: 'mobile.channel.markAsRead', defaultMessage: 'Mark As Read'}),
|
||||
}],
|
||||
passProps: {
|
||||
channelId,
|
||||
const passProps = {
|
||||
channelId,
|
||||
};
|
||||
const options = {
|
||||
preview: {
|
||||
reactTag,
|
||||
actions: [{
|
||||
id: 'action-mark-as-read',
|
||||
title: intl.formatMessage({id: 'mobile.channel.markAsRead', defaultMessage: 'Mark As Read'}),
|
||||
}],
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
setPreviewRef = (ref) => {
|
||||
this.previewRef = ref;
|
||||
previewChannel(passProps, options);
|
||||
}
|
||||
};
|
||||
|
||||
showChannelAsUnread = () => {
|
||||
|
|
@ -190,11 +187,12 @@ export default class ChannelItem extends PureComponent {
|
|||
);
|
||||
|
||||
return (
|
||||
<AnimatedView ref={this.setPreviewRef}>
|
||||
<TouchableHighlight
|
||||
<AnimatedView>
|
||||
<Navigation.TouchablePreview
|
||||
touchableComponent={TouchableHighlight}
|
||||
underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
|
||||
onPress={this.onPress}
|
||||
onLongPress={this.onPreview}
|
||||
onPressIn={this.onPreview}
|
||||
>
|
||||
<View style={[style.container, mutedStyle]}>
|
||||
{extraBorder}
|
||||
|
|
@ -210,7 +208,7 @@ export default class ChannelItem extends PureComponent {
|
|||
{badge}
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</Navigation.TouchablePreview>
|
||||
</AnimatedView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import {TouchableHighlight} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Preferences from 'mattermost-redux/constants/preferences';
|
||||
|
||||
|
|
@ -31,7 +31,6 @@ describe('ChannelItem', () => {
|
|||
isUnread: true,
|
||||
hasDraft: false,
|
||||
mentions: 0,
|
||||
navigator: {push: () => {}}, // eslint-disable-line no-empty-function
|
||||
onSelectChannel: () => {}, // eslint-disable-line no-empty-function
|
||||
shouldHideChannel: false,
|
||||
showUnreadForMsgs: true,
|
||||
|
|
@ -216,7 +215,7 @@ describe('ChannelItem', () => {
|
|||
{context: {intl: {formatMessage: jest.fn()}}},
|
||||
);
|
||||
|
||||
wrapper.find(TouchableHighlight).simulate('press');
|
||||
wrapper.find(Navigation.TouchablePreview).simulate('press');
|
||||
jest.runAllTimers();
|
||||
|
||||
const expectedChannelParams = {id: baseProps.channelId, display_name: baseProps.displayName, fake: channel.fake, type: channel.type};
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ let FilteredList = null;
|
|||
|
||||
export default class ChannelsList extends PureComponent {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
onJoinChannel: PropTypes.func.isRequired,
|
||||
onSearchEnds: PropTypes.func.isRequired,
|
||||
onSearchStart: PropTypes.func.isRequired,
|
||||
|
|
@ -30,6 +29,7 @@ export default class ChannelsList extends PureComponent {
|
|||
onShowTeams: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
drawerOpened: PropTypes.bool,
|
||||
previewChannel: PropTypes.func,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -86,9 +86,9 @@ export default class ChannelsList extends PureComponent {
|
|||
render() {
|
||||
const {intl} = this.context;
|
||||
const {
|
||||
navigator,
|
||||
onShowTeams,
|
||||
theme,
|
||||
previewChannel,
|
||||
} = this.props;
|
||||
|
||||
const {searching, term} = this.state;
|
||||
|
|
@ -101,14 +101,15 @@ export default class ChannelsList extends PureComponent {
|
|||
onSelectChannel={this.onSelectChannel}
|
||||
styles={styles}
|
||||
term={term}
|
||||
previewChannel={previewChannel}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
list = (
|
||||
<List
|
||||
navigator={navigator}
|
||||
onSelectChannel={this.onSelectChannel}
|
||||
styles={styles}
|
||||
previewChannel={previewChannel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class FilteredList extends Component {
|
|||
styles: PropTypes.object.isRequired,
|
||||
term: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
previewChannel: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -126,6 +127,7 @@ class FilteredList extends Component {
|
|||
isUnread={channel.isUnread}
|
||||
mentions={0}
|
||||
onSelectChannel={this.onSelectChannel}
|
||||
previewChannel={this.props.previewChannel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
|
@ -17,6 +18,8 @@ import {memoizeResult} from 'mattermost-redux/utils/helpers';
|
|||
import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {showModal} from 'app/actions/navigation';
|
||||
|
||||
import {DeviceTypes, ViewTypes} from 'app/constants';
|
||||
|
||||
import List from './list';
|
||||
|
|
@ -67,6 +70,14 @@ function mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
showModal,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
function areStatesEqual(next, prev) {
|
||||
const equalRoles = getCurrentUserRoles(prev) === getCurrentUserRoles(next);
|
||||
const equalChannels = next.entities.channels === prev.entities.channels;
|
||||
|
|
@ -77,4 +88,4 @@ function areStatesEqual(next, prev) {
|
|||
return equalChannels && equalConfig && equalRoles && equalUsers && equalFav;
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, null, {pure: true, areStatesEqual})(List);
|
||||
export default connect(mapStateToProps, mapDispatchToProps, null, {pure: true, areStatesEqual})(List);
|
||||
|
|
|
|||
|
|
@ -34,14 +34,17 @@ let UnreadIndicator = null;
|
|||
|
||||
export default class List extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
showModal: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
canCreatePrivateChannels: PropTypes.bool.isRequired,
|
||||
favoriteChannelIds: PropTypes.array.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onSelectChannel: PropTypes.func.isRequired,
|
||||
unreadChannelIds: PropTypes.array.isRequired,
|
||||
styles: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
orderedChannelIds: PropTypes.array.isRequired,
|
||||
previewChannel: PropTypes.func,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -159,7 +162,10 @@ export default class List extends PureComponent {
|
|||
};
|
||||
|
||||
showCreateChannelOptions = () => {
|
||||
const {canCreatePrivateChannels, navigator} = this.props;
|
||||
const {
|
||||
canCreatePrivateChannels,
|
||||
actions,
|
||||
} = this.props;
|
||||
|
||||
const items = [];
|
||||
const moreChannels = {
|
||||
|
|
@ -197,117 +203,87 @@ export default class List extends PureComponent {
|
|||
}
|
||||
items.push(newConversation);
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'OptionsModal',
|
||||
title: '',
|
||||
animationType: 'none',
|
||||
passProps: {
|
||||
items,
|
||||
onItemPress: () => navigator.dismissModal({
|
||||
animationType: 'none',
|
||||
}),
|
||||
const screen = 'OptionsModal';
|
||||
const title = '';
|
||||
const passProps = {
|
||||
items,
|
||||
};
|
||||
const options = {
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
layout: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
topBar: {
|
||||
visible: false,
|
||||
height: 0,
|
||||
},
|
||||
});
|
||||
animations: {
|
||||
showModal: {
|
||||
enable: false,
|
||||
},
|
||||
dismissModal: {
|
||||
enable: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
actions.showModal(screen, title, passProps, options);
|
||||
};
|
||||
|
||||
goToCreatePublicChannel = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'CreateChannel';
|
||||
const title = intl.formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'});
|
||||
const passProps = {
|
||||
channelType: General.OPEN_CHANNEL,
|
||||
closeButton: this.closeButton,
|
||||
};
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'CreateChannel',
|
||||
animationType: 'slide-up',
|
||||
title: intl.formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'}),
|
||||
backButtonTitle: '',
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
passProps: {
|
||||
channelType: General.OPEN_CHANNEL,
|
||||
closeButton: this.closeButton,
|
||||
},
|
||||
});
|
||||
actions.showModal(screen, title, passProps);
|
||||
});
|
||||
|
||||
goToCreatePrivateChannel = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'CreateChannel';
|
||||
const title = intl.formatMessage({id: 'mobile.create_channel.private', defaultMessage: 'New Private Channel'});
|
||||
const passProps = {
|
||||
channelType: General.PRIVATE_CHANNEL,
|
||||
closeButton: this.closeButton,
|
||||
};
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'CreateChannel',
|
||||
animationType: 'slide-up',
|
||||
title: intl.formatMessage({id: 'mobile.create_channel.private', defaultMessage: 'New Private Channel'}),
|
||||
backButtonTitle: '',
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
passProps: {
|
||||
channelType: General.PRIVATE_CHANNEL,
|
||||
closeButton: this.closeButton,
|
||||
},
|
||||
});
|
||||
actions.showModal(screen, title, passProps);
|
||||
});
|
||||
|
||||
goToDirectMessages = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'MoreDirectMessages',
|
||||
title: intl.formatMessage({id: 'mobile.more_dms.title', defaultMessage: 'New Conversation'}),
|
||||
animationType: 'slide-up',
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
navigatorButtons: {
|
||||
const screen = 'MoreDirectMessages';
|
||||
const title = intl.formatMessage({id: 'mobile.more_dms.title', defaultMessage: 'New Conversation'});
|
||||
const passProps = {};
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-dms',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
actions.showModal(screen, title, passProps, options);
|
||||
});
|
||||
|
||||
goToMoreChannels = preventDoubleTap(() => {
|
||||
const {navigator, theme} = this.props;
|
||||
const {actions} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'MoreChannels';
|
||||
const title = intl.formatMessage({id: 'more_channels.title', defaultMessage: 'More Channels'});
|
||||
const passProps = {
|
||||
closeButton: this.closeButton,
|
||||
};
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'MoreChannels',
|
||||
animationType: 'slide-up',
|
||||
title: intl.formatMessage({id: 'more_channels.title', defaultMessage: 'More Channels'}),
|
||||
backButtonTitle: '',
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
passProps: {
|
||||
closeButton: this.closeButton,
|
||||
},
|
||||
});
|
||||
actions.showModal(screen, title, passProps);
|
||||
});
|
||||
|
||||
keyExtractor = (item) => item.id || item;
|
||||
|
|
@ -349,15 +325,15 @@ export default class List extends PureComponent {
|
|||
};
|
||||
|
||||
renderItem = ({item}) => {
|
||||
const {favoriteChannelIds, unreadChannelIds} = this.props;
|
||||
const {favoriteChannelIds, unreadChannelIds, previewChannel} = this.props;
|
||||
|
||||
return (
|
||||
<ChannelItem
|
||||
channelId={item}
|
||||
isUnread={unreadChannelIds.includes(item)}
|
||||
isFavorite={favoriteChannelIds.includes(item)}
|
||||
navigator={this.props.navigator}
|
||||
onSelectChannel={this.onSelectChannel}
|
||||
previewChannel={previewChannel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ export default class ChannelSidebar extends Component {
|
|||
currentUserId: PropTypes.string.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
teamsCount: PropTypes.number.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
previewChannel: PropTypes.func,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -143,7 +143,7 @@ export default class ChannelSidebar extends Component {
|
|||
};
|
||||
|
||||
handleShowDrawerContent = () => {
|
||||
this.setState({show: true});
|
||||
requestAnimationFrame(() => this.setState({show: true}));
|
||||
};
|
||||
|
||||
closeChannelDrawer = () => {
|
||||
|
|
@ -315,9 +315,9 @@ export default class ChannelSidebar extends Component {
|
|||
|
||||
renderNavigationView = (drawerWidth) => {
|
||||
const {
|
||||
navigator,
|
||||
teamsCount,
|
||||
theme,
|
||||
previewChannel,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
|
@ -349,7 +349,6 @@ export default class ChannelSidebar extends Component {
|
|||
>
|
||||
<TeamsList
|
||||
closeChannelDrawer={this.closeChannelDrawer}
|
||||
navigator={navigator}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -363,7 +362,6 @@ export default class ChannelSidebar extends Component {
|
|||
>
|
||||
<ChannelsList
|
||||
ref={this.channelListRef}
|
||||
navigator={navigator}
|
||||
onSelectChannel={this.selectChannel}
|
||||
onJoinChannel={this.joinChannel}
|
||||
onShowTeams={this.showTeams}
|
||||
|
|
@ -371,6 +369,7 @@ export default class ChannelSidebar extends Component {
|
|||
onSearchEnds={this.onSearchEnds}
|
||||
theme={theme}
|
||||
drawerOpened={this.state.drawerOpened}
|
||||
previewChannel={previewChannel}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -380,7 +379,6 @@ export default class ChannelSidebar extends Component {
|
|||
navBarBackgroundColor={theme.sidebarBg}
|
||||
backgroundColor={theme.sidebarHeaderBg}
|
||||
footerColor={theme.sidebarHeaderBg}
|
||||
navigator={navigator}
|
||||
>
|
||||
<DrawerSwiper
|
||||
ref={this.drawerSwiperRef}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
|||
import {getCurrentTeamId, getMySortedTeamIds, getJoinableTeamIds} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {showModal} from 'app/actions/navigation';
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
import {getCurrentLocale} from 'app/selectors/i18n';
|
||||
import {removeProtocol} from 'app/utils/url';
|
||||
|
|
@ -30,6 +31,7 @@ function mapDispatchToProps(dispatch) {
|
|||
return {
|
||||
actions: bindActionCreators({
|
||||
handleTeamChange,
|
||||
showModal,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ export default class TeamsList extends PureComponent {
|
|||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
handleTeamChange: PropTypes.func.isRequired,
|
||||
showModal: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
closeChannelDrawer: PropTypes.func.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
hasOtherJoinableTeams: PropTypes.bool,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
teamIds: PropTypes.array.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
@ -76,31 +76,23 @@ export default class TeamsList extends PureComponent {
|
|||
|
||||
goToSelectTeam = preventDoubleTap(() => {
|
||||
const {intl} = this.context;
|
||||
const {currentUrl, navigator, theme} = this.props;
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'SelectTeam',
|
||||
title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}),
|
||||
animationType: 'slide-up',
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
navigatorButtons: {
|
||||
const {currentUrl, theme, actions} = this.props;
|
||||
const screen = 'SelectTeam';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'});
|
||||
const passProps = {
|
||||
currentUrl,
|
||||
theme,
|
||||
};
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-teams',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
},
|
||||
passProps: {
|
||||
currentUrl,
|
||||
theme,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
actions.showModal(screen, title, passProps, options);
|
||||
});
|
||||
|
||||
keyExtractor = (item) => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ import {logout, setStatus} from 'mattermost-redux/actions/users';
|
|||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentUser, getStatusForUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import {
|
||||
showModal,
|
||||
showModalOverCurrentContext,
|
||||
dismissModal,
|
||||
} from 'app/actions/navigation';
|
||||
|
||||
import {isLandscape, getDimensions} from 'app/selectors/device';
|
||||
|
||||
import SettingsSidebar from './settings_sidebar';
|
||||
|
|
@ -30,6 +36,9 @@ function mapDispatchToProps(dispatch) {
|
|||
actions: bindActionCreators({
|
||||
logout,
|
||||
setStatus,
|
||||
showModal,
|
||||
showModalOverCurrentContext,
|
||||
dismissModal,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,15 @@ export default class SettingsDrawer extends PureComponent {
|
|||
actions: PropTypes.shape({
|
||||
logout: PropTypes.func.isRequired,
|
||||
setStatus: PropTypes.func.isRequired,
|
||||
showModal: PropTypes.func.isRequired,
|
||||
showModalOverCurrentContext: PropTypes.func.isRequired,
|
||||
dismissModal: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
blurPostTextBox: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
status: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
|
@ -66,10 +68,12 @@ export default class SettingsDrawer extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventEmitter.on('close_settings_sidebar', this.closeSettingsSidebar);
|
||||
BackHandler.addEventListener('hardwareBackPress', this.handleAndroidBack);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventEmitter.off('close_settings_sidebar', this.closeSettingsSidebar);
|
||||
BackHandler.removeEventListener('hardwareBackPress', this.handleAndroidBack);
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +111,7 @@ export default class SettingsDrawer extends PureComponent {
|
|||
};
|
||||
|
||||
handleSetStatus = preventDoubleTap(() => {
|
||||
const {actions} = this.props;
|
||||
const items = [{
|
||||
action: () => this.setStatus(General.ONLINE),
|
||||
text: {
|
||||
|
|
@ -133,21 +138,7 @@ export default class SettingsDrawer extends PureComponent {
|
|||
},
|
||||
}];
|
||||
|
||||
this.props.navigator.showModal({
|
||||
screen: 'OptionsModal',
|
||||
title: '',
|
||||
animationType: 'none',
|
||||
passProps: {
|
||||
items,
|
||||
},
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
modalPresentationStyle: 'overCurrentContext',
|
||||
},
|
||||
});
|
||||
actions.showModalOverCurrentContext('OptionsModal', {items});
|
||||
});
|
||||
|
||||
goToEditProfile = preventDoubleTap(() => {
|
||||
|
|
@ -207,31 +198,20 @@ export default class SettingsDrawer extends PureComponent {
|
|||
});
|
||||
|
||||
openModal = (screen, title, passProps) => {
|
||||
const {navigator, theme} = this.props;
|
||||
|
||||
this.closeSettingsSidebar();
|
||||
|
||||
const {actions} = this.props;
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-settings',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
navigator.showModal({
|
||||
screen,
|
||||
title,
|
||||
animationType: 'slide-up',
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
navigatorButtons: {
|
||||
leftButtons: [{
|
||||
id: 'close-settings',
|
||||
icon: this.closeButton,
|
||||
}],
|
||||
},
|
||||
passProps,
|
||||
});
|
||||
actions.showModal(screen, title, passProps, options);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -251,7 +231,7 @@ export default class SettingsDrawer extends PureComponent {
|
|||
};
|
||||
|
||||
renderNavigationView = () => {
|
||||
const {currentUser, navigator, theme} = this.props;
|
||||
const {currentUser, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
|
|
@ -261,7 +241,6 @@ export default class SettingsDrawer extends PureComponent {
|
|||
footerColor={theme.centerChannelBg}
|
||||
footerComponent={<View style={style.container}/>}
|
||||
headerComponent={<View style={style.container}/>}
|
||||
navigator={navigator}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={style.container}>
|
||||
|
|
@ -356,12 +335,10 @@ export default class SettingsDrawer extends PureComponent {
|
|||
};
|
||||
|
||||
setStatus = (status) => {
|
||||
const {status: currentUserStatus, navigator} = this.props;
|
||||
const {status: currentUserStatus, actions} = this.props;
|
||||
|
||||
if (currentUserStatus === General.OUT_OF_OFFICE) {
|
||||
navigator.dismissModal({
|
||||
animationType: 'none',
|
||||
});
|
||||
actions.dismissModal();
|
||||
this.closeSettingsSidebar();
|
||||
this.confirmReset(status);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const NavigationTypes = keyMirror({
|
|||
NAVIGATION_NO_TEAMS: null,
|
||||
RESTART_APP: null,
|
||||
NAVIGATION_ERROR_TEAMS: null,
|
||||
NAVIGATION_SHOW_OVERLAY: null,
|
||||
});
|
||||
|
||||
export default NavigationTypes;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ const ViewTypes = keyMirror({
|
|||
COMMENT_DRAFT_SELECTION_CHANGED: null,
|
||||
|
||||
NOTIFICATION_IN_APP: null,
|
||||
NOTIFICATION_TAPPED: null,
|
||||
|
||||
SET_POST_DRAFT: null,
|
||||
SET_COMMENT_DRAFT: null,
|
||||
|
|
@ -87,9 +86,6 @@ const ViewTypes = keyMirror({
|
|||
|
||||
INCREMENT_EMOJI_PICKER_PAGE: null,
|
||||
|
||||
LAUNCH_LOGIN: null,
|
||||
LAUNCH_CHANNEL: null,
|
||||
|
||||
SET_DEEP_LINK_URL: null,
|
||||
|
||||
SET_PROFILE_IMAGE_URI: null,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import * as KeyChain from 'react-native-keychain';
|
|||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import ephemeralStore from 'app/store/ephemeral_store';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import {setCSRFFromCookie} from 'app/utils/security';
|
||||
|
||||
const CURRENT_SERVER = '@currentServerUrl';
|
||||
|
|
@ -26,7 +26,7 @@ export const setAppCredentials = (deviceToken, currentUserId, token, url) => {
|
|||
try {
|
||||
const username = `${deviceToken}, ${currentUserId}`;
|
||||
|
||||
ephemeralStore.deviceToken = deviceToken;
|
||||
EphemeralStore.deviceToken = deviceToken;
|
||||
AsyncStorage.setItem(CURRENT_SERVER, url);
|
||||
KeyChain.setInternetCredentials(url, username, token, {accessGroup: mattermostManaged.appGroupIdentifier});
|
||||
} catch (e) {
|
||||
|
|
@ -111,7 +111,7 @@ async function getInternetCredentials(url) {
|
|||
const [deviceToken, currentUserId] = usernameParsed;
|
||||
|
||||
if (token && token !== 'undefined') {
|
||||
ephemeralStore.deviceToken = deviceToken;
|
||||
EphemeralStore.deviceToken = deviceToken;
|
||||
Client4.setUserId(currentUserId);
|
||||
Client4.setUrl(url);
|
||||
Client4.setToken(token);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue