Switch push notification lib (#346)
<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR --> @RocketChat/ReactNative <!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below --> Closes #342 <!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
|
@ -124,8 +124,7 @@ module.exports = {
|
|||
"prefer-const": 2,
|
||||
"object-shorthand": 2,
|
||||
"consistent-return": 0,
|
||||
"global-require": "off",
|
||||
"react/prop-types": [0, { skipUndeclared: true }]
|
||||
"global-require": "off"
|
||||
},
|
||||
"globals": {
|
||||
"__DEV__": true
|
||||
|
|
22
.snyk
|
@ -1,5 +1,5 @@
|
|||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||
version: v1.7.1
|
||||
version: v1.12.0
|
||||
ignore: {}
|
||||
# patches apply the minimum changes required to fix a vulnerability
|
||||
patch:
|
||||
|
@ -24,3 +24,23 @@ patch:
|
|||
patched: '2017-09-29T23:29:20.238Z'
|
||||
- realm > extract-zip > debug:
|
||||
patched: '2017-09-29T23:29:20.238Z'
|
||||
'npm:lodash:20180130':
|
||||
- react-native > plist > xmlbuilder > lodash:
|
||||
patched: '2018-07-02T23:20:39.933Z'
|
||||
'npm:hoek:20180212':
|
||||
- realm > node-pre-gyp > hawk > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > hawk > boom > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > hawk > sntp > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > hawk > cryptiles > boom > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > request > hawk > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > request > hawk > boom > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > request > hawk > sntp > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
- realm > node-pre-gyp > request > hawk > cryptiles > boom > hoek:
|
||||
patched: '2018-06-22T03:39:40.096Z'
|
||||
|
|
|
@ -618,7 +618,7 @@ exports[`render unread +999 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -868,7 +868,7 @@ exports[`render unread 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1118,7 +1118,7 @@ exports[`renders correctly 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -426,7 +426,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -648,7 +648,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -874,7 +874,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1119,7 +1119,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1368,7 +1368,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1613,7 +1613,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1858,7 +1858,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2103,7 +2103,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2348,7 +2348,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2570,7 +2570,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2792,7 +2792,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
|||
undefined,
|
||||
],
|
||||
Object {
|
||||
"backgroundColor": "#cbced1",
|
||||
"backgroundColor": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -95,13 +95,19 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId "chat.rocket.reactnative"
|
||||
minSdkVersion 16
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 27
|
||||
versionCode VERSIONCODE as Integer
|
||||
versionName "1"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
missingDimensionStrategy "RNN.reactNativeVersion", "reactNative55"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
@ -172,29 +178,30 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-i18n')
|
||||
compile project(':react-native-fabric')
|
||||
compile project(':react-native-audio')
|
||||
compile project(":reactnativekeyboardinput")
|
||||
compile project(':react-native-splash-screen')
|
||||
compile project(':react-native-video')
|
||||
compile project(':react-native-push-notification')
|
||||
compile project(':react-native-svg')
|
||||
compile project(':react-native-image-picker')
|
||||
compile project(':react-native-vector-icons')
|
||||
compile project(':react-native-fetch-blob')
|
||||
compile project(':react-native-zeroconf')
|
||||
compile project(':react-native-toast')
|
||||
compile project(':react-native-fast-image')
|
||||
compile project(':realm')
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:27.1.0"
|
||||
compile "com.android.support:support-v4:27.1.0"
|
||||
compile 'com.android.support:customtabs:27.1.0'
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
compile 'com.facebook.fresco:fresco:1.7.1'
|
||||
compile 'com.facebook.fresco:animated-gif:1.7.1'
|
||||
compile('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
|
||||
implementation project(':react-native-i18n')
|
||||
implementation project(':react-native-fabric')
|
||||
implementation project(':react-native-audio')
|
||||
implementation project(":reactnativekeyboardinput")
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-image-picker')
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-fetch-blob')
|
||||
implementation project(':react-native-zeroconf')
|
||||
implementation project(':react-native-toast')
|
||||
implementation project(':react-native-fast-image')
|
||||
implementation project(':realm')
|
||||
implementation project(':react-native-navigation')
|
||||
implementation project(':reactnativenotifications')
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.android.support:appcompat-v7:27.1.0"
|
||||
implementation "com.android.support:support-v4:27.1.0"
|
||||
implementation 'com.android.support:customtabs:27.1.0'
|
||||
implementation 'com.android.support:design:27.1.0'
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
implementation 'com.facebook.fresco:fresco:1.7.1'
|
||||
implementation 'com.facebook.fresco:animated-gif:1.7.1'
|
||||
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
|
||||
transitive = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission-sdk-23 android:name="android.permission.VIBRATE"/>
|
||||
|
||||
<permission
|
||||
android:name="${applicationId}.permission.C2D_MESSAGE"
|
||||
|
@ -50,27 +50,7 @@
|
|||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
|
||||
|
||||
<receiver
|
||||
android:name="com.google.android.gms.gcm.GcmReceiver"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.c2dm.permission.SEND" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
<category android:name="${applicationId}" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
|
||||
<service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>
|
||||
<service
|
||||
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
|
||||
android:exported="false" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<meta-data android:name="com.wix.reactnativenotifications.gcmSenderId" android:value="673693445664\0"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package chat.rocket.reactnative;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings.System;
|
||||
import android.media.RingtoneManager;
|
||||
|
||||
import com.wix.reactnativenotifications.core.AppLaunchHelper;
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
|
||||
import com.wix.reactnativenotifications.core.JsIOHelper;
|
||||
import com.wix.reactnativenotifications.core.notification.PushNotification;
|
||||
|
||||
public class CustomPushNotification extends PushNotification {
|
||||
public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
|
||||
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
|
||||
final Resources res = mContext.getResources();
|
||||
String packageName = mContext.getPackageName();
|
||||
|
||||
Bundle bundle = mNotificationProps.asBundle();
|
||||
int smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
|
||||
int largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
|
||||
String title = bundle.getString("title");
|
||||
String message = bundle.getString("message");
|
||||
|
||||
final Notification.Builder notification = new Notification.Builder(mContext);
|
||||
notification
|
||||
.setSmallIcon(smallIconResId)
|
||||
.setContentIntent(intent)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setStyle(new Notification.BigTextStyle().bigText(message))
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setColor(mContext.getColor(R.color.notification_text))
|
||||
.setDefaults(Notification.DEFAULT_ALL)
|
||||
.setAutoCancel(true);
|
||||
|
||||
Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
|
||||
notification.setLargeIcon(largeIconBitmap);
|
||||
|
||||
return notification;
|
||||
}
|
||||
}
|
|
@ -1,26 +1,17 @@
|
|||
package chat.rocket.reactnative;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.facebook.react.ReactActivity;
|
||||
import org.devio.rn.splashscreen.SplashScreen;
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.widget.LinearLayout;
|
||||
import com.reactnativenavigation.controllers.SplashActivity;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
public class MainActivity extends SplashActivity {
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "RocketChatRN";
|
||||
}
|
||||
public LinearLayout createSplashLayout() {
|
||||
LinearLayout splash = new LinearLayout(this);
|
||||
Drawable launch_screen_bitmap = ContextCompat.getDrawable(getApplicationContext(),R.drawable.launch_screen_bitmap);
|
||||
splash.setBackground(launch_screen_bitmap);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SplashScreen.show(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
Fabric.with(this, new Crashlytics());
|
||||
return splash;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,60 @@
|
|||
package chat.rocket.reactnative;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.AlexanderZaytsev.RNI18n.RNI18nPackage;
|
||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||
import com.balthazargronon.RCTZeroconf.ZeroconfReactPackage;
|
||||
import com.brentvatne.react.ReactVideoPackage;
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
import com.dylanvann.fastimage.FastImageViewPackage;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.horcrux.svg.SvgPackage;
|
||||
import com.imagepicker.ImagePickerPackage;
|
||||
import com.oblador.vectoricons.VectorIconsPackage;
|
||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||
import com.balthazargronon.RCTZeroconf.ZeroconfReactPackage;
|
||||
import io.realm.react.RealmReactPackage;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
|
||||
import com.brentvatne.react.ReactVideoPackage;
|
||||
import com.reactnativenavigation.NavigationApplication;
|
||||
import com.remobile.toast.RCTToastPackage;
|
||||
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
|
||||
import com.rnim.rn.audio.ReactNativeAudioPackage;
|
||||
import com.smixx.fabric.FabricPackage;
|
||||
import com.dylanvann.fastimage.FastImageViewPackage;
|
||||
import com.AlexanderZaytsev.RNI18n.RNI18nPackage;
|
||||
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
|
||||
import com.wix.reactnativenotifications.RNNotificationsPackage;
|
||||
import com.wix.reactnativenotifications.core.AppLaunchHelper;
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
|
||||
import com.wix.reactnativenotifications.core.JsIOHelper;
|
||||
import com.wix.reactnativenotifications.core.notification.INotificationsApplication;
|
||||
import com.wix.reactnativenotifications.core.notification.IPushNotification;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.devio.rn.splashscreen.SplashScreenReactPackage;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
import io.realm.react.RealmReactPackage;
|
||||
|
||||
public class MainApplication extends NavigationApplication implements INotificationsApplication {
|
||||
|
||||
private NotificationsLifecycleFacade notificationsLifecycleFacade;
|
||||
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
public boolean isDebug() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
protected List<ReactPackage> getPackages() {
|
||||
// Add additional packages you require here
|
||||
// No need to add RnnPackage and MainReactPackage
|
||||
return Arrays.<ReactPackage>asList(
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReactPackage> createAdditionalReactPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(),
|
||||
new SvgPackage(),
|
||||
|
@ -44,28 +63,37 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
new RNFetchBlobPackage(),
|
||||
new ZeroconfReactPackage(),
|
||||
new RealmReactPackage(),
|
||||
new ReactNativePushNotificationPackage(),
|
||||
new ReactVideoPackage(),
|
||||
new SplashScreenReactPackage(),
|
||||
new RCTToastPackage(),
|
||||
new ReactNativeAudioPackage(),
|
||||
new KeyboardInputPackage(MainApplication.this),
|
||||
new RocketChatNativePackage(),
|
||||
new FabricPackage(),
|
||||
new FastImageViewPackage(),
|
||||
new RNI18nPackage()
|
||||
new RNI18nPackage(),
|
||||
new RNNotificationsPackage(MainApplication.this)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
Fabric.with(this, new Crashlytics());
|
||||
|
||||
// Create an object of the custom facade impl
|
||||
notificationsLifecycleFacade = new NotificationsLifecycleFacade();
|
||||
// Attach it to react-native-navigation
|
||||
setActivityCallbacks(notificationsLifecycleFacade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade defaultFacade, AppLaunchHelper defaultAppLaunchHelper) {
|
||||
return new CustomPushNotification(
|
||||
context,
|
||||
bundle,
|
||||
notificationsLifecycleFacade, // Instead of defaultFacade!!!
|
||||
defaultAppLaunchHelper,
|
||||
new JsIOHelper()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package chat.rocket.reactnative;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
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 Activity mVisibleActivity;
|
||||
private Set<AppVisibilityListener> mListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
switchToVisible(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
switchToInvisible(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
switchToInvisible(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);
|
||||
}
|
||||
|
||||
private synchronized void switchToVisible(Activity activity) {
|
||||
if (mVisibleActivity == null) {
|
||||
mVisibleActivity = activity;
|
||||
Log.d(TAG, "Activity is now visible ("+activity+")");
|
||||
for (AppVisibilityListener listener : mListeners) {
|
||||
listener.onAppVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void switchToInvisible(Activity activity) {
|
||||
if (mVisibleActivity == activity) {
|
||||
mVisibleActivity = null;
|
||||
Log.d(TAG, "Activity is now NOT visible ("+activity+")");
|
||||
for (AppVisibilityListener listener : mListeners) {
|
||||
listener.onAppNotVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 134 B |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 100 B |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 207 B |
|
@ -0,0 +1,8 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
|
||||
<item android:drawable="@color/splashBackground"/>
|
||||
<item>
|
||||
<bitmap
|
||||
android:src="@drawable/launch_screen"
|
||||
android:gravity="center"/>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/launch_screen">
|
||||
</LinearLayout>
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 766 B |
After Width: | Height: | Size: 766 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
|
@ -1 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="primary_dark">#660B0B0B</color> </resources>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="primary_dark">#660B0B0B</color>
|
||||
<item name="splashBackground" type="color">#FFFFFF</item>
|
||||
<item name="notification_text" type="color">#CC3333</item>
|
||||
</resources>
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
// classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
|
@ -17,6 +19,7 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
google()
|
||||
maven {
|
||||
|
|
|
@ -9,8 +9,6 @@ include ':react-native-audio'
|
|||
project(':react-native-audio').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-audio/android')
|
||||
include ':reactnativekeyboardinput'
|
||||
project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android')
|
||||
include ':react-native-splash-screen'
|
||||
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
||||
include ':react-native-svg'
|
||||
|
@ -25,8 +23,10 @@ include ':react-native-zeroconf'
|
|||
project(':react-native-zeroconf').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-zeroconf/android')
|
||||
include ':realm'
|
||||
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
|
||||
include ':react-native-push-notification'
|
||||
project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android')
|
||||
include ':react-native-toast'
|
||||
project(':react-native-toast').projectDir = new File(settingsDir, '../node_modules/@remobile/react-native-toast/android')
|
||||
include ':react-native-navigation'
|
||||
project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app/')
|
||||
include ':reactnativenotifications'
|
||||
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android')
|
||||
include ':app'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { Platform } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
const isIOS = Platform.OS === 'ios';
|
||||
const prefix = isIOS ? 'ios' : 'md';
|
||||
|
||||
// icon name from provider: [ size of the uri, icon provider, name to be used later ]
|
||||
const icons = {
|
||||
[`${ prefix }-search`]: [30, Ionicons, 'search'],
|
||||
[`${ prefix }-menu`]: [30, Ionicons, 'menu'],
|
||||
[`${ prefix }-star`]: [30, Ionicons, 'star'],
|
||||
[`${ prefix }-star-outline`]: [30, Ionicons, 'starOutline'],
|
||||
[isIOS ? 'ios-create-outline' : 'md-create']: [30, Ionicons, 'create'],
|
||||
[`${ prefix }-more`]: [30, Ionicons, 'more'],
|
||||
[`${ prefix }-add`]: [30, Ionicons, 'add']
|
||||
};
|
||||
|
||||
const iconsMap = {};
|
||||
const iconsLoaded = async() => {
|
||||
const promises = Object.keys(icons).map((icon) => {
|
||||
const Provider = icons[icon][1];
|
||||
return Provider.getImageSource(icon, icons[icon][0]);
|
||||
});
|
||||
const sources = await Promise.all(promises);
|
||||
Object.keys(icons).forEach((icon, i) => (iconsMap[icons[icon][2]] = sources[i]));
|
||||
};
|
||||
|
||||
export { iconsLoaded, iconsMap };
|
|
@ -0,0 +1,13 @@
|
|||
class NavigationActionsClass {
|
||||
setNavigator(navigator) {
|
||||
this.navigator = navigator;
|
||||
}
|
||||
|
||||
push = params => this.navigator && this.navigator.push(params);
|
||||
pop = params => this.navigator && this.navigator.pop(params);
|
||||
popToRoot = params => this.navigator && this.navigator.popToRoot(params);
|
||||
resetTo = params => this.navigator && this.navigator.resetTo(params);
|
||||
toggleDrawer = params => this.navigator && this.navigator.toggleDrawer(params);
|
||||
}
|
||||
|
||||
export const NavigationActions = new NavigationActionsClass();
|
|
@ -1,11 +1,14 @@
|
|||
/* eslint-disable */
|
||||
import { NativeModules } from 'react-native';
|
||||
import Reactotron from 'reactotron-react-native';
|
||||
import { reactotronRedux } from 'reactotron-redux';
|
||||
import sagaPlugin from 'reactotron-redux-saga'
|
||||
|
||||
if (__DEV__) {
|
||||
const scriptURL = NativeModules.SourceCode.scriptURL;
|
||||
const scriptHostname = scriptURL.split('://')[1].split(':')[0];
|
||||
Reactotron
|
||||
.configure()
|
||||
.configure({ host: scriptHostname })
|
||||
.useReactNative()
|
||||
.use(reactotronRedux())
|
||||
.use(sagaPlugin())
|
||||
|
|
|
@ -44,7 +44,7 @@ export const ROOM = createRequestTypes('ROOM', [
|
|||
'MESSAGE_RECEIVED',
|
||||
'SET_LAST_OPEN'
|
||||
]);
|
||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
||||
export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT']);
|
||||
export const MESSAGES = createRequestTypes('MESSAGES', [
|
||||
...defaultTypes,
|
||||
'ACTIONS_SHOW',
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import * as types from '../constants/types';
|
||||
import { APP } from './actionsTypes';
|
||||
|
||||
export function appStart(root) {
|
||||
return {
|
||||
type: APP.START,
|
||||
root
|
||||
};
|
||||
}
|
||||
|
||||
export function appReady() {
|
||||
return {
|
||||
type: APP.READY
|
||||
|
@ -12,6 +19,7 @@ export function appInit() {
|
|||
type: APP.INIT
|
||||
};
|
||||
}
|
||||
|
||||
export function setCurrentServer(server) {
|
||||
return {
|
||||
type: types.SET_CURRENT_SERVER,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { SERVER } from './actionsTypes';
|
||||
|
||||
export function setServer(server) {
|
||||
export function selectServer(server) {
|
||||
return {
|
||||
type: SERVER.SELECT,
|
||||
server
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { NavigationActions } from 'react-navigation';
|
||||
import { COLOR_TEXT } from '../constants/colors';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
width: 25,
|
||||
height: 25,
|
||||
marginTop: 5
|
||||
},
|
||||
icon: {
|
||||
color: COLOR_TEXT,
|
||||
left: -5
|
||||
}
|
||||
});
|
||||
|
||||
export default class CloseModalButton extends React.PureComponent {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.props.navigation.dispatch(NavigationActions.back())}
|
||||
style={styles.button}
|
||||
testID='close-modal-button'
|
||||
>
|
||||
<Icon
|
||||
style={styles.icon}
|
||||
name='close'
|
||||
size={25}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet, Platform } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SafeAreaView } from 'react-navigation';
|
||||
|
||||
let platformContainerStyles;
|
||||
if (Platform.OS === 'ios') {
|
||||
platformContainerStyles = {
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: 'rgba(0, 0, 0, .3)'
|
||||
};
|
||||
} else {
|
||||
platformContainerStyles = {
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: StyleSheet.hairlineWidth,
|
||||
shadowOffset: {
|
||||
height: StyleSheet.hairlineWidth
|
||||
},
|
||||
elevation: 4
|
||||
};
|
||||
}
|
||||
|
||||
const height = Platform.OS === 'ios' ? 44 : 56;
|
||||
const backgroundColor = Platform.OS === 'ios' ? '#F7F7F7' : '#FFF';
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor,
|
||||
...platformContainerStyles
|
||||
},
|
||||
appBar: {
|
||||
height,
|
||||
backgroundColor
|
||||
}
|
||||
});
|
||||
|
||||
export default class Header extends React.PureComponent {
|
||||
static propTypes = {
|
||||
subview: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView forceInset={{ bottom: 'never' }} style={styles.container}>
|
||||
<View style={styles.appBar}>
|
||||
{this.props.subview}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ import I18n from '../i18n';
|
|||
@connect(
|
||||
state => ({
|
||||
actionMessage: state.messages.actionMessage,
|
||||
user: state.login.user,
|
||||
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
||||
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
||||
Message_AllowEditing: state.settings.Message_AllowEditing,
|
||||
|
@ -42,9 +41,9 @@ import I18n from '../i18n';
|
|||
export default class MessageActions extends React.Component {
|
||||
static propTypes = {
|
||||
actionsHide: PropTypes.func.isRequired,
|
||||
room: PropTypes.object,
|
||||
room: PropTypes.object.isRequired,
|
||||
actionMessage: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
user: PropTypes.object.isRequired,
|
||||
deleteRequest: PropTypes.func.isRequired,
|
||||
editInit: PropTypes.func.isRequired,
|
||||
toggleStarRequest: PropTypes.func.isRequired,
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Linking } from 'react-native';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import SplashScreen from 'react-native-splash-screen';
|
||||
|
||||
import { appInit } from '../actions';
|
||||
import { deepLinkingOpen } from '../actions/deepLinking';
|
||||
import AuthRoutes from './routes/AuthRoutes';
|
||||
import PublicRoutes from './routes/PublicRoutes';
|
||||
import * as NavigationService from './routes/NavigationService';
|
||||
import parseQuery from '../lib/methods/helpers/parseQuery';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
login: state.login,
|
||||
app: state.app,
|
||||
background: state.app.background
|
||||
}),
|
||||
dispatch => bindActionCreators({
|
||||
appInit, deepLinkingOpen
|
||||
}, dispatch)
|
||||
)
|
||||
export default class Routes extends React.Component {
|
||||
static propTypes = {
|
||||
login: PropTypes.object.isRequired,
|
||||
app: PropTypes.object.isRequired,
|
||||
appInit: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleOpenURL = this.handleOpenURL.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.app.ready) {
|
||||
return SplashScreen.hide();
|
||||
}
|
||||
this.props.appInit();
|
||||
|
||||
Linking
|
||||
.getInitialURL()
|
||||
.then(url => this.handleOpenURL({ url }))
|
||||
.catch(console.error);
|
||||
Linking.addEventListener('url', this.handleOpenURL);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.app.ready && this.props.app.ready !== nextProps.app.ready) {
|
||||
SplashScreen.hide();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
NavigationService.setNavigator(this.navigator);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Linking.removeEventListener('url', this.handleOpenURL);
|
||||
}
|
||||
|
||||
handleOpenURL({ url }) {
|
||||
if (url) {
|
||||
url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
|
||||
const regex = /^(room|auth)\?/;
|
||||
if (url.match(regex)) {
|
||||
url = url.replace(regex, '');
|
||||
const params = parseQuery(url);
|
||||
this.props.deepLinkingOpen(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { login } = this.props;
|
||||
|
||||
if (this.props.app.starting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!login.token || login.isRegistering) {
|
||||
return (<PublicRoutes ref={nav => this.navigator = nav} />);
|
||||
}
|
||||
return (<AuthRoutes ref={nav => this.navigator = nav} />);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation } from 'react-native';
|
||||
import { ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation, AsyncStorage, SafeAreaView } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { DrawerActions, SafeAreaView } from 'react-navigation';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import database from '../lib/realm';
|
||||
import { setServer } from '../actions/server';
|
||||
import { selectServer } from '../actions/server';
|
||||
import { logout } from '../actions/login';
|
||||
import { appStart } from '../actions';
|
||||
import Avatar from '../containers/Avatar';
|
||||
import Status from '../containers/status';
|
||||
import Touch from '../utils/touch';
|
||||
|
@ -16,6 +16,7 @@ import { STATUS_COLORS } from '../constants/colors';
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import log from '../utils/log';
|
||||
import I18n from '../i18n';
|
||||
import { NavigationActions } from '../Navigation';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
selected: {
|
||||
|
@ -76,19 +77,29 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
const keyExtractor = item => item.id;
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server.server,
|
||||
user: state.login.user
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
language: state.login.user && state.login.user.language,
|
||||
server: state.login.user && state.login.user.server,
|
||||
status: state.login.user && state.login.user.status,
|
||||
username: state.login.user && state.login.user.username
|
||||
}
|
||||
}), dispatch => ({
|
||||
selectServer: server => dispatch(setServer(server)),
|
||||
logout: () => dispatch(logout())
|
||||
selectServer: server => dispatch(selectServer(server)),
|
||||
logout: () => dispatch(logout()),
|
||||
appStart: () => dispatch(appStart('outside'))
|
||||
}))
|
||||
export default class Sidebar extends Component {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
server: PropTypes.string.isRequired,
|
||||
selectServer: PropTypes.func.isRequired,
|
||||
navigation: PropTypes.object.isRequired,
|
||||
logout: PropTypes.func.isRequired
|
||||
user: PropTypes.object,
|
||||
logout: PropTypes.func.isRequired,
|
||||
appStart: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -117,7 +128,6 @@ export default class Sidebar extends Component {
|
|||
|
||||
onPressItem = (item) => {
|
||||
this.props.selectServer(item.id);
|
||||
this.closeDrawer();
|
||||
}
|
||||
|
||||
setStatus = () => {
|
||||
|
@ -149,7 +159,11 @@ export default class Sidebar extends Component {
|
|||
}
|
||||
|
||||
closeDrawer = () => {
|
||||
this.props.navigation.dispatch(DrawerActions.closeDrawer());
|
||||
this.props.navigator.toggleDrawer({
|
||||
side: 'left',
|
||||
animated: true,
|
||||
to: 'close'
|
||||
});
|
||||
}
|
||||
|
||||
toggleServers = () => {
|
||||
|
@ -157,25 +171,15 @@ export default class Sidebar extends Component {
|
|||
this.setState({ showServers: !this.state.showServers });
|
||||
}
|
||||
|
||||
isRouteFocused = (route) => {
|
||||
const { state } = this.props.navigation;
|
||||
const activeItemKey = state.routes[state.index] ? state.routes[state.index].key : null;
|
||||
return activeItemKey === route;
|
||||
}
|
||||
|
||||
sidebarNavigate = (route) => {
|
||||
const { navigate } = this.props.navigation;
|
||||
if (!this.isRouteFocused(route)) {
|
||||
navigate(route);
|
||||
} else {
|
||||
sidebarNavigate = (screen, title) => {
|
||||
this.closeDrawer();
|
||||
}
|
||||
NavigationActions.resetTo({ screen, title });
|
||||
}
|
||||
|
||||
renderSeparator = key => <View key={key} style={styles.separator} />;
|
||||
|
||||
renderItem = ({
|
||||
text, left, selected, onPress, testID
|
||||
text, left, onPress, testID
|
||||
}) => (
|
||||
<Touch
|
||||
key={text}
|
||||
|
@ -184,8 +188,8 @@ export default class Sidebar extends Component {
|
|||
activeOpacity={0.3}
|
||||
testID={testID}
|
||||
>
|
||||
<View style={[styles.item, selected && styles.selected]}>
|
||||
<View style={[styles.itemLeft, !selected && styles.itemLeftOpacity]}>
|
||||
<View style={styles.item}>
|
||||
<View style={styles.itemLeft}>
|
||||
{left}
|
||||
</View>
|
||||
<Text style={styles.itemText}>
|
||||
|
@ -222,12 +226,15 @@ export default class Sidebar extends Component {
|
|||
source={{ uri: encodeURI(`${ item.id }/assets/favicon_32.png`) }}
|
||||
/>,
|
||||
selected: this.props.server === item.id,
|
||||
onPress: () => {
|
||||
onPress: async() => {
|
||||
this.closeDrawer();
|
||||
this.toggleServers();
|
||||
if (this.props.server !== item.id) {
|
||||
this.props.selectServer(item.id);
|
||||
this.props.navigation.navigate('RoomsList');
|
||||
const token = await AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ item.id }`);
|
||||
if (!token) {
|
||||
this.props.appStart();
|
||||
}
|
||||
}
|
||||
},
|
||||
testID: `sidebar-${ item.id }`
|
||||
|
@ -239,31 +246,25 @@ export default class Sidebar extends Component {
|
|||
this.renderItem({
|
||||
text: I18n.t('Chats'),
|
||||
left: <Icon name='chat-bubble' size={20} />,
|
||||
onPress: () => this.sidebarNavigate('Chats'),
|
||||
selected: this.isRouteFocused('Chats'),
|
||||
onPress: () => this.sidebarNavigate('RoomsListView', I18n.t('Messages')),
|
||||
testID: 'sidebar-chats'
|
||||
}),
|
||||
this.renderItem({
|
||||
text: I18n.t('Profile'),
|
||||
left: <Icon name='person' size={20} />,
|
||||
onPress: () => this.sidebarNavigate('ProfileView'),
|
||||
selected: this.isRouteFocused('ProfileView'),
|
||||
onPress: () => this.sidebarNavigate('ProfileView', I18n.t('Profile')),
|
||||
testID: 'sidebar-profile'
|
||||
}),
|
||||
this.renderItem({
|
||||
text: I18n.t('Settings'),
|
||||
left: <Icon name='settings' size={20} />,
|
||||
onPress: () => this.sidebarNavigate('SettingsView'),
|
||||
selected: this.isRouteFocused('SettingsView'),
|
||||
onPress: () => this.sidebarNavigate('SettingsView', I18n.t('Settings')),
|
||||
testID: 'sidebar-settings'
|
||||
}),
|
||||
this.renderSeparator('separator-logout'),
|
||||
this.renderItem({
|
||||
text: I18n.t('Logout'),
|
||||
left: <Icon
|
||||
name='exit-to-app'
|
||||
size={20}
|
||||
/>,
|
||||
left: <Icon name='exit-to-app' size={20} />,
|
||||
onPress: () => this.props.logout(),
|
||||
testID: 'sidebar-logout'
|
||||
})
|
||||
|
@ -297,7 +298,10 @@ export default class Sidebar extends Component {
|
|||
onPress: () => {
|
||||
this.closeDrawer();
|
||||
this.toggleServers();
|
||||
this.props.navigation.navigate('AddServer');
|
||||
NavigationActions.push({
|
||||
screen: 'NewServerView',
|
||||
title: I18n.t('Add_Server')
|
||||
});
|
||||
},
|
||||
testID: 'sidebar-add-server'
|
||||
})
|
||||
|
@ -306,13 +310,12 @@ export default class Sidebar extends Component {
|
|||
|
||||
render() {
|
||||
const { user, server } = this.props;
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ScrollView>
|
||||
<SafeAreaView
|
||||
style={styles.container}
|
||||
forceInset={{ top: 'always', horizontal: 'never' }}
|
||||
testID='sidebar'
|
||||
>
|
||||
<ScrollView style={{ backgroundColor: '#fff' }}>
|
||||
<SafeAreaView testID='sidebar'>
|
||||
<Touch
|
||||
onPress={() => this.toggleServers()}
|
||||
underlayColor='rgba(255, 255, 255, 0.5)'
|
||||
|
@ -335,6 +338,7 @@ export default class Sidebar extends Component {
|
|||
<Icon
|
||||
name={this.state.showServers ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
|
||||
size={30}
|
||||
style={{ paddingHorizontal: 10 }}
|
||||
/>
|
||||
</View>
|
||||
</Touch>
|
||||
|
|
|
@ -66,7 +66,10 @@ export default class RCTextInput extends React.PureComponent {
|
|||
secureTextEntry: PropTypes.bool,
|
||||
containerStyle: ViewPropTypes.style,
|
||||
inputStyle: PropTypes.object,
|
||||
inputRef: PropTypes.func
|
||||
inputRef: PropTypes.func,
|
||||
testID: PropTypes.string,
|
||||
iconLeft: PropTypes.string,
|
||||
placeholder: PropTypes.string
|
||||
}
|
||||
static defaultProps = {
|
||||
error: {}
|
||||
|
|
|
@ -28,7 +28,8 @@ export default class extends React.PureComponent {
|
|||
static propTypes = {
|
||||
file: PropTypes.object.isRequired,
|
||||
baseUrl: PropTypes.string.isRequired,
|
||||
user: PropTypes.object.isRequired
|
||||
user: PropTypes.object.isRequired,
|
||||
customEmojis: PropTypes.object
|
||||
}
|
||||
|
||||
state = { modalVisible: false };
|
||||
|
|
|
@ -99,7 +99,11 @@ export default class Message extends React.Component {
|
|||
Message_GroupingPeriod: PropTypes.number.isRequired,
|
||||
customTimeFormat: PropTypes.string,
|
||||
message: PropTypes.object.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
token: PropTypes.string.isRequired
|
||||
}),
|
||||
editing: PropTypes.bool,
|
||||
errorActionsShow: PropTypes.func,
|
||||
toggleReactionPicker: PropTypes.func,
|
||||
|
@ -109,7 +113,8 @@ export default class Message extends React.Component {
|
|||
onLongPress: PropTypes.func,
|
||||
_updatedAt: PropTypes.instanceOf(Date),
|
||||
archived: PropTypes.bool,
|
||||
broadcast: PropTypes.bool
|
||||
broadcast: PropTypes.bool,
|
||||
previousItem: PropTypes.object
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Platform, TouchableOpacity } from 'react-native';
|
||||
import { createStackNavigator, createDrawerNavigator } from 'react-navigation';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import Sidebar from '../../containers/Sidebar';
|
||||
import RoomsListView from '../../views/RoomsListView';
|
||||
import RoomView from '../../views/RoomView';
|
||||
import RoomActionsView from '../../views/RoomActionsView';
|
||||
import CreateChannelView from '../../views/CreateChannelView';
|
||||
import SelectedUsersView from '../../views/SelectedUsersView';
|
||||
import NewServerView from '../../views/NewServerView';
|
||||
import StarredMessagesView from '../../views/StarredMessagesView';
|
||||
import PinnedMessagesView from '../../views/PinnedMessagesView';
|
||||
import MentionedMessagesView from '../../views/MentionedMessagesView';
|
||||
import SnippetedMessagesView from '../../views/SnippetedMessagesView';
|
||||
import SearchMessagesView from '../../views/SearchMessagesView';
|
||||
import RoomFilesView from '../../views/RoomFilesView';
|
||||
import RoomMembersView from '../../views/RoomMembersView';
|
||||
import RoomInfoView from '../../views/RoomInfoView';
|
||||
import RoomInfoEditView from '../../views/RoomInfoEditView';
|
||||
import ProfileView from '../../views/ProfileView';
|
||||
import SettingsView from '../../views/SettingsView';
|
||||
import I18n from '../../i18n';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
|
||||
const headerTintColor = '#292E35';
|
||||
|
||||
const AuthRoutes = createStackNavigator(
|
||||
{
|
||||
RoomsList: {
|
||||
screen: RoomsListView
|
||||
},
|
||||
Room: {
|
||||
screen: RoomView
|
||||
},
|
||||
CreateChannel: {
|
||||
screen: CreateChannelView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Create_Channel'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
SelectedUsers: {
|
||||
screen: SelectedUsersView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Select_Users'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
AddServer: {
|
||||
screen: NewServerView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('New_Server'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
RoomActions: {
|
||||
screen: RoomActionsView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Actions'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
StarredMessages: {
|
||||
screen: StarredMessagesView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Starred_Messages'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
PinnedMessages: {
|
||||
screen: PinnedMessagesView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Pinned_Messages'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
MentionedMessages: {
|
||||
screen: MentionedMessagesView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Mentioned_Messages'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
SnippetedMessages: {
|
||||
screen: SnippetedMessagesView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Snippet_Messages'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
SearchMessages: {
|
||||
screen: SearchMessagesView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Search_Messages'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
RoomFiles: {
|
||||
screen: RoomFilesView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Room_Files'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
RoomMembers: {
|
||||
screen: RoomMembersView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Room_Members'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
RoomInfo: {
|
||||
screen: RoomInfoView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Room_Info'),
|
||||
headerTintColor
|
||||
}
|
||||
},
|
||||
RoomInfoEdit: {
|
||||
screen: RoomInfoEditView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Room_Info_Edit'),
|
||||
headerTintColor
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
navigationOptions: {
|
||||
headerTitleAllowFontScaling: false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const MenuButton = ({ navigation, testID }) => (
|
||||
<TouchableOpacity
|
||||
style={sharedStyles.headerButton}
|
||||
onPress={navigation.toggleDrawer}
|
||||
accessibilityLabel={I18n.t('Toggle_Drawer')}
|
||||
accessibilityTraits='button'
|
||||
testID={testID}
|
||||
>
|
||||
<Icon name='menu' size={30} color='#292E35' />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
const Routes = createDrawerNavigator(
|
||||
{
|
||||
Chats: {
|
||||
screen: AuthRoutes,
|
||||
navigationOptions: {
|
||||
drawerLabel: I18n.t('Chats'),
|
||||
drawerIcon: () => <Icon name='chat-bubble' size={20} />
|
||||
}
|
||||
},
|
||||
ProfileView: {
|
||||
screen: createStackNavigator({
|
||||
ProfileView: {
|
||||
screen: ProfileView,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: I18n.t('Profile'),
|
||||
headerTintColor: '#292E35',
|
||||
headerLeft: <MenuButton navigation={navigation} testID='profile-view-sidebar' />
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
SettingsView: {
|
||||
screen: createStackNavigator({
|
||||
SettingsView: {
|
||||
screen: SettingsView,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: I18n.t('Settings'),
|
||||
headerTintColor: '#292E35',
|
||||
headerLeft: <MenuButton navigation={navigation} testID='settings-view-sidebar' />
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
contentComponent: Sidebar,
|
||||
drawerLockMode: Platform.OS === 'ios' ? 'locked-closed' : 'unlocked',
|
||||
initialRouteName: 'Chats',
|
||||
backBehavior: 'initialRoute'
|
||||
}
|
||||
);
|
||||
|
||||
export default Routes;
|
|
@ -1,58 +0,0 @@
|
|||
import { NavigationActions, StackActions } from 'react-navigation';
|
||||
|
||||
const config = {};
|
||||
|
||||
export function setNavigator(nav) {
|
||||
if (nav) {
|
||||
config.navigator = nav;
|
||||
}
|
||||
}
|
||||
|
||||
export function navigate(routeName, params) {
|
||||
if (config.navigator && routeName) {
|
||||
const action = NavigationActions.navigate({ key: routeName, routeName, params });
|
||||
config.navigator.dispatch(action);
|
||||
}
|
||||
}
|
||||
|
||||
export function goBack() {
|
||||
if (config.navigator) {
|
||||
const action = NavigationActions.back({});
|
||||
config.navigator.dispatch(action);
|
||||
}
|
||||
}
|
||||
|
||||
export function goRoomsList() {
|
||||
if (config.navigator) {
|
||||
const action = StackActions.reset({
|
||||
index: 0,
|
||||
actions: [NavigationActions.navigate({ key: 'RoomsList', routeName: 'RoomsList' })]
|
||||
});
|
||||
config.navigator.dispatch(action);
|
||||
}
|
||||
}
|
||||
|
||||
export function goRoom({ rid, name }, counter = 0) {
|
||||
// about counter: we can call this method before navigator be set. so we have to wait, if we tried a lot, we give up ...
|
||||
if (!rid || counter > 10) {
|
||||
return;
|
||||
}
|
||||
if (!config.navigator) {
|
||||
return setTimeout(() => goRoom({ rid, name }, counter + 1), 100);
|
||||
}
|
||||
|
||||
const action = StackActions.reset({
|
||||
index: 1,
|
||||
actions: [
|
||||
NavigationActions.navigate({ key: 'RoomsList', routeName: 'RoomsList' }),
|
||||
NavigationActions.navigate({ key: `Room-${ rid }`, routeName: 'Room', params: { room: { rid, name }, rid, name } })
|
||||
]
|
||||
});
|
||||
config.navigator.dispatch(action);
|
||||
}
|
||||
|
||||
export function dispatch(action) {
|
||||
if (config.navigator) {
|
||||
config.navigator.dispatch(action);
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
import React from 'react';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { createStackNavigator } from 'react-navigation';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import ListServerView from '../../views/ListServerView';
|
||||
import NewServerView from '../../views/NewServerView';
|
||||
import LoginSignupView from '../../views/LoginSignupView';
|
||||
import LoginView from '../../views/LoginView';
|
||||
import RegisterView from '../../views/RegisterView';
|
||||
|
||||
import TermsServiceView from '../../views/TermsServiceView';
|
||||
import PrivacyPolicyView from '../../views/PrivacyPolicyView';
|
||||
import ForgotPasswordView from '../../views/ForgotPasswordView';
|
||||
import database from '../../lib/realm';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
const hasServers = () => {
|
||||
const db = database.databases.serversDB.objects('servers');
|
||||
return db.length > 0;
|
||||
};
|
||||
|
||||
const ServerStack = createStackNavigator({
|
||||
ListServer: {
|
||||
screen: ListServerView,
|
||||
navigationOptions({ navigation }) {
|
||||
return {
|
||||
title: I18n.t('Servers'),
|
||||
headerRight: (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigate({ key: 'AddServer', routeName: 'AddServer' })}
|
||||
style={{ width: 50, alignItems: 'center' }}
|
||||
accessibilityLabel={I18n.t('Add_Server')}
|
||||
accessibilityTraits='button'
|
||||
>
|
||||
<Icon name='plus' size={16} />
|
||||
</TouchableOpacity>
|
||||
)
|
||||
};
|
||||
}
|
||||
},
|
||||
AddServer: {
|
||||
screen: NewServerView,
|
||||
navigationOptions: {
|
||||
header: null
|
||||
}
|
||||
},
|
||||
LoginSignup: {
|
||||
screen: LoginSignupView,
|
||||
navigationOptions: {
|
||||
header: null
|
||||
}
|
||||
}
|
||||
}, {
|
||||
headerMode: 'screen',
|
||||
initialRouteName: hasServers() ? 'ListServer' : 'AddServer'
|
||||
});
|
||||
|
||||
const LoginStack = createStackNavigator({
|
||||
Login: {
|
||||
screen: LoginView,
|
||||
navigationOptions: {
|
||||
header: null
|
||||
}
|
||||
},
|
||||
ForgotPassword: {
|
||||
screen: ForgotPasswordView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Forgot_my_password'),
|
||||
headerTintColor: '#292E35'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
headerMode: 'screen'
|
||||
});
|
||||
|
||||
const RegisterStack = createStackNavigator({
|
||||
Register: {
|
||||
screen: RegisterView,
|
||||
navigationOptions: {
|
||||
header: null
|
||||
}
|
||||
},
|
||||
TermsService: {
|
||||
screen: TermsServiceView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Terms_of_Service'),
|
||||
headerTintColor: '#292E35'
|
||||
}
|
||||
},
|
||||
PrivacyPolicy: {
|
||||
screen: PrivacyPolicyView,
|
||||
navigationOptions: {
|
||||
title: I18n.t('Privacy_Policy'),
|
||||
headerTintColor: '#292E35'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
headerMode: 'screen'
|
||||
});
|
||||
|
||||
const PublicRoutes = createStackNavigator(
|
||||
{
|
||||
Server: {
|
||||
screen: ServerStack
|
||||
},
|
||||
Login: {
|
||||
screen: LoginStack
|
||||
},
|
||||
Register: {
|
||||
screen: RegisterStack
|
||||
}
|
||||
},
|
||||
{
|
||||
mode: 'modal',
|
||||
headerMode: 'none'
|
||||
}
|
||||
);
|
||||
|
||||
export default PublicRoutes;
|
|
@ -12,41 +12,33 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
@connect(state => ({
|
||||
activeUsers: state.activeUsers,
|
||||
user: state.login.user,
|
||||
@connect((state, ownProps) => {
|
||||
if (state.login.user && ownProps.id === state.login.user.id) {
|
||||
return {
|
||||
status: state.login.user && state.login.user.status,
|
||||
offline: !state.meteor.connected
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
export default class Status extends React.Component {
|
||||
const user = state.activeUsers[ownProps.id];
|
||||
return {
|
||||
status: (user && user.status) || 'offline'
|
||||
};
|
||||
})
|
||||
|
||||
export default class Status extends React.PureComponent {
|
||||
static propTypes = {
|
||||
style: ViewPropTypes.style,
|
||||
id: PropTypes.string,
|
||||
activeUsers: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
status: PropTypes.string,
|
||||
offline: PropTypes.bool
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { id: userId, user } = this.props;
|
||||
if (user.id === userId) {
|
||||
if (nextProps.offline !== this.props.offline) {
|
||||
return true;
|
||||
}
|
||||
return (nextProps.user && nextProps.user.status !== user.status);
|
||||
}
|
||||
return (nextProps.activeUsers[userId] && nextProps.activeUsers[userId].status) !== this.status;
|
||||
}
|
||||
|
||||
get status() {
|
||||
const { id: userId, user, offline } = this.props;
|
||||
if (user.id === userId) {
|
||||
const { offline, status } = this.props;
|
||||
if (offline) {
|
||||
return 'offline';
|
||||
}
|
||||
return user.status || 'offline';
|
||||
}
|
||||
return (this.props.activeUsers && this.props.activeUsers[userId] && this.props.activeUsers[userId].status) || 'offline';
|
||||
return status;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -109,6 +109,7 @@ export default {
|
|||
changing_avatar: 'changing avatar',
|
||||
Channel_Name: 'Channel Name',
|
||||
Chats: 'Chats',
|
||||
Close: 'Close',
|
||||
Close_emoji_selector: 'Close emoji selector',
|
||||
Code: 'Code',
|
||||
Colaborative: 'Colaborative',
|
||||
|
@ -141,6 +142,7 @@ export default {
|
|||
Forgot_my_password: 'Forgot my password',
|
||||
Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.',
|
||||
Forgot_password: 'Forgot password',
|
||||
Forgot_Password: 'Forgot Password',
|
||||
Has_joined_the_channel: 'Has joined the channel',
|
||||
Has_left_the_channel: 'Has left the channel',
|
||||
I_have_an_account: 'I have an account',
|
||||
|
@ -164,6 +166,7 @@ export default {
|
|||
Message_actions: 'Message actions',
|
||||
Message_pinned: 'Message pinned',
|
||||
Message_removed: 'Message removed',
|
||||
Messages: 'Messages',
|
||||
Microphone_Permission_Message: 'Rocket Chat needs access to your microphone so you can send audio message.',
|
||||
Microphone_Permission: 'Microphone Permission',
|
||||
Mute: 'Mute',
|
||||
|
@ -294,6 +297,7 @@ export default {
|
|||
Validating: 'Validating',
|
||||
Video_call: 'Video call',
|
||||
Voice_call: 'Voice call',
|
||||
Welcome: 'Welcome',
|
||||
Welcome_title_pt_1: 'Prepare to take off with',
|
||||
Welcome_title_pt_2: 'the ultimate chat platform',
|
||||
Yes_action_it: 'Yes, {{action}} it!',
|
||||
|
|
98
app/index.js
|
@ -1,14 +1,94 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Component } from 'react';
|
||||
import { Linking } from 'react-native';
|
||||
import { Navigation } from 'react-native-navigation';
|
||||
|
||||
import store from './lib/createStore';
|
||||
import { appInit } from './actions';
|
||||
import database from './lib/realm';
|
||||
import { iconsLoaded } from './Icons';
|
||||
import { registerScreens } from './views';
|
||||
import { deepLinkingOpen } from './actions/deepLinking';
|
||||
import parseQuery from './lib/methods/helpers/parseQuery';
|
||||
import I18n from './i18n';
|
||||
import { initializePushNotifications } from './push';
|
||||
|
||||
import Routes from './containers/Routes';
|
||||
const startLogged = () => {
|
||||
Navigation.startSingleScreenApp({
|
||||
screen: {
|
||||
screen: 'RoomsListView',
|
||||
title: I18n.t('Messages')
|
||||
},
|
||||
drawer: {
|
||||
left: {
|
||||
screen: 'Sidebar'
|
||||
}
|
||||
},
|
||||
animationType: 'fade'
|
||||
});
|
||||
};
|
||||
|
||||
const RocketChat = () => (
|
||||
<Provider store={store}>
|
||||
<Routes />
|
||||
</Provider>
|
||||
);
|
||||
const startNotLogged = (route) => {
|
||||
Navigation.startSingleScreenApp({
|
||||
screen: {
|
||||
screen: route,
|
||||
title: route === 'NewServerView' ? I18n.t('New_Server') : I18n.t('Servers')
|
||||
},
|
||||
animationType: 'fade'
|
||||
});
|
||||
};
|
||||
|
||||
export default RocketChat;
|
||||
const hasServers = () => {
|
||||
const db = database.databases.serversDB.objects('servers');
|
||||
return db.length > 0;
|
||||
};
|
||||
|
||||
const handleOpenURL = ({ url }) => {
|
||||
if (url) {
|
||||
url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
|
||||
const regex = /^(room|auth)\?/;
|
||||
if (url.match(regex)) {
|
||||
url = url.replace(regex, '');
|
||||
const params = parseQuery(url);
|
||||
store.dispatch(deepLinkingOpen(params));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
registerScreens(store);
|
||||
iconsLoaded();
|
||||
|
||||
export default class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
store.dispatch(appInit());
|
||||
store.subscribe(this.onStoreUpdate.bind(this));
|
||||
initializePushNotifications();
|
||||
|
||||
Linking
|
||||
.getInitialURL()
|
||||
.then(url => handleOpenURL({ url }))
|
||||
.catch(e => console.warn(e));
|
||||
Linking.addEventListener('url', handleOpenURL);
|
||||
}
|
||||
|
||||
onStoreUpdate = () => {
|
||||
const { root } = store.getState().app;
|
||||
|
||||
if (this.currentRoot !== root) {
|
||||
this.currentRoot = root;
|
||||
if (root === 'outside') {
|
||||
if (hasServers()) {
|
||||
startNotLogged('ListServerView');
|
||||
} else {
|
||||
startNotLogged('NewServerView');
|
||||
}
|
||||
} else if (root === 'inside') {
|
||||
startLogged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setDeviceToken(deviceToken) {
|
||||
this.deviceToken = deviceToken;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ if (__DEV__) {
|
|||
/* eslint-disable global-require */
|
||||
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
||||
sagaMiddleware = createSagaMiddleware({
|
||||
sagaMonitor: Reactotron.createSagaMonitor()
|
||||
// sagaMonitor: Reactotron.createSagaMonitor()
|
||||
});
|
||||
|
||||
enhancers = compose(
|
||||
|
|
|
@ -54,5 +54,6 @@ export default async function canOpenRoom({ rid, path }) {
|
|||
return data;
|
||||
} catch (e) {
|
||||
log('canOpenRoom', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ const attachment = {
|
|||
video_url: { type: 'string', optional: true },
|
||||
title: { type: 'string', optional: true },
|
||||
title_link: { type: 'string', optional: true },
|
||||
title_link_download: { type: 'bool', optional: true },
|
||||
// title_link_download: { type: 'bool', optional: true },
|
||||
type: { type: 'string', optional: true },
|
||||
author_icon: { type: 'string', optional: true },
|
||||
author_name: { type: 'string', optional: true },
|
||||
|
|
|
@ -40,6 +40,8 @@ import loadMissedMessages from './methods/loadMissedMessages';
|
|||
|
||||
import sendMessage, { getMessage, _sendMessageCall } from './methods/sendMessage';
|
||||
|
||||
import { getDeviceToken } from '../push';
|
||||
|
||||
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
||||
const call = (method, ...params) => RocketChat.ddp.call(method, ...params); // eslint-disable-line
|
||||
const returnAnArray = obj => obj || [];
|
||||
|
@ -119,7 +121,7 @@ const RocketChat = {
|
|||
reduxStore.dispatch(setActiveUser(this.activeUsers));
|
||||
this._setUserTimer = null;
|
||||
return this.activeUsers = {};
|
||||
}, 5000);
|
||||
}, 2000);
|
||||
|
||||
const activeUser = reduxStore.getState().activeUsers[ddpMessage.id];
|
||||
if (!ddpMessage.fields) {
|
||||
|
@ -175,8 +177,8 @@ const RocketChat = {
|
|||
this.ddp.on('disconnected', () => console.log('disconnected'));
|
||||
|
||||
this.ddp.on('logged', protectedFunction((user) => {
|
||||
this.getRooms().catch(e => log('logged getRooms', e));
|
||||
this.loginSuccess(user);
|
||||
this.getRooms().catch(e => log('logged getRooms', e));
|
||||
}));
|
||||
this.ddp.once('logged', protectedFunction(({ id }) => {
|
||||
this.subscribeRooms(id);
|
||||
|
@ -556,21 +558,24 @@ const RocketChat = {
|
|||
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
||||
},
|
||||
|
||||
registerPushToken(id, token) {
|
||||
registerPushToken(userId) {
|
||||
const deviceToken = getDeviceToken();
|
||||
if (deviceToken) {
|
||||
const key = Platform.OS === 'ios' ? 'apn' : 'gcm';
|
||||
const data = {
|
||||
id: `RocketChatRN${ id }`,
|
||||
token: { [key]: token },
|
||||
id: `RocketChatRN${ userId }`,
|
||||
token: { [key]: deviceToken },
|
||||
appName: 'chat.rocket.reactnative', // TODO: try to get from config file
|
||||
userId: id,
|
||||
userId,
|
||||
metadata: {}
|
||||
};
|
||||
return call('raix:push-update', data);
|
||||
}
|
||||
},
|
||||
|
||||
updatePushToken(pushId) {
|
||||
return call('raix:push-setuser', pushId);
|
||||
},
|
||||
// updatePushToken(pushId) {
|
||||
// return call('raix:push-setuser', pushId);
|
||||
// },
|
||||
loadMissedMessages,
|
||||
loadMessagesForRoom,
|
||||
getMessage,
|
||||
|
|
|
@ -121,7 +121,7 @@ const renderNumber = (unread, userMentions) => {
|
|||
|
||||
const attrs = ['name', 'unread', 'userMentions', 'alert', 'showLastMessage', 'type'];
|
||||
@connect(state => ({
|
||||
user: state.login && state.login.user,
|
||||
username: state.login.user && state.login.user.username,
|
||||
StoreLastMessage: state.settings.Store_Last_Message
|
||||
}))
|
||||
export default class RoomItem extends React.Component {
|
||||
|
@ -139,7 +139,7 @@ export default class RoomItem extends React.Component {
|
|||
id: PropTypes.string,
|
||||
onPress: PropTypes.func,
|
||||
onLongPress: PropTypes.func,
|
||||
user: PropTypes.object,
|
||||
username: PropTypes.string,
|
||||
avatarSize: PropTypes.number,
|
||||
statusStyle: ViewPropTypes.style,
|
||||
testID: PropTypes.string
|
||||
|
@ -182,7 +182,7 @@ export default class RoomItem extends React.Component {
|
|||
|
||||
let prefix = '';
|
||||
|
||||
if (lastMessage.u.username === this.props.user.username) {
|
||||
if (lastMessage.u.username === this.props.username) {
|
||||
prefix = I18n.t('You_colon');
|
||||
} else if (type !== 'd') {
|
||||
prefix = `${ lastMessage.u.username }: `;
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import PushNotification from 'react-native-push-notification';
|
||||
import { AsyncStorage } from 'react-native';
|
||||
import EJSON from 'ejson';
|
||||
import { goRoom } from './containers/routes/NavigationService';
|
||||
|
||||
import { NavigationActions } from './Navigation';
|
||||
|
||||
const handleNotification = (notification) => {
|
||||
if (!notification.userInteraction) {
|
||||
return;
|
||||
}
|
||||
if (notification.userInteraction) {
|
||||
const {
|
||||
rid, name, sender, type
|
||||
} = EJSON.parse(notification.ejson || notification.data.ejson);
|
||||
return rid && goRoom({ rid, name: type === 'd' ? sender.username : name });
|
||||
NavigationActions.push({
|
||||
screen: 'RoomView',
|
||||
passProps: { rid, name: type === 'd' ? sender.username : name }
|
||||
});
|
||||
}
|
||||
};
|
||||
PushNotification.configure({
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import EJSON from 'ejson';
|
||||
|
||||
import PushNotification from './push';
|
||||
import store from '../lib/createStore';
|
||||
import { deepLinkingOpen } from '../actions/deepLinking';
|
||||
|
||||
const onNotification = (notification) => {
|
||||
if (notification) {
|
||||
const data = notification.getData();
|
||||
if (data) {
|
||||
try {
|
||||
const {
|
||||
rid, name, sender, type, host
|
||||
} = EJSON.parse(data.ejson);
|
||||
|
||||
const types = {
|
||||
c: 'channel', d: 'direct', p: 'group'
|
||||
};
|
||||
const roomName = type === 'd' ? sender.username : name;
|
||||
|
||||
const params = {
|
||||
host,
|
||||
rid,
|
||||
path: `${ types[type] }/${ roomName }`
|
||||
};
|
||||
store.dispatch(deepLinkingOpen(params));
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const initializePushNotifications = () => {
|
||||
PushNotification.configure({
|
||||
onNotification
|
||||
});
|
||||
};
|
||||
|
||||
const getDeviceToken = () => PushNotification.getDeviceToken();
|
||||
|
||||
export { initializePushNotifications, getDeviceToken };
|
|
@ -0,0 +1,34 @@
|
|||
import { NotificationsAndroid, PendingNotifications } from 'react-native-notifications';
|
||||
|
||||
class PushNotification {
|
||||
constructor() {
|
||||
this.onRegister = null;
|
||||
this.onNotification = null;
|
||||
this.deviceToken = null;
|
||||
|
||||
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
|
||||
this.deviceToken = deviceToken;
|
||||
});
|
||||
|
||||
NotificationsAndroid.setNotificationOpenedListener((notification) => {
|
||||
this.onNotification(notification);
|
||||
});
|
||||
}
|
||||
|
||||
getDeviceToken() {
|
||||
return this.deviceToken;
|
||||
}
|
||||
|
||||
configure(params) {
|
||||
this.onRegister = params.onRegister;
|
||||
this.onNotification = params.onNotification;
|
||||
|
||||
PendingNotifications.getInitialNotification()
|
||||
.then((notification) => {
|
||||
this.onNotification(notification);
|
||||
})
|
||||
.catch(e => console.warn(e));
|
||||
}
|
||||
}
|
||||
|
||||
export default new PushNotification();
|
|
@ -0,0 +1,31 @@
|
|||
import NotificationsIOS from 'react-native-notifications';
|
||||
|
||||
class PushNotification {
|
||||
constructor() {
|
||||
this.onRegister = null;
|
||||
this.onNotification = null;
|
||||
this.deviceToken = null;
|
||||
|
||||
NotificationsIOS.addEventListener('remoteNotificationsRegistered', (deviceToken) => {
|
||||
this.deviceToken = deviceToken;
|
||||
});
|
||||
|
||||
NotificationsIOS.addEventListener('notificationOpened', (notification) => {
|
||||
this.onNotification(notification);
|
||||
});
|
||||
|
||||
NotificationsIOS.requestPermissions();
|
||||
}
|
||||
|
||||
getDeviceToken() {
|
||||
return this.deviceToken;
|
||||
}
|
||||
|
||||
configure(params) {
|
||||
this.onRegister = params.onRegister;
|
||||
this.onNotification = params.onNotification;
|
||||
|
||||
NotificationsIOS.consumeBackgroundQueue();
|
||||
}
|
||||
}
|
||||
export default new PushNotification();
|
|
@ -2,6 +2,7 @@ import { FOREGROUND, BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-ap
|
|||
import { APP } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
root: null,
|
||||
starting: true,
|
||||
ready: false,
|
||||
inactive: false,
|
||||
|
@ -31,6 +32,11 @@ export default function app(state = initialState, action) {
|
|||
foreground: false,
|
||||
background: false
|
||||
};
|
||||
case APP.START:
|
||||
return {
|
||||
...state,
|
||||
root: action.root
|
||||
};
|
||||
case APP.INIT:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -19,6 +19,7 @@ export default function login(state = initialState, action) {
|
|||
...state,
|
||||
isFetching: true,
|
||||
isAuthenticated: false,
|
||||
isRegistering: false,
|
||||
failure: false,
|
||||
error: ''
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { call, takeLatest, select, put, all } from 'redux-saga/effects';
|
||||
import { call, takeLatest, select, put } from 'redux-saga/effects';
|
||||
import { AsyncStorage } from 'react-native';
|
||||
import { METEOR } from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
@ -31,7 +31,8 @@ const test = function* test() {
|
|||
const server = yield select(getServer);
|
||||
const user = yield call(getToken);
|
||||
// const response =
|
||||
yield all([call(connect, server, user && user.token ? { resume: user.token, ...user.user } : undefined)]);// , put(loginRequest({ resume: user.token }))]);
|
||||
// yield all([call(connect, server, user && user.token ? { resume: user.token, ...user.user } : undefined)]);// , put(loginRequest({ resume: user.token }))]);
|
||||
yield call(connect, server, user && user.token ? { resume: user.token, ...user.user } : undefined);
|
||||
// yield put(connectSuccess(response));
|
||||
} catch (err) {
|
||||
console.warn('test', err);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { delay } from 'redux-saga';
|
||||
import { select, put, call, take, takeLatest } from 'redux-saga/effects';
|
||||
import { NavigationActions } from '../Navigation';
|
||||
|
||||
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
||||
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { goRoom } from '../containers/routes/NavigationService';
|
||||
|
||||
const create = function* create(data) {
|
||||
return yield RocketChat.createChannel(data);
|
||||
|
@ -18,7 +19,17 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
}
|
||||
const result = yield call(create, data);
|
||||
const { rid, name } = result;
|
||||
goRoom({ rid, name });
|
||||
NavigationActions.popToRoot();
|
||||
yield delay(1000);
|
||||
NavigationActions.push({
|
||||
screen: 'RoomView',
|
||||
title: name,
|
||||
passProps: {
|
||||
room: { rid, name },
|
||||
rid,
|
||||
name
|
||||
}
|
||||
});
|
||||
yield put(createChannelSuccess(result));
|
||||
} catch (err) {
|
||||
yield put(createChannelFailure(err));
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
import { delay } from 'redux-saga';
|
||||
import { takeLatest, take, select, call, put } from 'redux-saga/effects';
|
||||
import { takeLatest, take, select, put } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import { setServer, addServer } from '../actions/server';
|
||||
import * as NavigationService from '../containers/routes/NavigationService';
|
||||
import { appStart } from '../actions';
|
||||
import { selectServer, addServer } from '../actions/server';
|
||||
import database from '../lib/realm';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { NavigationActions } from '../Navigation';
|
||||
|
||||
const navigate = function* go({ server, params, sameServer = true }) {
|
||||
const user = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||
if (user) {
|
||||
const { rid, path } = params;
|
||||
if (rid) {
|
||||
const canOpenRoom = yield RocketChat.canOpenRoom({ rid, path });
|
||||
if (canOpenRoom) {
|
||||
return yield call(NavigationService.goRoom, { rid: params.rid });
|
||||
}
|
||||
}
|
||||
const navigate = function* go({ params, sameServer = true }) {
|
||||
if (!sameServer) {
|
||||
yield call(NavigationService.goRoomsList);
|
||||
yield put(appStart('inside'));
|
||||
}
|
||||
if (params.rid) {
|
||||
const canOpenRoom = yield RocketChat.canOpenRoom(params);
|
||||
if (canOpenRoom) {
|
||||
return NavigationActions.push({
|
||||
screen: 'RoomView',
|
||||
passProps: {
|
||||
rid: params.rid
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -35,7 +37,14 @@ const handleOpen = function* handleOpen({ params }) {
|
|||
return;
|
||||
}
|
||||
|
||||
const host = `https://${ params.host }`;
|
||||
let { host } = params;
|
||||
if (!/^(http|https)/.test(host)) {
|
||||
host = `https://${ params.host }`;
|
||||
}
|
||||
// remove last "/" from host
|
||||
if (host.slice(-1) === '/') {
|
||||
host = host.slice(0, host.length - 1);
|
||||
}
|
||||
|
||||
try {
|
||||
yield RocketChat.testServer(host);
|
||||
|
@ -43,18 +52,26 @@ const handleOpen = function* handleOpen({ params }) {
|
|||
return;
|
||||
}
|
||||
|
||||
const token = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ host }`);
|
||||
|
||||
// TODO: needs better test
|
||||
// if deep link is from same server
|
||||
if (server === host) {
|
||||
yield navigate({ server, params });
|
||||
if (token) {
|
||||
yield navigate({ params });
|
||||
}
|
||||
} else { // if deep link is from a different server
|
||||
// search if deep link's server already exists
|
||||
const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test
|
||||
if (servers.length) {
|
||||
// if server exists, select it
|
||||
yield put(setServer(servers[0].id));
|
||||
yield delay(2000);
|
||||
yield navigate({ server: servers[0].id, params, sameServer: false });
|
||||
const deepLinkServer = servers[0].id;
|
||||
if (!token) {
|
||||
yield put(appStart('outside'));
|
||||
} else {
|
||||
yield put(selectServer(deepLinkServer));
|
||||
yield take(types.METEOR.REQUEST);
|
||||
yield navigate({ params, sameServer: false });
|
||||
}
|
||||
} else {
|
||||
yield put(addServer(host));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { AsyncStorage } from 'react-native';
|
|||
import { call, put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as actions from '../actions';
|
||||
import { setServer } from '../actions/server';
|
||||
import { selectServer } from '../actions/server';
|
||||
import { restoreToken, setUser } from '../actions/login';
|
||||
import { APP } from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
@ -13,11 +13,16 @@ const restore = function* restore() {
|
|||
const token = yield call([AsyncStorage, 'getItem'], RocketChat.TOKEN_KEY);
|
||||
if (token) {
|
||||
yield put(restoreToken(token));
|
||||
} else {
|
||||
yield put(actions.appStart('outside'));
|
||||
}
|
||||
|
||||
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
||||
if (currentServer) {
|
||||
yield put(setServer(currentServer));
|
||||
yield put(selectServer(currentServer));
|
||||
if (token) {
|
||||
yield put(actions.appStart('inside'));
|
||||
}
|
||||
|
||||
const login = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
|
||||
if (login) {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
import { delay } from 'redux-saga';
|
||||
import { put, call, take, takeLatest, select, all } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import { appStart } from '../actions';
|
||||
import {
|
||||
// loginRequest,
|
||||
// loginSubmit,
|
||||
|
@ -18,7 +20,6 @@ import {
|
|||
forgotPasswordFailure
|
||||
} from '../actions/login';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import * as NavigationService from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
import I18n from '../i18n';
|
||||
|
||||
|
@ -26,7 +27,6 @@ const getUser = state => state.login;
|
|||
const getServer = state => state.server.server;
|
||||
const getIsConnected = state => state.meteor.connected;
|
||||
|
||||
// const loginCall = args => ((args.resume || args.oauth) ? RocketChat.login(args) : RocketChat.loginWithPassword(args));
|
||||
const loginCall = args => RocketChat.loginWithPassword(args);
|
||||
const registerCall = args => RocketChat.register(args);
|
||||
const setUsernameCall = args => RocketChat.setUsername(args);
|
||||
|
@ -34,67 +34,27 @@ const loginSuccessCall = () => RocketChat.loginSuccess();
|
|||
const logoutCall = args => RocketChat.logout(args);
|
||||
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
||||
|
||||
// const getToken = function* getToken() {
|
||||
// const currentServer = yield select(getServer);
|
||||
// const user = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
|
||||
// if (user) {
|
||||
// try {
|
||||
// yield put(setToken(JSON.parse(user)));
|
||||
// yield call([AsyncStorage, 'setItem'], RocketChat.TOKEN_KEY, JSON.parse(user).token || '');
|
||||
// return JSON.parse(user);
|
||||
// } catch (e) {
|
||||
// console.log('getTokenerr', e);
|
||||
// }
|
||||
// } else {
|
||||
// return yield put(setToken());
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() {
|
||||
// try {
|
||||
// const user = yield call(getToken);
|
||||
// if (user.token) {
|
||||
// yield put(loginRequest({ resume: user.token }));
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log(e);
|
||||
// }
|
||||
// };
|
||||
|
||||
const saveToken = function* saveToken() {
|
||||
const handleLoginSuccess = function* handleLoginSuccess() {
|
||||
try {
|
||||
const [server, user] = yield all([select(getServer), select(getUser)]);
|
||||
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
|
||||
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
||||
const token = yield AsyncStorage.getItem('pushId');
|
||||
if (token) {
|
||||
yield RocketChat.registerPushToken(user.user.id, token);
|
||||
}
|
||||
if (!user.user.username && !user.isRegistering) {
|
||||
// const token = yield AsyncStorage.getItem('pushId');
|
||||
// if (token) {
|
||||
// yield RocketChat.registerPushToken(user.user.id, token);
|
||||
// }
|
||||
yield RocketChat.registerPushToken(user.user.id);
|
||||
if (!user.user.username || user.isRegistering) {
|
||||
yield put(registerIncomplete());
|
||||
} else {
|
||||
yield delay(300);
|
||||
yield put(appStart('inside'));
|
||||
}
|
||||
} catch (e) {
|
||||
log('saveToken', e);
|
||||
log('handleLoginSuccess', e);
|
||||
}
|
||||
};
|
||||
|
||||
// const handleLoginRequest = function* handleLoginRequest({ credentials }) {
|
||||
// try {
|
||||
// // const server = yield select(getServer);
|
||||
// const user = yield call(loginCall, credentials);
|
||||
// yield put(loginSuccess(user));
|
||||
// } catch (err) {
|
||||
// if (err.error === 403) {
|
||||
// return yield put(logout());
|
||||
// }
|
||||
// yield put(loginFailure(err));
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleLoginSubmit = function* handleLoginSubmit({ credentials }) {
|
||||
// yield put(loginRequest(credentials));
|
||||
// };
|
||||
|
||||
const handleRegisterSubmit = function* handleRegisterSubmit({ credentials }) {
|
||||
yield put(registerRequest(credentials));
|
||||
};
|
||||
|
@ -137,6 +97,8 @@ const handleLogout = function* handleLogout() {
|
|||
const server = yield select(getServer);
|
||||
if (server) {
|
||||
try {
|
||||
yield put(appStart('outside'));
|
||||
yield delay(300);
|
||||
yield call(logoutCall, { server });
|
||||
} catch (e) {
|
||||
log('handleLogout', e);
|
||||
|
@ -145,7 +107,7 @@ const handleLogout = function* handleLogout() {
|
|||
};
|
||||
|
||||
const handleRegisterIncomplete = function* handleRegisterIncomplete() {
|
||||
yield call(NavigationService.navigate, 'Register');
|
||||
yield put(appStart('outside'));
|
||||
};
|
||||
|
||||
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
|
||||
|
@ -183,7 +145,7 @@ const handleSetUser = function* handleSetUser(params) {
|
|||
const root = function* root() {
|
||||
// yield takeLatest(types.METEOR.SUCCESS, handleLoginWhenServerChanges);
|
||||
// yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
|
||||
yield takeLatest(types.LOGIN.SUCCESS, saveToken);
|
||||
yield takeLatest(types.LOGIN.SUCCESS, handleLoginSuccess);
|
||||
// yield takeLatest(types.LOGIN.SUBMIT, handleLoginSubmit);
|
||||
yield takeLatest(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
|
||||
yield takeLatest(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { delay } from 'redux-saga';
|
||||
import { takeLatest, put, call, select } from 'redux-saga/effects';
|
||||
|
||||
import { MESSAGES } from '../actions/actionsTypes';
|
||||
import {
|
||||
messagesSuccess,
|
||||
|
@ -16,8 +17,8 @@ import {
|
|||
} from '../actions/messages';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import { goRoom } from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
import { NavigationActions } from '../Navigation';
|
||||
|
||||
const deleteMessage = message => RocketChat.deleteMessage(message);
|
||||
const editMessage = message => RocketChat.editMessage(message);
|
||||
|
@ -74,17 +75,30 @@ const handleTogglePinRequest = function* handleTogglePinRequest({ message }) {
|
|||
}
|
||||
};
|
||||
|
||||
const goRoom = function* goRoom({ rid, name }) {
|
||||
NavigationActions.popToRoot();
|
||||
yield delay(1000);
|
||||
NavigationActions.push({
|
||||
screen: 'RoomView',
|
||||
passProps: {
|
||||
room: { rid, name },
|
||||
rid,
|
||||
name
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
||||
try {
|
||||
const { username } = message.u;
|
||||
const subscriptions = database.objects('subscriptions').filtered('name = $0', username);
|
||||
if (subscriptions.length) {
|
||||
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||
yield goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||
} else {
|
||||
const room = yield RocketChat.createDirectMessage(username);
|
||||
goRoom({ rid: room.rid, name: username });
|
||||
yield goRoom({ rid: room.rid, name: username });
|
||||
}
|
||||
yield delay(100);
|
||||
yield delay(500);
|
||||
const server = yield select(state => state.server.server);
|
||||
const msg = `[ ](${ server }/direct/${ username }?msg=${ message._id }) `;
|
||||
yield put(setInput({ msg }));
|
||||
|
|
|
@ -9,8 +9,8 @@ import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
|
|||
import { messagesRequest, editCancel } from '../actions/messages';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import * as NavigationService from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
import { NavigationActions } from '../Navigation';
|
||||
|
||||
const leaveRoom = rid => RocketChat.leaveRoom(rid);
|
||||
const eraseRoom = rid => RocketChat.eraseRoom(rid);
|
||||
|
@ -139,7 +139,7 @@ const updateLastOpen = function* updateLastOpen() {
|
|||
};
|
||||
|
||||
const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
|
||||
NavigationService.goRoomsList();
|
||||
NavigationActions.popToRoot();
|
||||
yield delay(1000);
|
||||
try {
|
||||
database.write(() => {
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import { put, call, takeLatest, take } from 'redux-saga/effects';
|
||||
import { put, call, takeLatest } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { AsyncStorage } from 'react-native';
|
||||
|
||||
import { SERVER, LOGIN } from '../actions/actionsTypes';
|
||||
import { NavigationActions } from '../Navigation';
|
||||
import { SERVER } from '../actions/actionsTypes';
|
||||
import * as actions from '../actions';
|
||||
import { connectRequest } from '../actions/connect';
|
||||
import { serverSuccess, serverFailure, setServer } from '../actions/server';
|
||||
import { serverSuccess, serverFailure, selectServer } from '../actions/server';
|
||||
import { setRoles } from '../actions/roles';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import { navigate } from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const validate = function* validate(server) {
|
||||
return yield RocketChat.testServer(server);
|
||||
};
|
||||
|
||||
const selectServer = function* selectServer({ server }) {
|
||||
const handleSelectServer = function* handleSelectServer({ server }) {
|
||||
try {
|
||||
yield database.setActiveDB(server);
|
||||
|
||||
|
@ -36,7 +37,7 @@ const selectServer = function* selectServer({ server }) {
|
|||
|
||||
yield put(connectRequest());
|
||||
} catch (e) {
|
||||
log('selectServer', e);
|
||||
log('handleSelectServer', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -53,12 +54,12 @@ const validateServer = function* validateServer({ server }) {
|
|||
|
||||
const addServer = function* addServer({ server }) {
|
||||
try {
|
||||
yield put(actions.appStart('outside'));
|
||||
yield call(NavigationActions.resetTo, { screen: 'ListServerView', title: I18n.t('Servers') });
|
||||
database.databases.serversDB.write(() => {
|
||||
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
||||
});
|
||||
yield put(setServer(server));
|
||||
yield take(LOGIN.SET_TOKEN);
|
||||
navigate('LoginSignup');
|
||||
yield put(selectServer(server));
|
||||
} catch (e) {
|
||||
log('addServer', e);
|
||||
}
|
||||
|
@ -66,7 +67,7 @@ const addServer = function* addServer({ server }) {
|
|||
|
||||
const root = function* root() {
|
||||
yield takeLatest(SERVER.REQUEST, validateServer);
|
||||
yield takeLatest(SERVER.SELECT, selectServer);
|
||||
yield takeLatest(SERVER.SELECT, handleSelectServer);
|
||||
yield takeLatest(SERVER.ADD, addServer);
|
||||
};
|
||||
export default root;
|
||||
|
|
After Width: | Height: | Size: 94 B |
After Width: | Height: | Size: 15 KiB |
|
@ -13,21 +13,19 @@ import scrollPersistTaps from '../utils/scrollPersistTaps';
|
|||
import Button from '../containers/Button';
|
||||
import I18n from '../i18n';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
@connect(state => ({
|
||||
createChannel: state.createChannel,
|
||||
users: state.selectedUsers.users
|
||||
}),
|
||||
dispatch => ({
|
||||
}), dispatch => ({
|
||||
create: data => dispatch(createChannelRequest(data))
|
||||
})
|
||||
)
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class CreateChannelView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
create: PropTypes.func.isRequired,
|
||||
createChannel: PropTypes.object.isRequired,
|
||||
users: PropTypes.array.isRequired,
|
||||
navigation: PropTypes.object.isRequired
|
||||
users: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
|
|
@ -20,12 +20,13 @@ import I18n from '../i18n';
|
|||
forgotPasswordInit: () => dispatch(forgotPasswordInit()),
|
||||
forgotPasswordRequest: email => dispatch(forgotPasswordRequest(email))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class ForgotPasswordView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
forgotPasswordInit: PropTypes.func.isRequired,
|
||||
forgotPasswordRequest: PropTypes.func.isRequired,
|
||||
login: PropTypes.object,
|
||||
navigation: PropTypes.object.isRequired
|
||||
login: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -44,7 +45,7 @@ export default class ForgotPasswordView extends LoggedView {
|
|||
componentDidUpdate() {
|
||||
const { login } = this.props;
|
||||
if (login.success) {
|
||||
this.props.navigation.goBack();
|
||||
this.props.navigator.pop();
|
||||
setTimeout(() => {
|
||||
showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert'));
|
||||
});
|
||||
|
|
|
@ -2,17 +2,16 @@ import React from 'react';
|
|||
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import PropTypes from 'prop-types';
|
||||
// import Zeroconf from 'react-native-zeroconf';
|
||||
import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native';
|
||||
import { View, Text, SectionList, StyleSheet } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { withNavigationFocus } from 'react-navigation';
|
||||
|
||||
import LoggedView from './View';
|
||||
import { setServer } from '../actions/server';
|
||||
import { selectServer } from '../actions/server';
|
||||
import database from '../lib/realm';
|
||||
import Fade from '../animations/fade';
|
||||
import Touch from '../utils/touch';
|
||||
import I18n from '../i18n';
|
||||
import { iconsMap } from '../Icons';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
view: {
|
||||
|
@ -63,22 +62,19 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
// const zeroconf = new Zeroconf();
|
||||
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server.server,
|
||||
login: state.login,
|
||||
connected: state.meteor.connected
|
||||
}), dispatch => ({
|
||||
selectServer: server => dispatch(setServer(server))
|
||||
selectServer: server => dispatch(selectServer(server))
|
||||
}))
|
||||
class ListServerView extends LoggedView {
|
||||
/** @extends React.Component */
|
||||
export default class ListServerView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
login: PropTypes.object.isRequired,
|
||||
selectServer: PropTypes.func.isRequired,
|
||||
connected: PropTypes.bool.isRequired,
|
||||
server: PropTypes.string
|
||||
}
|
||||
|
||||
|
@ -88,51 +84,36 @@ class ListServerView extends LoggedView {
|
|||
sections: []
|
||||
};
|
||||
this.data = database.databases.serversDB.objects('servers');
|
||||
// this.redirected = false;
|
||||
this.data.addListener(this.updateState);
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
this.props.navigator.setButtons({
|
||||
rightButtons: [{
|
||||
id: 'addServer',
|
||||
icon: iconsMap.add
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// zeroconf.on('update', this.updateState);
|
||||
// zeroconf.scan('http', 'tcp', 'local.');
|
||||
this.updateState();
|
||||
this.jumpToSelectedServer();
|
||||
}
|
||||
|
||||
// componentDidUpdate() {
|
||||
// if (this.props.connected &&
|
||||
// this.props.server &&
|
||||
// !this.props.login.token &&
|
||||
// !this.redirected) {
|
||||
// this.redirected = true;
|
||||
// this.props.navigation.navigate({ key: 'LoginSignup', routeName: 'LoginSignup' });
|
||||
// } else if (!this.props.connected) {
|
||||
// this.redirected = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
componentWillUnmount() {
|
||||
// zeroconf.stop();
|
||||
this.data.removeAllListeners();
|
||||
// zeroconf.removeListener('update', this.updateState);
|
||||
}
|
||||
|
||||
openLogin = () => {
|
||||
this.props.navigation.navigate({ key: 'LoginSignup', routeName: 'LoginSignup' });
|
||||
onNavigatorEvent(event) {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'addServer') {
|
||||
this.props.navigator.push({
|
||||
screen: 'NewServerView',
|
||||
title: I18n.t('New_Server')
|
||||
});
|
||||
}
|
||||
|
||||
selectAndNavigateTo = (server) => {
|
||||
this.props.selectServer(server);
|
||||
this.openLogin();
|
||||
}
|
||||
|
||||
jumpToSelectedServer() {
|
||||
if (this.props.server && !this.props.login.isRegistering) {
|
||||
setTimeout(() => {
|
||||
if (this.props.isFocused) {
|
||||
this.openLogin();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,24 +126,6 @@ class ListServerView extends LoggedView {
|
|||
title: I18n.t('My_servers'),
|
||||
data: this.data
|
||||
}];
|
||||
//
|
||||
// this.state.nearBy = zeroconf.getServices();
|
||||
// if (this.state.nearBy) {
|
||||
// const nearBy = Object.keys(this.state.nearBy)
|
||||
// .filter(key => this.state.nearBy[key].addresses);
|
||||
// if (nearBy.length) {
|
||||
// sections.push({
|
||||
// title: 'Nearby',
|
||||
// data: nearBy.map((key) => {
|
||||
// const server = this.state.nearBy[key];
|
||||
// const address = `http://${ server.addresses[0] }:${ server.port }`;
|
||||
// return {
|
||||
// id: address
|
||||
// };
|
||||
// })
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
...this.state,
|
||||
|
@ -170,6 +133,26 @@ class ListServerView extends LoggedView {
|
|||
};
|
||||
};
|
||||
|
||||
openLogin = (server) => {
|
||||
this.props.navigator.push({
|
||||
screen: 'LoginSignupView',
|
||||
title: server
|
||||
});
|
||||
}
|
||||
|
||||
selectAndNavigateTo = (server) => {
|
||||
this.props.selectServer(server);
|
||||
this.openLogin(server);
|
||||
}
|
||||
|
||||
jumpToSelectedServer() {
|
||||
if (this.props.server && !this.props.login.isRegistering) {
|
||||
setTimeout(() => {
|
||||
this.openLogin(this.props.server);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
updateState = () => {
|
||||
this.setState(this.getState());
|
||||
}
|
||||
|
@ -210,7 +193,7 @@ class ListServerView extends LoggedView {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView style={styles.view} testID='list-server-view'>
|
||||
<View style={styles.view} testID='list-server-view'>
|
||||
<SectionList
|
||||
style={styles.list}
|
||||
sections={this.state.sections}
|
||||
|
@ -219,8 +202,7 @@ class ListServerView extends LoggedView {
|
|||
keyExtractor={item => item.id}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default withNavigationFocus(ListServerView);
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text, View, ScrollView, TouchableOpacity, SafeAreaView, WebView, Platform, LayoutAnimation, Image, StyleSheet } from 'react-native';
|
||||
import { Text, View, ScrollView, TouchableOpacity, LayoutAnimation, Image, StyleSheet } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import { Base64 } from 'js-base64';
|
||||
import Modal from 'react-native-modal';
|
||||
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { open, close } from '../actions/login';
|
||||
import LoggedView from './View';
|
||||
import sharedStyles from './Styles';
|
||||
|
@ -17,9 +15,6 @@ import Button from '../containers/Button';
|
|||
import Loading from '../containers/Loading';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const userAgentAndroid = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1';
|
||||
const userAgent = Platform.OS === 'ios' ? 'UserAgent' : userAgentAndroid;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
|
@ -44,14 +39,13 @@ const styles = StyleSheet.create({
|
|||
planetImage: {
|
||||
width: 200,
|
||||
height: 162,
|
||||
marginVertical: 20,
|
||||
opacity: 0.6
|
||||
marginVertical: 20
|
||||
}
|
||||
});
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server.server,
|
||||
login: state.login,
|
||||
isFetching: state.login.isFetching,
|
||||
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
|
||||
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
|
||||
Accounts_OAuth_Facebook: state.settings.Accounts_OAuth_Facebook,
|
||||
|
@ -63,17 +57,16 @@ const styles = StyleSheet.create({
|
|||
Accounts_OAuth_Twitter: state.settings.Accounts_OAuth_Twitter,
|
||||
services: state.login.services
|
||||
}), dispatch => ({
|
||||
loginOAuth: params => RocketChat.login(params),
|
||||
open: () => dispatch(open()),
|
||||
close: () => dispatch(close())
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class LoginSignupView extends LoggedView {
|
||||
static propTypes = {
|
||||
loginOAuth: PropTypes.func.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
open: PropTypes.func.isRequired,
|
||||
close: PropTypes.func.isRequired,
|
||||
navigation: PropTypes.object.isRequired,
|
||||
login: PropTypes.object,
|
||||
isFetching: PropTypes.bool,
|
||||
server: PropTypes.string,
|
||||
Accounts_EmailOrUsernamePlaceholder: PropTypes.bool,
|
||||
Accounts_PasswordPlaceholder: PropTypes.string,
|
||||
|
@ -89,13 +82,6 @@ export default class LoginSignupView extends LoggedView {
|
|||
|
||||
constructor(props) {
|
||||
super('LoginSignupView', props);
|
||||
|
||||
this.state = {
|
||||
modalVisible: false,
|
||||
oAuthUrl: '',
|
||||
showSocialButtons: false
|
||||
};
|
||||
this.redirectRegex = new RegExp(`(?=.*(${ this.props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -183,19 +169,29 @@ export default class LoginSignupView extends LoggedView {
|
|||
}
|
||||
|
||||
openOAuth = (oAuthUrl) => {
|
||||
this.setState({ oAuthUrl, modalVisible: true });
|
||||
this.props.navigator.showModal({
|
||||
screen: 'OAuthView',
|
||||
title: 'OAuth',
|
||||
passProps: {
|
||||
oAuthUrl
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
login = () => {
|
||||
this.props.navigator.push({
|
||||
screen: 'LoginView',
|
||||
title: this.props.server,
|
||||
backButtonTitle: I18n.t('Welcome')
|
||||
});
|
||||
}
|
||||
|
||||
register = () => {
|
||||
this.props.navigation.navigate({ key: 'Register', routeName: 'Register' });
|
||||
}
|
||||
|
||||
closeOAuth = () => {
|
||||
this.setState({ modalVisible: false });
|
||||
}
|
||||
|
||||
toggleSocialButtons = () => {
|
||||
this.setState({ showSocialButtons: !this.state.showSocialButtons });
|
||||
this.props.navigator.push({
|
||||
screen: 'RegisterView',
|
||||
title: this.props.server,
|
||||
backButtonTitle: I18n.t('Welcome')
|
||||
});
|
||||
}
|
||||
|
||||
renderServices = () => {
|
||||
|
@ -279,13 +275,11 @@ export default class LoginSignupView extends LoggedView {
|
|||
|
||||
render() {
|
||||
return (
|
||||
[
|
||||
<ScrollView
|
||||
key='login-view'
|
||||
style={[sharedStyles.container, sharedStyles.containerScrollView]}
|
||||
{...scrollPersistTaps}
|
||||
>
|
||||
<SafeAreaView testID='welcome-view'>
|
||||
<View testID='welcome-view'>
|
||||
<View style={styles.container}>
|
||||
<Image
|
||||
source={require('../static/images/logo.png')}
|
||||
|
@ -301,44 +295,20 @@ export default class LoginSignupView extends LoggedView {
|
|||
<Button
|
||||
title={I18n.t('I_have_an_account')}
|
||||
type='primary'
|
||||
onPress={() => this.props.navigation.navigate({ key: 'Login', routeName: 'Login' })}
|
||||
onPress={() => this.login()}
|
||||
testID='welcome-view-login'
|
||||
/>
|
||||
<Button
|
||||
title={I18n.t('Create_account')}
|
||||
type='secondary'
|
||||
onPress={() => this.props.navigation.navigate({ key: 'Register', routeName: 'Register' })}
|
||||
onPress={() => this.register()}
|
||||
testID='welcome-view-register'
|
||||
/>
|
||||
{this.renderServices()}
|
||||
</View>
|
||||
<Loading visible={this.props.login.isFetching} />
|
||||
</SafeAreaView>
|
||||
</ScrollView>,
|
||||
<Modal
|
||||
key='modal-oauth'
|
||||
visible={this.state.modalVisible}
|
||||
animationType='slide'
|
||||
style={sharedStyles.oAuthModal}
|
||||
onBackButtonPress={this.closeOAuth}
|
||||
useNativeDriver
|
||||
>
|
||||
<WebView
|
||||
source={{ uri: this.state.oAuthUrl }}
|
||||
userAgent={userAgent}
|
||||
onNavigationStateChange={(webViewState) => {
|
||||
const url = decodeURIComponent(webViewState.url);
|
||||
if (this.redirectRegex.test(url)) {
|
||||
const parts = url.split('#');
|
||||
const credentials = JSON.parse(parts[1]);
|
||||
this.props.loginOAuth({ oauth: { ...credentials } });
|
||||
this.setState({ modalVisible: false });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Icon name='close' size={30} style={sharedStyles.closeOAuth} onPress={this.closeOAuth} />
|
||||
</Modal>
|
||||
]
|
||||
<Loading visible={this.props.isFetching} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Keyboard, Text, ScrollView, SafeAreaView, View } from 'react-native';
|
||||
import { Keyboard, Text, ScrollView, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Answers } from 'react-native-fabric';
|
||||
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import KeyboardView from '../presentation/KeyboardView';
|
||||
import TextInput from '../containers/TextInput';
|
||||
import CloseModalButton from '../containers/CloseModalButton';
|
||||
import Button from '../containers/Button';
|
||||
import Loading from '../containers/Loading';
|
||||
import styles from './Styles';
|
||||
|
@ -26,12 +25,19 @@ import I18n from '../i18n';
|
|||
}), () => ({
|
||||
loginSubmit: params => RocketChat.loginWithPassword(params)
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class LoginView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
loginSubmit: PropTypes.func.isRequired,
|
||||
navigation: PropTypes.object.isRequired,
|
||||
login: PropTypes.object,
|
||||
server: PropTypes.string
|
||||
server: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
|
||||
Accounts_PasswordPlaceholder: PropTypes.string,
|
||||
failure: PropTypes.bool,
|
||||
isFetching: PropTypes.bool,
|
||||
reason: PropTypes.string
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -58,6 +64,22 @@ export default class LoginView extends LoggedView {
|
|||
}
|
||||
}
|
||||
|
||||
register = () => {
|
||||
this.props.navigator.push({
|
||||
screen: 'RegisterView',
|
||||
title: this.props.server,
|
||||
backButtonTitle: I18n.t('Login')
|
||||
});
|
||||
}
|
||||
|
||||
forgotPassword = () => {
|
||||
this.props.navigator.push({
|
||||
screen: 'ForgotPasswordView',
|
||||
title: I18n.t('Forgot_Password'),
|
||||
backButtonTitle: I18n.t('Login')
|
||||
});
|
||||
}
|
||||
|
||||
renderTOTP = () => {
|
||||
if (/totp/ig.test(this.props.error)) {
|
||||
return (
|
||||
|
@ -84,8 +106,7 @@ export default class LoginView extends LoggedView {
|
|||
key='login-view'
|
||||
>
|
||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||
<SafeAreaView testID='login-view'>
|
||||
<CloseModalButton navigation={this.props.navigation} />
|
||||
<View testID='login-view'>
|
||||
<Text style={[styles.loginText, styles.loginTitle]}>Login</Text>
|
||||
<TextInput
|
||||
label={I18n.t('Username')}
|
||||
|
@ -122,14 +143,14 @@ export default class LoginView extends LoggedView {
|
|||
<Text
|
||||
style={[styles.loginText, { marginTop: 10 }]}
|
||||
testID='login-view-register'
|
||||
onPress={() => this.props.navigation.navigate('Register')}
|
||||
onPress={() => this.register()}
|
||||
>{I18n.t('New_in_RocketChat_question_mark')}
|
||||
<Text style={{ color: COLOR_BUTTON_PRIMARY }}>{I18n.t('Sign_Up')}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text
|
||||
style={[styles.loginText, { marginTop: 20, fontSize: 13 }]}
|
||||
onPress={() => this.props.navigation.navigate('ForgotPassword')}
|
||||
onPress={() => this.forgotPassword()}
|
||||
testID='login-view-forgot-password'
|
||||
>{I18n.t('Forgot_password')}
|
||||
</Text>
|
||||
|
@ -137,7 +158,7 @@ export default class LoginView extends LoggedView {
|
|||
|
||||
{this.props.failure ? <Text style={styles.error}>{this.props.reason}</Text> : null}
|
||||
<Loading visible={this.props.isFetching} />
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
);
|
||||
|
|
|
@ -10,21 +10,23 @@ import Message from '../../containers/message';
|
|||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
@connect(state => ({
|
||||
messages: state.mentionedMessages.messages,
|
||||
ready: state.mentionedMessages.ready,
|
||||
user: state.login.user,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
},
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}),
|
||||
dispatch => ({
|
||||
}), dispatch => ({
|
||||
openMentionedMessages: (rid, limit) => dispatch(openMentionedMessages(rid, limit)),
|
||||
closeMentionedMessages: () => dispatch(closeMentionedMessages())
|
||||
})
|
||||
)
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class MentionedMessagesView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
|
@ -57,7 +59,7 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
}
|
||||
|
||||
load = () => {
|
||||
this.props.openMentionedMessages(this.props.navigation.state.params.rid, this.limit);
|
||||
this.props.openMentionedMessages(this.props.rid, this.limit);
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text, ScrollView, View, SafeAreaView, Keyboard } from 'react-native';
|
||||
import { Text, ScrollView, View, Keyboard } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { serverRequest, addServer } from '../actions/server';
|
||||
|
@ -21,14 +21,15 @@ import I18n from '../i18n';
|
|||
validateServer: url => dispatch(serverRequest(url)),
|
||||
addServer: url => dispatch(addServer(url))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class NewServerView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
validateServer: PropTypes.func.isRequired,
|
||||
addServer: PropTypes.func.isRequired,
|
||||
validating: PropTypes.bool.isRequired,
|
||||
validInstance: PropTypes.bool.isRequired,
|
||||
addingServer: PropTypes.bool.isRequired,
|
||||
navigation: PropTypes.object.isRequired
|
||||
addingServer: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -36,11 +37,7 @@ export default class NewServerView extends LoggedView {
|
|||
this.state = {
|
||||
defaultServer: 'https://open.rocket.chat'
|
||||
};
|
||||
this.props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.input.focus();
|
||||
props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field
|
||||
}
|
||||
|
||||
onChangeText = (text) => {
|
||||
|
@ -109,7 +106,7 @@ export default class NewServerView extends LoggedView {
|
|||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||
<SafeAreaView testID='new-server-view'>
|
||||
<View testID='new-server-view'>
|
||||
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_in_your_server')}</Text>
|
||||
<TextInput
|
||||
inputRef={e => this.input = e}
|
||||
|
@ -132,7 +129,7 @@ export default class NewServerView extends LoggedView {
|
|||
/>
|
||||
</View>
|
||||
<Loading visible={this.props.addingServer} />
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { WebView, Platform } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const userAgentAndroid = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1';
|
||||
const userAgent = Platform.OS === 'ios' ? 'UserAgent' : userAgentAndroid;
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server.server
|
||||
}))
|
||||
export default class TermsServiceView extends React.PureComponent {
|
||||
static navigatorButtons = {
|
||||
leftButtons: [{
|
||||
id: 'close',
|
||||
title: I18n.t('Close')
|
||||
}]
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
oAuthUrl: PropTypes.string,
|
||||
server: PropTypes.string
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.redirectRegex = new RegExp(`(?=.*(${ props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||
}
|
||||
|
||||
onNavigatorEvent(event) {
|
||||
const { navigator } = this.props;
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'close') {
|
||||
navigator.dismissModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
login = async(params) => {
|
||||
try {
|
||||
await RocketChat.login(params);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<WebView
|
||||
source={{ uri: this.props.oAuthUrl }}
|
||||
userAgent={userAgent}
|
||||
onNavigationStateChange={(webViewState) => {
|
||||
const url = decodeURIComponent(webViewState.url);
|
||||
if (this.redirectRegex.test(url)) {
|
||||
const parts = url.split('#');
|
||||
const credentials = JSON.parse(parts[1]);
|
||||
this.login({ oauth: { ...credentials } });
|
||||
this.props.navigator.dismissModal();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,22 +16,24 @@ const PIN_INDEX = 0;
|
|||
const CANCEL_INDEX = 1;
|
||||
const options = [I18n.t('Unpin'), I18n.t('Cancel')];
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
@connect(state => ({
|
||||
messages: state.pinnedMessages.messages,
|
||||
ready: state.pinnedMessages.ready,
|
||||
user: state.login.user,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
},
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}),
|
||||
dispatch => ({
|
||||
}), dispatch => ({
|
||||
openPinnedMessages: (rid, limit) => dispatch(openPinnedMessages(rid, limit)),
|
||||
closePinnedMessages: () => dispatch(closePinnedMessages()),
|
||||
togglePinRequest: message => dispatch(togglePinRequest(message))
|
||||
})
|
||||
)
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class PinnedMessagesView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
|
@ -81,7 +83,7 @@ export default class PinnedMessagesView extends LoggedView {
|
|||
}
|
||||
|
||||
load = () => {
|
||||
this.props.openPinnedMessages(this.props.navigation.state.params.rid, this.limit);
|
||||
this.props.openPinnedMessages(this.props.rid, this.limit);
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
|
|
|
@ -3,7 +3,10 @@ import PropTypes from 'prop-types';
|
|||
import { WebView } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
class PrivacyPolicyView extends React.PureComponent {
|
||||
@connect(state => ({
|
||||
privacyPolicy: state.settings.Layout_Privacy_Policy
|
||||
}))
|
||||
export default class PrivacyPolicyView extends React.PureComponent {
|
||||
static propTypes = {
|
||||
privacyPolicy: PropTypes.string
|
||||
}
|
||||
|
@ -14,11 +17,3 @@ class PrivacyPolicyView extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
privacyPolicy: state.settings.Layout_Privacy_Policy
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(PrivacyPolicyView);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, ScrollView, SafeAreaView, Keyboard } from 'react-native';
|
||||
import { View, ScrollView, SafeAreaView, Keyboard, Platform } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import Dialog from 'react-native-dialog';
|
||||
import SHA256 from 'js-sha256';
|
||||
|
@ -22,17 +22,24 @@ import I18n from '../../i18n';
|
|||
import Button from '../../containers/Button';
|
||||
import Avatar from '../../containers/Avatar';
|
||||
import Touch from '../../utils/touch';
|
||||
import { iconsMap } from '../../Icons';
|
||||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
user: {
|
||||
name: state.login.user && state.login.user.name,
|
||||
username: state.login.user && state.login.user.username,
|
||||
customFields: state.login.user && state.login.user.customFields,
|
||||
emails: state.login.user && state.login.user.emails
|
||||
},
|
||||
Accounts_CustomFields: state.settings.Accounts_CustomFields
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class ProfileView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
navigator: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
Accounts_CustomFields: PropTypes.string
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('ProfileView', props);
|
||||
|
@ -49,11 +56,26 @@ export default class ProfileView extends LoggedView {
|
|||
avatarSuggestions: {},
|
||||
customFields: {}
|
||||
};
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.navigator.setButtons({
|
||||
leftButtons: [{
|
||||
id: 'sideMenu',
|
||||
icon: Platform.OS === 'ios' ? iconsMap.menu : undefined
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
this.init();
|
||||
|
||||
this.props.navigator.setDrawerEnabled({
|
||||
side: 'left',
|
||||
enabled: true
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await RocketChat.getAvatarSuggestion();
|
||||
this.setState({ avatarSuggestions: result });
|
||||
|
@ -68,6 +90,22 @@ export default class ProfileView extends LoggedView {
|
|||
}
|
||||
}
|
||||
|
||||
onNavigatorEvent(event) {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'sideMenu' && Platform.OS === 'ios') {
|
||||
this.props.navigator.toggleDrawer({
|
||||
side: 'left',
|
||||
animated: true,
|
||||
to: 'missing'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setAvatar = (avatar) => {
|
||||
this.setState({ avatar });
|
||||
}
|
||||
|
||||
init = (user) => {
|
||||
const {
|
||||
name, username, emails, customFields
|
||||
|
@ -195,10 +233,6 @@ export default class ProfileView extends LoggedView {
|
|||
}
|
||||
}
|
||||
|
||||
setAvatar = (avatar) => {
|
||||
this.setState({ avatar });
|
||||
}
|
||||
|
||||
resetAvatar = async() => {
|
||||
try {
|
||||
await RocketChat.resetAvatar();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Keyboard, Text, View, SafeAreaView, ScrollView } from 'react-native';
|
||||
import { Keyboard, Text, View, ScrollView } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { registerSubmit, setUsernameSubmit } from '../actions/login';
|
||||
|
@ -10,7 +10,6 @@ import Loading from '../containers/Loading';
|
|||
import KeyboardView from '../presentation/KeyboardView';
|
||||
import styles from './Styles';
|
||||
import { showToast } from '../utils/info';
|
||||
import CloseModalButton from '../containers/CloseModalButton';
|
||||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||
import LoggedView from './View';
|
||||
import I18n from '../i18n';
|
||||
|
@ -26,8 +25,11 @@ import I18n from '../i18n';
|
|||
registerSubmit: params => dispatch(registerSubmit(params)),
|
||||
setUsernameSubmit: params => dispatch(setUsernameSubmit(params))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class RegisterView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
server: PropTypes.string,
|
||||
registerSubmit: PropTypes.func.isRequired,
|
||||
setUsernameSubmit: PropTypes.func,
|
||||
Accounts_UsernamePlaceholder: PropTypes.string,
|
||||
|
@ -88,11 +90,19 @@ export default class RegisterView extends LoggedView {
|
|||
}
|
||||
|
||||
termsService = () => {
|
||||
this.props.navigation.navigate({ key: 'TermsService', routeName: 'TermsService' });
|
||||
this.props.navigator.push({
|
||||
screen: 'TermsServiceView',
|
||||
title: I18n.t('Terms_of_Service'),
|
||||
backButtonTitle: I18n.t('Sign_Up')
|
||||
});
|
||||
}
|
||||
|
||||
privacyPolicy = () => {
|
||||
this.props.navigation.navigate({ key: 'PrivacyPolicy', routeName: 'PrivacyPolicy' });
|
||||
this.props.navigator.push({
|
||||
screen: 'PrivacyPolicyView',
|
||||
title: I18n.t('Privacy_Policy'),
|
||||
backButtonTitle: I18n.t('Sign_Up')
|
||||
});
|
||||
}
|
||||
|
||||
_renderRegister() {
|
||||
|
@ -202,8 +212,7 @@ export default class RegisterView extends LoggedView {
|
|||
return (
|
||||
<KeyboardView contentContainerStyle={styles.container}>
|
||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||
<SafeAreaView testID='register-view'>
|
||||
<CloseModalButton navigation={this.props.navigation} />
|
||||
<View testID='register-view'>
|
||||
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_Up')}</Text>
|
||||
{this._renderRegister()}
|
||||
{this._renderUsername()}
|
||||
|
@ -214,7 +223,7 @@ export default class RegisterView extends LoggedView {
|
|||
: null
|
||||
}
|
||||
<Loading visible={this.props.login.isFetching} />
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
);
|
||||
|
|
|
@ -14,7 +14,6 @@ import Touch from '../../utils/touch';
|
|||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import { leaveRoom } from '../../actions/room';
|
||||
import { setLoading } from '../../actions/selectedUsers';
|
||||
import log from '../../utils/log';
|
||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||
import I18n from '../../i18n';
|
||||
|
@ -23,24 +22,24 @@ const renderSeparator = () => <View style={styles.separator} />;
|
|||
const getRoomTitle = room => (room.t === 'd' ? <Text>{room.fname}</Text> : <Text><RoomTypeIcon type={room.t} /> {room.name}</Text>);
|
||||
|
||||
@connect(state => ({
|
||||
user_id: state.login.user.id,
|
||||
user_username: state.login.user.username
|
||||
userId: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username
|
||||
}), dispatch => ({
|
||||
leaveRoom: rid => dispatch(leaveRoom(rid)),
|
||||
setLoadingInvite: loading => dispatch(setLoading(loading))
|
||||
leaveRoom: rid => dispatch(leaveRoom(rid))
|
||||
}))
|
||||
|
||||
/** @extends React.Component */
|
||||
export default class RoomActionsView extends LoggedView {
|
||||
static propTypes = {
|
||||
baseUrl: PropTypes.string,
|
||||
user: PropTypes.object,
|
||||
navigation: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
navigator: PropTypes.object,
|
||||
userId: PropTypes.string,
|
||||
username: PropTypes.string,
|
||||
leaveRoom: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('RoomActionsView', props);
|
||||
const { rid } = props.navigation.state.params;
|
||||
const { rid } = props;
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
[this.room] = this.rooms;
|
||||
this.state = {
|
||||
|
@ -63,59 +62,24 @@ export default class RoomActionsView extends LoggedView {
|
|||
|
||||
onPressTouchable = (item) => {
|
||||
if (item.route) {
|
||||
return this.props.navigation.navigate({ key: item.route, routeName: item.route, params: item.params });
|
||||
this.props.navigator.push({
|
||||
screen: item.route,
|
||||
title: item.name,
|
||||
passProps: item.params
|
||||
});
|
||||
}
|
||||
if (item.event) {
|
||||
return item.event();
|
||||
}
|
||||
}
|
||||
|
||||
updateRoomMembers = async() => {
|
||||
const { t } = this.state.room;
|
||||
|
||||
if (!this.canViewMembers) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (t === 'c' || t === 'p') {
|
||||
let onlineMembers = [];
|
||||
let allMembers = [];
|
||||
try {
|
||||
const onlineMembersCall = RocketChat.getRoomMembers(this.state.room.rid, false);
|
||||
const allMembersCall = RocketChat.getRoomMembers(this.state.room.rid, true);
|
||||
const [onlineMembersResult, allMembersResult] = await Promise.all([onlineMembersCall, allMembersCall]);
|
||||
onlineMembers = onlineMembersResult.records;
|
||||
allMembers = allMembersResult.records;
|
||||
return { onlineMembers, allMembers };
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRoomMember = async() => {
|
||||
if (this.state.room.t !== 'd') {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const member = await RocketChat.getRoomMember(this.state.room.rid, this.props.user_id);
|
||||
return { member };
|
||||
} catch (e) {
|
||||
log('RoomActions updateRoomMember', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
updateRoom = () => {
|
||||
this.setState({ room: this.room });
|
||||
}
|
||||
get canAddUser() { // Invite user
|
||||
const {
|
||||
rid, t
|
||||
} = this.room;
|
||||
const { allMembers } = this.state;
|
||||
// TODO: same test joined
|
||||
const userInRoom = !!allMembers.find(m => m.username === this.props.user_username);
|
||||
const userInRoom = !!allMembers.find(m => m.username === this.props.username);
|
||||
const permissions = RocketChat.hasPermission(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], rid);
|
||||
|
||||
if (userInRoom && permissions['add-user-to-joined-room']) {
|
||||
|
@ -149,8 +113,8 @@ export default class RoomActionsView extends LoggedView {
|
|||
const sections = [{
|
||||
data: [{
|
||||
icon: 'ios-star',
|
||||
name: 'USER',
|
||||
route: 'RoomInfo',
|
||||
name: I18n.t('Room_Info'),
|
||||
route: 'RoomInfoView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-info'
|
||||
}],
|
||||
|
@ -176,28 +140,28 @@ export default class RoomActionsView extends LoggedView {
|
|||
{
|
||||
icon: 'ios-attach',
|
||||
name: I18n.t('Files'),
|
||||
route: 'RoomFiles',
|
||||
route: 'RoomFilesView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-files'
|
||||
},
|
||||
{
|
||||
icon: 'ios-at-outline',
|
||||
name: I18n.t('Mentions'),
|
||||
route: 'MentionedMessages',
|
||||
route: 'MentionedMessagesView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-mentioned'
|
||||
},
|
||||
{
|
||||
icon: 'ios-star-outline',
|
||||
name: I18n.t('Starred'),
|
||||
route: 'StarredMessages',
|
||||
route: 'StarredMessagesView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-starred'
|
||||
},
|
||||
{
|
||||
icon: 'ios-search',
|
||||
name: I18n.t('Search'),
|
||||
route: 'SearchMessages',
|
||||
route: 'SearchMessagesView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-search'
|
||||
},
|
||||
|
@ -210,14 +174,14 @@ export default class RoomActionsView extends LoggedView {
|
|||
{
|
||||
icon: 'ios-pin',
|
||||
name: I18n.t('Pinned'),
|
||||
route: 'PinnedMessages',
|
||||
route: 'PinnedMessagesView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-pinned'
|
||||
},
|
||||
{
|
||||
icon: 'ios-code',
|
||||
name: I18n.t('Snippets'),
|
||||
route: 'SnippetedMessages',
|
||||
route: 'SnippetedMessagesView',
|
||||
params: { rid },
|
||||
testID: 'room-actions-snippeted'
|
||||
},
|
||||
|
@ -254,7 +218,7 @@ export default class RoomActionsView extends LoggedView {
|
|||
description: (onlineMembers.length === 1 ?
|
||||
I18n.t('1_online_member') :
|
||||
I18n.t('N_online_members', { n: onlineMembers.length })),
|
||||
route: 'RoomMembers',
|
||||
route: 'RoomMembersView',
|
||||
params: { rid, members: onlineMembers },
|
||||
testID: 'room-actions-members'
|
||||
});
|
||||
|
@ -264,19 +228,10 @@ export default class RoomActionsView extends LoggedView {
|
|||
actions.push({
|
||||
icon: 'ios-person-add',
|
||||
name: I18n.t('Add_user'),
|
||||
route: 'SelectedUsers',
|
||||
route: 'SelectedUsersView',
|
||||
params: {
|
||||
nextAction: async() => {
|
||||
try {
|
||||
this.props.setLoadingInvite(true);
|
||||
await RocketChat.addUsersToRoom(rid);
|
||||
this.props.navigation.goBack();
|
||||
} catch (e) {
|
||||
log('RoomActions Add User', e);
|
||||
} finally {
|
||||
this.props.setLoadingInvite(false);
|
||||
}
|
||||
}
|
||||
nextAction: 'ADD_USER',
|
||||
rid
|
||||
},
|
||||
testID: 'room-actions-add-user'
|
||||
});
|
||||
|
@ -298,6 +253,46 @@ export default class RoomActionsView extends LoggedView {
|
|||
return sections;
|
||||
}
|
||||
|
||||
updateRoomMembers = async() => {
|
||||
const { t } = this.state.room;
|
||||
|
||||
if (!this.canViewMembers) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (t === 'c' || t === 'p') {
|
||||
let onlineMembers = [];
|
||||
let allMembers = [];
|
||||
try {
|
||||
const onlineMembersCall = RocketChat.getRoomMembers(this.state.room.rid, false);
|
||||
const allMembersCall = RocketChat.getRoomMembers(this.state.room.rid, true);
|
||||
const [onlineMembersResult, allMembersResult] = await Promise.all([onlineMembersCall, allMembersCall]);
|
||||
onlineMembers = onlineMembersResult.records;
|
||||
allMembers = allMembersResult.records;
|
||||
return { onlineMembers, allMembers };
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRoomMember = async() => {
|
||||
if (this.state.room.t !== 'd') {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const member = await RocketChat.getRoomMember(this.state.room.rid, this.props.userId);
|
||||
return { member };
|
||||
} catch (e) {
|
||||
log('RoomActions updateRoomMember', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
updateRoom = () => {
|
||||
this.setState({ room: this.room });
|
||||
}
|
||||
|
||||
toggleBlockUser = async() => {
|
||||
const { rid, blocker } = this.state.room;
|
||||
const { member } = this.state;
|
||||
|
|
|
@ -10,25 +10,25 @@ import Message from '../../containers/message';
|
|||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
@connect(state => ({
|
||||
messages: state.roomFiles.messages,
|
||||
ready: state.roomFiles.ready,
|
||||
user: state.login.user
|
||||
// baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}),
|
||||
dispatch => ({
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
}
|
||||
}), dispatch => ({
|
||||
openRoomFiles: (rid, limit) => dispatch(openRoomFiles(rid, limit)),
|
||||
closeRoomFiles: () => dispatch(closeRoomFiles())
|
||||
})
|
||||
)
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class RoomFilesView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
baseUrl: PropTypes.string,
|
||||
openRoomFiles: PropTypes.func,
|
||||
closeRoomFiles: PropTypes.func
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export default class RoomFilesView extends LoggedView {
|
|||
}
|
||||
|
||||
load = () => {
|
||||
this.props.openRoomFiles(this.props.navigation.state.params.rid, this.limit);
|
||||
this.props.openRoomFiles(this.props.rid, this.limit);
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
|
|
|
@ -34,18 +34,16 @@ const PERMISSIONS_ARRAY = [
|
|||
PERMISSION_DELETE_P
|
||||
];
|
||||
|
||||
@connect(null, dispatch => ({
|
||||
eraseRoom: rid => dispatch(eraseRoom(rid))
|
||||
}))
|
||||
export default class RoomInfoEditView extends LoggedView {
|
||||
/** @extends React.Component */
|
||||
class RoomInfoEditView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
eraseRoom: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super('RoomInfoEditView', props);
|
||||
const { rid } = props.navigation.state.params;
|
||||
const { rid } = props;
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
this.permissions = {};
|
||||
this.state = {
|
||||
|
@ -400,3 +398,9 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
eraseRoom: rid => dispatch(eraseRoom(rid))
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(RoomInfoEditView);
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { View, Text, ScrollView } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
import moment from 'moment';
|
||||
|
||||
import LoggedView from '../View';
|
||||
|
@ -12,11 +11,11 @@ import styles from './styles';
|
|||
import sharedStyles from '../Styles';
|
||||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import Touch from '../../utils/touch';
|
||||
|
||||
import log from '../../utils/log';
|
||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||
import I18n from '../../i18n';
|
||||
import { iconsMap } from '../../Icons';
|
||||
|
||||
const PERMISSION_EDIT_ROOM = 'edit-room';
|
||||
|
||||
|
@ -28,49 +27,28 @@ const getRoomTitle = room => (room.t === 'd' ?
|
|||
<Text testID='room-info-view-name' style={styles.roomTitle} key='room-info-name'>{room.name}</Text>
|
||||
]
|
||||
);
|
||||
|
||||
@connect(state => ({
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
user: state.login.user,
|
||||
userId: state.login.user && state.login.user.id,
|
||||
activeUsers: state.activeUsers,
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
roles: state.roles
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class RoomInfoView extends LoggedView {
|
||||
static propTypes = {
|
||||
baseUrl: PropTypes.string,
|
||||
user: PropTypes.object,
|
||||
navigation: PropTypes.object,
|
||||
navigator: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
userId: PropTypes.string,
|
||||
activeUsers: PropTypes.object,
|
||||
Message_TimeFormat: PropTypes.string,
|
||||
roles: PropTypes.object
|
||||
}
|
||||
|
||||
static navigationOptions = ({ navigation }) => {
|
||||
const params = navigation.state.params || {};
|
||||
if (!params.hasEditPermission) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
headerRight: (
|
||||
<Touch
|
||||
onPress={() => navigation.navigate({ key: 'RoomInfoEdit', routeName: 'RoomInfoEdit', params: { rid: navigation.state.params.rid } })}
|
||||
underlayColor='#ffffff'
|
||||
activeOpacity={0.5}
|
||||
accessibilityLabel={I18n.t('edit')}
|
||||
accessibilityTraits='button'
|
||||
testID='room-info-view-edit-button'
|
||||
>
|
||||
<View style={sharedStyles.headerButton}>
|
||||
<MaterialIcon name='edit' size={20} />
|
||||
</View>
|
||||
</Touch>
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super('RoomInfoView', props);
|
||||
const { rid } = props.navigation.state.params;
|
||||
const { rid } = props;
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
this.sub = {
|
||||
unsubscribe: () => {}
|
||||
|
@ -80,6 +58,7 @@ export default class RoomInfoView extends LoggedView {
|
|||
roomUser: {},
|
||||
roles: []
|
||||
};
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
|
@ -87,9 +66,10 @@ export default class RoomInfoView extends LoggedView {
|
|||
this.rooms.addListener(this.updateRoom);
|
||||
|
||||
// get user of room
|
||||
if (this.state.room) {
|
||||
if (this.state.room.t === 'd') {
|
||||
try {
|
||||
const roomUser = await RocketChat.getRoomMember(this.state.room.rid, this.props.user.id);
|
||||
const roomUser = await RocketChat.getRoomMember(this.state.room.rid, this.props.userId);
|
||||
this.setState({ roomUser });
|
||||
const username = this.state.room.name;
|
||||
|
||||
|
@ -112,7 +92,16 @@ export default class RoomInfoView extends LoggedView {
|
|||
}
|
||||
} else {
|
||||
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], this.state.room.rid);
|
||||
this.props.navigation.setParams({ hasEditPermission: permissions[PERMISSION_EDIT_ROOM] });
|
||||
if (permissions[PERMISSION_EDIT_ROOM]) {
|
||||
this.props.navigator.setButtons({
|
||||
rightButtons: [{
|
||||
id: 'edit',
|
||||
icon: iconsMap.create,
|
||||
testID: 'room-info-view-edit-button'
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +110,20 @@ export default class RoomInfoView extends LoggedView {
|
|||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
onNavigatorEvent(event) {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'edit') {
|
||||
this.props.navigator.push({
|
||||
screen: 'RoomInfoEditView',
|
||||
title: I18n.t('Room_Info_Edit'),
|
||||
passProps: {
|
||||
rid: this.props.rid
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getFullUserData = async(username) => {
|
||||
try {
|
||||
const result = await RocketChat.subscribe('fullUserData', username);
|
||||
|
@ -170,7 +173,6 @@ export default class RoomInfoView extends LoggedView {
|
|||
if (!utcOffset) {
|
||||
return null;
|
||||
}
|
||||
// TODO: translate
|
||||
return (
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.itemLabel}>{I18n.t('Timezone')}</Text>
|
||||
|
|
|
@ -1,57 +1,46 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList, Text, View, TextInput, Vibration, TouchableOpacity } from 'react-native';
|
||||
import { FlatList, View, TextInput, Vibration } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import ActionSheet from 'react-native-actionsheet';
|
||||
|
||||
import LoggedView from '../View';
|
||||
import styles from './styles';
|
||||
import sharedStyles from '../Styles';
|
||||
import RoomItem from '../../presentation/RoomItem';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import { goRoom } from '../../containers/routes/NavigationService';
|
||||
import database from '../../lib/realm';
|
||||
import { showToast } from '../../utils/info';
|
||||
import log from '../../utils/log';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}))
|
||||
export default class MentionedMessagesView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object
|
||||
}
|
||||
/** @extends React.Component */
|
||||
export default class RoomMembersView extends LoggedView {
|
||||
static navigatorButtons = {
|
||||
rightButtons: [{
|
||||
title: 'All',
|
||||
id: 'toggleOnline',
|
||||
testID: 'room-members-view-toggle-status'
|
||||
}]
|
||||
};
|
||||
|
||||
static navigationOptions = ({ navigation }) => {
|
||||
const params = navigation.state.params || {};
|
||||
const label = params.allUsers ? I18n.t('All') : I18n.t('Online');
|
||||
if (params.allUsers === undefined) {
|
||||
return;
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object,
|
||||
rid: PropTypes.string,
|
||||
members: PropTypes.array,
|
||||
baseUrl: PropTypes.string
|
||||
}
|
||||
return {
|
||||
headerRight: (
|
||||
<TouchableOpacity
|
||||
onPress={params.onPressToogleStatus}
|
||||
accessibilityLabel={label}
|
||||
accessibilityTraits='button'
|
||||
style={[sharedStyles.headerButton, styles.headerButton]}
|
||||
testID='room-members-view-toggle-status'
|
||||
>
|
||||
<Text>{label}</Text>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super('MentionedMessagesView', props);
|
||||
this.CANCEL_INDEX = 0;
|
||||
this.MUTE_INDEX = 1;
|
||||
this.actionSheetOptions = [''];
|
||||
const { rid, members } = props.navigation.state.params;
|
||||
const { rid, members } = props;
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
this.permissions = RocketChat.hasPermission(['mute-user'], rid);
|
||||
this.state = {
|
||||
|
@ -63,13 +52,10 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
userLongPressed: {},
|
||||
room: {}
|
||||
};
|
||||
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.navigation.setParams({
|
||||
onPressToogleStatus: this.onPressToogleStatus,
|
||||
allUsers: this.state.allUsers
|
||||
});
|
||||
this.rooms.addListener(this.updateRoom);
|
||||
}
|
||||
|
||||
|
@ -77,9 +63,26 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
this.rooms.removeAllListeners();
|
||||
}
|
||||
|
||||
updateRoom = async() => {
|
||||
const [room] = this.rooms;
|
||||
await this.setState({ room });
|
||||
async onNavigatorEvent(event) {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'toggleOnline') {
|
||||
try {
|
||||
const allUsers = !this.state.allUsers;
|
||||
const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers);
|
||||
const members = membersResult.records;
|
||||
this.setState({ allUsers, members });
|
||||
this.props.navigator.setButtons({
|
||||
rightButtons: [{
|
||||
title: this.state.allUsers ? 'Online' : 'All',
|
||||
id: 'toggleOnline',
|
||||
testID: 'room-members-view-toggle-status'
|
||||
}]
|
||||
});
|
||||
} catch (e) {
|
||||
log('RoomMembers.onNavigationButtonPressed', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSearchChangeText = (text) => {
|
||||
|
@ -90,26 +93,14 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
this.setState({ filtering: !!text, membersFiltered });
|
||||
}
|
||||
|
||||
onPressToogleStatus = async() => {
|
||||
try {
|
||||
const allUsers = !this.state.allUsers;
|
||||
this.props.navigation.setParams({ allUsers });
|
||||
const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers);
|
||||
const members = membersResult.records;
|
||||
this.setState({ allUsers, members });
|
||||
} catch (e) {
|
||||
log('onPressToogleStatus', e);
|
||||
}
|
||||
}
|
||||
|
||||
onPressUser = async(item) => {
|
||||
try {
|
||||
const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username);
|
||||
if (subscriptions.length) {
|
||||
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||
this.goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||
} else {
|
||||
const room = await RocketChat.createDirectMessage(item.username);
|
||||
goRoom({ rid: room.rid, name: item.username });
|
||||
this.goRoom({ rid: room.rid, name: item.username });
|
||||
}
|
||||
} catch (e) {
|
||||
log('onPressUser', e);
|
||||
|
@ -134,6 +125,26 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
this.ActionSheet.show();
|
||||
}
|
||||
|
||||
updateRoom = async() => {
|
||||
const [room] = this.rooms;
|
||||
await this.setState({ room });
|
||||
}
|
||||
|
||||
goRoom = ({ rid, name }) => {
|
||||
this.props.navigator.popToRoot();
|
||||
setTimeout(() => {
|
||||
this.props.navigator.push({
|
||||
screen: 'RoomView',
|
||||
title: name,
|
||||
passProps: {
|
||||
room: { rid, name },
|
||||
rid,
|
||||
name
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
handleMute = async() => {
|
||||
const { rid, userLongPressed } = this.state;
|
||||
try {
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Text, View, Platform, TouchableOpacity } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { HeaderBackButton } from 'react-navigation';
|
||||
|
||||
import RocketChat from '../../../lib/rocketchat';
|
||||
import realm from '../../../lib/realm';
|
||||
import Avatar from '../../../containers/Avatar';
|
||||
import { STATUS_COLORS } from '../../../constants/colors';
|
||||
import styles from './styles';
|
||||
import { closeRoom } from '../../../actions/room';
|
||||
import log from '../../../utils/log';
|
||||
import RoomTypeIcon from '../../../containers/RoomTypeIcon';
|
||||
import I18n from '../../../i18n';
|
||||
import sharedStyles from '../../Styles';
|
||||
|
||||
const title = (offline, connecting, authenticating, logged) => {
|
||||
if (offline) {
|
||||
return `${ I18n.t('You_are_offline') }...`;
|
||||
}
|
||||
|
||||
if (connecting) {
|
||||
return `${ I18n.t('Connecting') }...`;
|
||||
}
|
||||
|
||||
if (authenticating) {
|
||||
return `${ I18n.t('Authenticating') }...`;
|
||||
}
|
||||
|
||||
if (logged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${ I18n.t('Not_logged') }...`;
|
||||
};
|
||||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
activeUsers: state.activeUsers,
|
||||
loading: state.messages.isFetching,
|
||||
connecting: state.meteor.connecting,
|
||||
authenticating: state.login.isFetching,
|
||||
offline: !state.meteor.connected,
|
||||
logged: !!state.login.token
|
||||
}), dispatch => ({
|
||||
close: () => dispatch(closeRoom())
|
||||
}))
|
||||
export default class RoomHeaderView extends React.PureComponent {
|
||||
static propTypes = {
|
||||
close: PropTypes.func.isRequired,
|
||||
navigation: PropTypes.object.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
activeUsers: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
room: props.navigation.state.params.room
|
||||
};
|
||||
this.room = realm.objects('subscriptions').filtered('rid = $0', this.state.room.rid);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateState();
|
||||
this.room.addListener(this.updateState);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.navigation.state.params.room !== this.props.navigation.state.params.room) {
|
||||
this.room.removeAllListeners();
|
||||
this.room = realm.objects('subscriptions').filtered('rid = $0', nextProps.navigation.state.params.room.rid);
|
||||
this.room.addListener(this.updateState);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.room.removeAllListeners();
|
||||
}
|
||||
|
||||
getUserStatus() {
|
||||
const userId = this.state.room.rid.replace(this.props.user.id, '').trim();
|
||||
const userInfo = this.props.activeUsers[userId];
|
||||
return (userInfo && userInfo.status) || 'offline';
|
||||
}
|
||||
|
||||
getUserStatusLabel() {
|
||||
const status = this.getUserStatus();
|
||||
return I18n.t(status.charAt(0).toUpperCase() + status.slice(1));
|
||||
}
|
||||
|
||||
updateState = () => {
|
||||
if (this.room.length > 0) {
|
||||
this.setState({ room: this.room[0] });
|
||||
}
|
||||
};
|
||||
|
||||
isDirect = () => this.state.room && this.state.room.t === 'd';
|
||||
|
||||
renderLeft = () => (<HeaderBackButton
|
||||
onPress={() => {
|
||||
this.props.navigation.goBack(null);
|
||||
requestAnimationFrame(() => this.props.close());
|
||||
}}
|
||||
tintColor='#292E35'
|
||||
title={I18n.t('Back')}
|
||||
titleStyle={{ display: 'none' }}
|
||||
/>);
|
||||
|
||||
renderCenter() {
|
||||
if (!this.state.room.name) {
|
||||
return <View style={styles.titleContainer} />;
|
||||
}
|
||||
|
||||
let accessibilityLabel = this.state.room.name;
|
||||
|
||||
if (this.isDirect()) {
|
||||
accessibilityLabel += `, ${ this.getUserStatusLabel() }`;
|
||||
}
|
||||
const {
|
||||
offline, connecting, authenticating, logged, loading
|
||||
} = this.props;
|
||||
|
||||
let t = '';
|
||||
if (!title(offline, connecting, authenticating, logged) && loading) {
|
||||
t = I18n.t('Loading_messages_ellipsis');
|
||||
} else if (this.isDirect()) {
|
||||
t = this.getUserStatusLabel();
|
||||
} else {
|
||||
t = this.state.room.topic || ' ';
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.titleContainer}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityTraits='header'
|
||||
onPress={() => this.props.navigation.navigate({ key: 'RoomInfo', routeName: 'RoomInfo', params: { rid: this.state.room.rid } })}
|
||||
testID='room-view-header-title'
|
||||
>
|
||||
|
||||
<Avatar
|
||||
text={this.state.room.name}
|
||||
size={24}
|
||||
style={styles.avatar}
|
||||
type={this.state.room.t}
|
||||
>
|
||||
{this.isDirect() ?
|
||||
<View style={[styles.status, { backgroundColor: STATUS_COLORS[this.getUserStatus()] }]} />
|
||||
: null
|
||||
}
|
||||
</Avatar>
|
||||
<View style={styles.titleTextContainer}>
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<RoomTypeIcon type={this.state.room.t} size={13} />
|
||||
<Text style={styles.title} allowFontScaling={false} testID='room-view-title'>
|
||||
{this.state.room.name}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{ t ? <Text style={styles.userStatus} allowFontScaling={false} numberOfLines={1}>{t}</Text> : null}
|
||||
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
renderRight = () => (
|
||||
<View style={styles.right}>
|
||||
<TouchableOpacity
|
||||
style={sharedStyles.headerButton}
|
||||
onPress={() => {
|
||||
try {
|
||||
RocketChat.toggleFavorite(this.state.room.rid, this.state.room.f);
|
||||
} catch (e) {
|
||||
log('toggleFavorite', e);
|
||||
}
|
||||
}}
|
||||
accessibilityLabel={I18n.t('Star_room')}
|
||||
accessibilityTraits='button'
|
||||
testID='room-view-header-star'
|
||||
>
|
||||
<Icon
|
||||
name={`${ Platform.OS === 'ios' ? 'ios' : 'md' }-star${ this.state.room.f ? '' : '-outline' }`}
|
||||
color='#f6c502'
|
||||
size={24}
|
||||
backgroundColor='transparent'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={sharedStyles.headerButton}
|
||||
onPress={() => this.props.navigation.navigate({ key: 'RoomActions', routeName: 'RoomActions', params: { rid: this.state.room.rid } })}
|
||||
accessibilityLabel={I18n.t('Room_actions')}
|
||||
accessibilityTraits='button'
|
||||
testID='room-view-header-actions'
|
||||
>
|
||||
<Icon
|
||||
name={Platform.OS === 'ios' ? 'ios-more' : 'md-more'}
|
||||
color='#292E35'
|
||||
size={24}
|
||||
backgroundColor='transparent'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.header} testID='room-view-header'>
|
||||
{this.renderLeft()}
|
||||
{this.renderCenter()}
|
||||
{this.renderRight()}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
import { StyleSheet, Platform } from 'react-native';
|
||||
|
||||
export default StyleSheet.create({
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flex: 1
|
||||
},
|
||||
titleContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
marginLeft: Platform.OS === 'ios' ? 18 : 0,
|
||||
height: 44
|
||||
},
|
||||
titleTextContainer: {
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
flex: 1
|
||||
},
|
||||
status: {
|
||||
borderRadius: 10,
|
||||
width: 10,
|
||||
height: 10,
|
||||
position: 'absolute',
|
||||
borderWidth: 2,
|
||||
borderColor: '#fff',
|
||||
bottom: -2,
|
||||
right: -2
|
||||
},
|
||||
userStatus: {
|
||||
fontSize: 10,
|
||||
color: '#888'
|
||||
},
|
||||
title: {
|
||||
fontWeight: '500',
|
||||
color: '#292E35'
|
||||
},
|
||||
right: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
avatar: {
|
||||
marginRight: 5
|
||||
}
|
||||
});
|
|
@ -71,7 +71,7 @@ export class List extends React.Component {
|
|||
onEndReachedThreshold={100}
|
||||
renderFooter={this.props.renderFooter}
|
||||
renderHeader={() => <Typing />}
|
||||
onEndReached={() => this.props.onEndReached(this.data[this.data.length - 1], this.data.length)}
|
||||
onEndReached={() => this.props.onEndReached(this.data[this.data.length - 1])}
|
||||
dataSource={this.dataSource}
|
||||
renderRow={(item, previousItem) => this.props.renderRow(item, previousItem)}
|
||||
initialListSize={20}
|
||||
|
|