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,
|
"prefer-const": 2,
|
||||||
"object-shorthand": 2,
|
"object-shorthand": 2,
|
||||||
"consistent-return": 0,
|
"consistent-return": 0,
|
||||||
"global-require": "off",
|
"global-require": "off"
|
||||||
"react/prop-types": [0, { skipUndeclared: true }]
|
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"__DEV__": true
|
"__DEV__": true
|
||||||
|
|
22
.snyk
|
@ -1,5 +1,5 @@
|
||||||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||||
version: v1.7.1
|
version: v1.12.0
|
||||||
ignore: {}
|
ignore: {}
|
||||||
# patches apply the minimum changes required to fix a vulnerability
|
# patches apply the minimum changes required to fix a vulnerability
|
||||||
patch:
|
patch:
|
||||||
|
@ -24,3 +24,23 @@ patch:
|
||||||
patched: '2017-09-29T23:29:20.238Z'
|
patched: '2017-09-29T23:29:20.238Z'
|
||||||
- realm > extract-zip > debug:
|
- realm > extract-zip > debug:
|
||||||
patched: '2017-09-29T23:29:20.238Z'
|
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,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -868,7 +868,7 @@ exports[`render unread 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1118,7 +1118,7 @@ exports[`renders correctly 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -426,7 +426,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -648,7 +648,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -874,7 +874,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1119,7 +1119,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1368,7 +1368,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1613,7 +1613,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1858,7 +1858,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2103,7 +2103,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2348,7 +2348,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2570,7 +2570,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2792,7 +2792,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
Object {
|
Object {
|
||||||
"backgroundColor": "#cbced1",
|
"backgroundColor": undefined,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,13 +95,19 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "chat.rocket.reactnative"
|
applicationId "chat.rocket.reactnative"
|
||||||
minSdkVersion 16
|
minSdkVersion 19
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode VERSIONCODE as Integer
|
versionCode VERSIONCODE as Integer
|
||||||
versionName "1"
|
versionName "1"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "armeabi-v7a", "x86"
|
abiFilters "armeabi-v7a", "x86"
|
||||||
}
|
}
|
||||||
|
missingDimensionStrategy "RNN.reactNativeVersion", "reactNative55"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
@ -172,29 +178,30 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':react-native-i18n')
|
implementation project(':react-native-i18n')
|
||||||
compile project(':react-native-fabric')
|
implementation project(':react-native-fabric')
|
||||||
compile project(':react-native-audio')
|
implementation project(':react-native-audio')
|
||||||
compile project(":reactnativekeyboardinput")
|
implementation project(":reactnativekeyboardinput")
|
||||||
compile project(':react-native-splash-screen')
|
implementation project(':react-native-video')
|
||||||
compile project(':react-native-video')
|
implementation project(':react-native-svg')
|
||||||
compile project(':react-native-push-notification')
|
implementation project(':react-native-image-picker')
|
||||||
compile project(':react-native-svg')
|
implementation project(':react-native-vector-icons')
|
||||||
compile project(':react-native-image-picker')
|
implementation project(':react-native-fetch-blob')
|
||||||
compile project(':react-native-vector-icons')
|
implementation project(':react-native-zeroconf')
|
||||||
compile project(':react-native-fetch-blob')
|
implementation project(':react-native-toast')
|
||||||
compile project(':react-native-zeroconf')
|
implementation project(':react-native-fast-image')
|
||||||
compile project(':react-native-toast')
|
implementation project(':realm')
|
||||||
compile project(':react-native-fast-image')
|
implementation project(':react-native-navigation')
|
||||||
compile project(':realm')
|
implementation project(':reactnativenotifications')
|
||||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
compile "com.android.support:appcompat-v7:27.1.0"
|
implementation "com.android.support:appcompat-v7:27.1.0"
|
||||||
compile "com.android.support:support-v4:27.1.0"
|
implementation "com.android.support:support-v4:27.1.0"
|
||||||
compile 'com.android.support:customtabs:27.1.0'
|
implementation 'com.android.support:customtabs:27.1.0'
|
||||||
compile "com.facebook.react:react-native:+" // From node_modules
|
implementation 'com.android.support:design:27.1.0'
|
||||||
compile 'com.facebook.fresco:fresco:1.7.1'
|
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||||
compile 'com.facebook.fresco:animated-gif:1.7.1'
|
implementation 'com.facebook.fresco:fresco:1.7.1'
|
||||||
compile('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
|
implementation 'com.facebook.fresco:animated-gif:1.7.1'
|
||||||
|
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
|
||||||
transitive = true;
|
transitive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->
|
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<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
|
<permission
|
||||||
android:name="${applicationId}.permission.C2D_MESSAGE"
|
android:name="${applicationId}.permission.C2D_MESSAGE"
|
||||||
|
@ -50,27 +50,7 @@
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||||
|
|
||||||
|
<meta-data android:name="com.wix.reactnativenotifications.gcmSenderId" android:value="673693445664\0"/>
|
||||||
|
|
||||||
<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>
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</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;
|
package chat.rocket.reactnative;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.graphics.drawable.Drawable;
|
||||||
import com.facebook.react.ReactActivity;
|
import android.support.v4.content.ContextCompat;
|
||||||
import org.devio.rn.splashscreen.SplashScreen;
|
import android.widget.LinearLayout;
|
||||||
import com.crashlytics.android.Crashlytics;
|
import com.reactnativenavigation.controllers.SplashActivity;
|
||||||
import io.fabric.sdk.android.Fabric;
|
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
public class MainActivity extends SplashActivity {
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the main component registered from JavaScript.
|
|
||||||
* This is used to schedule rendering of the component.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected String getMainComponentName() {
|
public LinearLayout createSplashLayout() {
|
||||||
return "RocketChatRN";
|
LinearLayout splash = new LinearLayout(this);
|
||||||
}
|
Drawable launch_screen_bitmap = ContextCompat.getDrawable(getApplicationContext(),R.drawable.launch_screen_bitmap);
|
||||||
|
splash.setBackground(launch_screen_bitmap);
|
||||||
|
|
||||||
@Override
|
return splash;
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
SplashScreen.show(this);
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
Fabric.with(this, new Crashlytics());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,60 @@
|
||||||
package chat.rocket.reactnative;
|
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.horcrux.svg.SvgPackage;
|
||||||
import com.imagepicker.ImagePickerPackage;
|
import com.imagepicker.ImagePickerPackage;
|
||||||
import com.oblador.vectoricons.VectorIconsPackage;
|
import com.oblador.vectoricons.VectorIconsPackage;
|
||||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
import com.reactnativenavigation.NavigationApplication;
|
||||||
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.remobile.toast.RCTToastPackage;
|
import com.remobile.toast.RCTToastPackage;
|
||||||
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
|
|
||||||
import com.rnim.rn.audio.ReactNativeAudioPackage;
|
import com.rnim.rn.audio.ReactNativeAudioPackage;
|
||||||
import com.smixx.fabric.FabricPackage;
|
import com.smixx.fabric.FabricPackage;
|
||||||
import com.dylanvann.fastimage.FastImageViewPackage;
|
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
|
||||||
import com.AlexanderZaytsev.RNI18n.RNI18nPackage;
|
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.Arrays;
|
||||||
import java.util.List;
|
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
|
@Override
|
||||||
public boolean getUseDeveloperSupport() {
|
public boolean isDebug() {
|
||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public String getJSMainModuleName() {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
|
||||||
protected List<ReactPackage> getPackages() {
|
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(
|
return Arrays.<ReactPackage>asList(
|
||||||
new MainReactPackage(),
|
new MainReactPackage(),
|
||||||
new SvgPackage(),
|
new SvgPackage(),
|
||||||
|
@ -44,28 +63,37 @@ public class MainApplication extends Application implements ReactApplication {
|
||||||
new RNFetchBlobPackage(),
|
new RNFetchBlobPackage(),
|
||||||
new ZeroconfReactPackage(),
|
new ZeroconfReactPackage(),
|
||||||
new RealmReactPackage(),
|
new RealmReactPackage(),
|
||||||
new ReactNativePushNotificationPackage(),
|
|
||||||
new ReactVideoPackage(),
|
new ReactVideoPackage(),
|
||||||
new SplashScreenReactPackage(),
|
|
||||||
new RCTToastPackage(),
|
new RCTToastPackage(),
|
||||||
new ReactNativeAudioPackage(),
|
new ReactNativeAudioPackage(),
|
||||||
new KeyboardInputPackage(MainApplication.this),
|
new KeyboardInputPackage(MainApplication.this),
|
||||||
new RocketChatNativePackage(),
|
new RocketChatNativePackage(),
|
||||||
new FabricPackage(),
|
new FabricPackage(),
|
||||||
new FastImageViewPackage(),
|
new FastImageViewPackage(),
|
||||||
new RNI18nPackage()
|
new RNI18nPackage(),
|
||||||
|
new RNNotificationsPackage(MainApplication.this)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReactNativeHost getReactNativeHost() {
|
|
||||||
return mReactNativeHost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.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 {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
|
||||||
google()
|
google()
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
// classpath 'com.android.tools.build:gradle:2.2.3'
|
// classpath 'com.android.tools.build:gradle:2.2.3'
|
||||||
|
@ -17,6 +19,7 @@ buildscript {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
google()
|
google()
|
||||||
maven {
|
maven {
|
||||||
|
|
|
@ -9,8 +9,6 @@ include ':react-native-audio'
|
||||||
project(':react-native-audio').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-audio/android')
|
project(':react-native-audio').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-audio/android')
|
||||||
include ':reactnativekeyboardinput'
|
include ':reactnativekeyboardinput'
|
||||||
project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android')
|
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'
|
include ':react-native-video'
|
||||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
||||||
include ':react-native-svg'
|
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')
|
project(':react-native-zeroconf').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-zeroconf/android')
|
||||||
include ':realm'
|
include ':realm'
|
||||||
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
|
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'
|
include ':react-native-toast'
|
||||||
project(':react-native-toast').projectDir = new File(settingsDir, '../node_modules/@remobile/react-native-toast/android')
|
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'
|
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 */
|
/* eslint-disable */
|
||||||
|
import { NativeModules } from 'react-native';
|
||||||
import Reactotron from 'reactotron-react-native';
|
import Reactotron from 'reactotron-react-native';
|
||||||
import { reactotronRedux } from 'reactotron-redux';
|
import { reactotronRedux } from 'reactotron-redux';
|
||||||
import sagaPlugin from 'reactotron-redux-saga'
|
import sagaPlugin from 'reactotron-redux-saga'
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
const scriptURL = NativeModules.SourceCode.scriptURL;
|
||||||
|
const scriptHostname = scriptURL.split('://')[1].split(':')[0];
|
||||||
Reactotron
|
Reactotron
|
||||||
.configure()
|
.configure({ host: scriptHostname })
|
||||||
.useReactNative()
|
.useReactNative()
|
||||||
.use(reactotronRedux())
|
.use(reactotronRedux())
|
||||||
.use(sagaPlugin())
|
.use(sagaPlugin())
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const ROOM = createRequestTypes('ROOM', [
|
||||||
'MESSAGE_RECEIVED',
|
'MESSAGE_RECEIVED',
|
||||||
'SET_LAST_OPEN'
|
'SET_LAST_OPEN'
|
||||||
]);
|
]);
|
||||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT']);
|
||||||
export const MESSAGES = createRequestTypes('MESSAGES', [
|
export const MESSAGES = createRequestTypes('MESSAGES', [
|
||||||
...defaultTypes,
|
...defaultTypes,
|
||||||
'ACTIONS_SHOW',
|
'ACTIONS_SHOW',
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
import * as types from '../constants/types';
|
import * as types from '../constants/types';
|
||||||
import { APP } from './actionsTypes';
|
import { APP } from './actionsTypes';
|
||||||
|
|
||||||
|
export function appStart(root) {
|
||||||
|
return {
|
||||||
|
type: APP.START,
|
||||||
|
root
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function appReady() {
|
export function appReady() {
|
||||||
return {
|
return {
|
||||||
type: APP.READY
|
type: APP.READY
|
||||||
|
@ -12,6 +19,7 @@ export function appInit() {
|
||||||
type: APP.INIT
|
type: APP.INIT
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setCurrentServer(server) {
|
export function setCurrentServer(server) {
|
||||||
return {
|
return {
|
||||||
type: types.SET_CURRENT_SERVER,
|
type: types.SET_CURRENT_SERVER,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { SERVER } from './actionsTypes';
|
import { SERVER } from './actionsTypes';
|
||||||
|
|
||||||
export function setServer(server) {
|
export function selectServer(server) {
|
||||||
return {
|
return {
|
||||||
type: SERVER.SELECT,
|
type: SERVER.SELECT,
|
||||||
server
|
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(
|
@connect(
|
||||||
state => ({
|
state => ({
|
||||||
actionMessage: state.messages.actionMessage,
|
actionMessage: state.messages.actionMessage,
|
||||||
user: state.login.user,
|
|
||||||
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
||||||
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
||||||
Message_AllowEditing: state.settings.Message_AllowEditing,
|
Message_AllowEditing: state.settings.Message_AllowEditing,
|
||||||
|
@ -42,9 +41,9 @@ import I18n from '../i18n';
|
||||||
export default class MessageActions extends React.Component {
|
export default class MessageActions extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
actionsHide: PropTypes.func.isRequired,
|
actionsHide: PropTypes.func.isRequired,
|
||||||
room: PropTypes.object,
|
room: PropTypes.object.isRequired,
|
||||||
actionMessage: PropTypes.object,
|
actionMessage: PropTypes.object,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object.isRequired,
|
||||||
deleteRequest: PropTypes.func.isRequired,
|
deleteRequest: PropTypes.func.isRequired,
|
||||||
editInit: PropTypes.func.isRequired,
|
editInit: PropTypes.func.isRequired,
|
||||||
toggleStarRequest: 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 React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
import { DrawerActions, SafeAreaView } from 'react-navigation';
|
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
|
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import { setServer } from '../actions/server';
|
import { selectServer } from '../actions/server';
|
||||||
import { logout } from '../actions/login';
|
import { logout } from '../actions/login';
|
||||||
|
import { appStart } from '../actions';
|
||||||
import Avatar from '../containers/Avatar';
|
import Avatar from '../containers/Avatar';
|
||||||
import Status from '../containers/status';
|
import Status from '../containers/status';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
|
@ -16,6 +16,7 @@ import { STATUS_COLORS } from '../constants/colors';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
import { NavigationActions } from '../Navigation';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
selected: {
|
selected: {
|
||||||
|
@ -76,19 +77,29 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const keyExtractor = item => item.id;
|
const keyExtractor = item => item.id;
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
server: state.server.server,
|
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 => ({
|
}), dispatch => ({
|
||||||
selectServer: server => dispatch(setServer(server)),
|
selectServer: server => dispatch(selectServer(server)),
|
||||||
logout: () => dispatch(logout())
|
logout: () => dispatch(logout()),
|
||||||
|
appStart: () => dispatch(appStart('outside'))
|
||||||
}))
|
}))
|
||||||
export default class Sidebar extends Component {
|
export default class Sidebar extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigator: PropTypes.object,
|
||||||
server: PropTypes.string.isRequired,
|
server: PropTypes.string.isRequired,
|
||||||
selectServer: PropTypes.func.isRequired,
|
selectServer: PropTypes.func.isRequired,
|
||||||
navigation: PropTypes.object.isRequired,
|
user: PropTypes.object,
|
||||||
logout: PropTypes.func.isRequired
|
logout: PropTypes.func.isRequired,
|
||||||
|
appStart: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -117,7 +128,6 @@ export default class Sidebar extends Component {
|
||||||
|
|
||||||
onPressItem = (item) => {
|
onPressItem = (item) => {
|
||||||
this.props.selectServer(item.id);
|
this.props.selectServer(item.id);
|
||||||
this.closeDrawer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus = () => {
|
setStatus = () => {
|
||||||
|
@ -149,7 +159,11 @@ export default class Sidebar extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDrawer = () => {
|
closeDrawer = () => {
|
||||||
this.props.navigation.dispatch(DrawerActions.closeDrawer());
|
this.props.navigator.toggleDrawer({
|
||||||
|
side: 'left',
|
||||||
|
animated: true,
|
||||||
|
to: 'close'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleServers = () => {
|
toggleServers = () => {
|
||||||
|
@ -157,25 +171,15 @@ export default class Sidebar extends Component {
|
||||||
this.setState({ showServers: !this.state.showServers });
|
this.setState({ showServers: !this.state.showServers });
|
||||||
}
|
}
|
||||||
|
|
||||||
isRouteFocused = (route) => {
|
sidebarNavigate = (screen, title) => {
|
||||||
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 {
|
|
||||||
this.closeDrawer();
|
this.closeDrawer();
|
||||||
}
|
NavigationActions.resetTo({ screen, title });
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSeparator = key => <View key={key} style={styles.separator} />;
|
renderSeparator = key => <View key={key} style={styles.separator} />;
|
||||||
|
|
||||||
renderItem = ({
|
renderItem = ({
|
||||||
text, left, selected, onPress, testID
|
text, left, onPress, testID
|
||||||
}) => (
|
}) => (
|
||||||
<Touch
|
<Touch
|
||||||
key={text}
|
key={text}
|
||||||
|
@ -184,8 +188,8 @@ export default class Sidebar extends Component {
|
||||||
activeOpacity={0.3}
|
activeOpacity={0.3}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
>
|
>
|
||||||
<View style={[styles.item, selected && styles.selected]}>
|
<View style={styles.item}>
|
||||||
<View style={[styles.itemLeft, !selected && styles.itemLeftOpacity]}>
|
<View style={styles.itemLeft}>
|
||||||
{left}
|
{left}
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.itemText}>
|
<Text style={styles.itemText}>
|
||||||
|
@ -222,12 +226,15 @@ export default class Sidebar extends Component {
|
||||||
source={{ uri: encodeURI(`${ item.id }/assets/favicon_32.png`) }}
|
source={{ uri: encodeURI(`${ item.id }/assets/favicon_32.png`) }}
|
||||||
/>,
|
/>,
|
||||||
selected: this.props.server === item.id,
|
selected: this.props.server === item.id,
|
||||||
onPress: () => {
|
onPress: async() => {
|
||||||
this.closeDrawer();
|
this.closeDrawer();
|
||||||
this.toggleServers();
|
this.toggleServers();
|
||||||
if (this.props.server !== item.id) {
|
if (this.props.server !== item.id) {
|
||||||
this.props.selectServer(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 }`
|
testID: `sidebar-${ item.id }`
|
||||||
|
@ -239,31 +246,25 @@ export default class Sidebar extends Component {
|
||||||
this.renderItem({
|
this.renderItem({
|
||||||
text: I18n.t('Chats'),
|
text: I18n.t('Chats'),
|
||||||
left: <Icon name='chat-bubble' size={20} />,
|
left: <Icon name='chat-bubble' size={20} />,
|
||||||
onPress: () => this.sidebarNavigate('Chats'),
|
onPress: () => this.sidebarNavigate('RoomsListView', I18n.t('Messages')),
|
||||||
selected: this.isRouteFocused('Chats'),
|
|
||||||
testID: 'sidebar-chats'
|
testID: 'sidebar-chats'
|
||||||
}),
|
}),
|
||||||
this.renderItem({
|
this.renderItem({
|
||||||
text: I18n.t('Profile'),
|
text: I18n.t('Profile'),
|
||||||
left: <Icon name='person' size={20} />,
|
left: <Icon name='person' size={20} />,
|
||||||
onPress: () => this.sidebarNavigate('ProfileView'),
|
onPress: () => this.sidebarNavigate('ProfileView', I18n.t('Profile')),
|
||||||
selected: this.isRouteFocused('ProfileView'),
|
|
||||||
testID: 'sidebar-profile'
|
testID: 'sidebar-profile'
|
||||||
}),
|
}),
|
||||||
this.renderItem({
|
this.renderItem({
|
||||||
text: I18n.t('Settings'),
|
text: I18n.t('Settings'),
|
||||||
left: <Icon name='settings' size={20} />,
|
left: <Icon name='settings' size={20} />,
|
||||||
onPress: () => this.sidebarNavigate('SettingsView'),
|
onPress: () => this.sidebarNavigate('SettingsView', I18n.t('Settings')),
|
||||||
selected: this.isRouteFocused('SettingsView'),
|
|
||||||
testID: 'sidebar-settings'
|
testID: 'sidebar-settings'
|
||||||
}),
|
}),
|
||||||
this.renderSeparator('separator-logout'),
|
this.renderSeparator('separator-logout'),
|
||||||
this.renderItem({
|
this.renderItem({
|
||||||
text: I18n.t('Logout'),
|
text: I18n.t('Logout'),
|
||||||
left: <Icon
|
left: <Icon name='exit-to-app' size={20} />,
|
||||||
name='exit-to-app'
|
|
||||||
size={20}
|
|
||||||
/>,
|
|
||||||
onPress: () => this.props.logout(),
|
onPress: () => this.props.logout(),
|
||||||
testID: 'sidebar-logout'
|
testID: 'sidebar-logout'
|
||||||
})
|
})
|
||||||
|
@ -297,7 +298,10 @@ export default class Sidebar extends Component {
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
this.closeDrawer();
|
this.closeDrawer();
|
||||||
this.toggleServers();
|
this.toggleServers();
|
||||||
this.props.navigation.navigate('AddServer');
|
NavigationActions.push({
|
||||||
|
screen: 'NewServerView',
|
||||||
|
title: I18n.t('Add_Server')
|
||||||
|
});
|
||||||
},
|
},
|
||||||
testID: 'sidebar-add-server'
|
testID: 'sidebar-add-server'
|
||||||
})
|
})
|
||||||
|
@ -306,13 +310,12 @@ export default class Sidebar extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { user, server } = this.props;
|
const { user, server } = this.props;
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView style={{ backgroundColor: '#fff' }}>
|
||||||
<SafeAreaView
|
<SafeAreaView testID='sidebar'>
|
||||||
style={styles.container}
|
|
||||||
forceInset={{ top: 'always', horizontal: 'never' }}
|
|
||||||
testID='sidebar'
|
|
||||||
>
|
|
||||||
<Touch
|
<Touch
|
||||||
onPress={() => this.toggleServers()}
|
onPress={() => this.toggleServers()}
|
||||||
underlayColor='rgba(255, 255, 255, 0.5)'
|
underlayColor='rgba(255, 255, 255, 0.5)'
|
||||||
|
@ -335,6 +338,7 @@ export default class Sidebar extends Component {
|
||||||
<Icon
|
<Icon
|
||||||
name={this.state.showServers ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
|
name={this.state.showServers ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
|
||||||
size={30}
|
size={30}
|
||||||
|
style={{ paddingHorizontal: 10 }}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Touch>
|
</Touch>
|
||||||
|
|
|
@ -66,7 +66,10 @@ export default class RCTextInput extends React.PureComponent {
|
||||||
secureTextEntry: PropTypes.bool,
|
secureTextEntry: PropTypes.bool,
|
||||||
containerStyle: ViewPropTypes.style,
|
containerStyle: ViewPropTypes.style,
|
||||||
inputStyle: PropTypes.object,
|
inputStyle: PropTypes.object,
|
||||||
inputRef: PropTypes.func
|
inputRef: PropTypes.func,
|
||||||
|
testID: PropTypes.string,
|
||||||
|
iconLeft: PropTypes.string,
|
||||||
|
placeholder: PropTypes.string
|
||||||
}
|
}
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
error: {}
|
error: {}
|
||||||
|
|
|
@ -28,7 +28,8 @@ export default class extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
file: PropTypes.object.isRequired,
|
file: PropTypes.object.isRequired,
|
||||||
baseUrl: PropTypes.string.isRequired,
|
baseUrl: PropTypes.string.isRequired,
|
||||||
user: PropTypes.object.isRequired
|
user: PropTypes.object.isRequired,
|
||||||
|
customEmojis: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
state = { modalVisible: false };
|
state = { modalVisible: false };
|
||||||
|
|
|
@ -99,7 +99,11 @@ export default class Message extends React.Component {
|
||||||
Message_GroupingPeriod: PropTypes.number.isRequired,
|
Message_GroupingPeriod: PropTypes.number.isRequired,
|
||||||
customTimeFormat: PropTypes.string,
|
customTimeFormat: PropTypes.string,
|
||||||
message: PropTypes.object.isRequired,
|
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,
|
editing: PropTypes.bool,
|
||||||
errorActionsShow: PropTypes.func,
|
errorActionsShow: PropTypes.func,
|
||||||
toggleReactionPicker: PropTypes.func,
|
toggleReactionPicker: PropTypes.func,
|
||||||
|
@ -109,7 +113,8 @@ export default class Message extends React.Component {
|
||||||
onLongPress: PropTypes.func,
|
onLongPress: PropTypes.func,
|
||||||
_updatedAt: PropTypes.instanceOf(Date),
|
_updatedAt: PropTypes.instanceOf(Date),
|
||||||
archived: PropTypes.bool,
|
archived: PropTypes.bool,
|
||||||
broadcast: PropTypes.bool
|
broadcast: PropTypes.bool,
|
||||||
|
previousItem: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
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 => ({
|
@connect((state, ownProps) => {
|
||||||
activeUsers: state.activeUsers,
|
if (state.login.user && ownProps.id === state.login.user.id) {
|
||||||
user: state.login.user,
|
return {
|
||||||
|
status: state.login.user && state.login.user.status,
|
||||||
offline: !state.meteor.connected
|
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 = {
|
static propTypes = {
|
||||||
style: ViewPropTypes.style,
|
style: ViewPropTypes.style,
|
||||||
id: PropTypes.string,
|
status: PropTypes.string,
|
||||||
activeUsers: PropTypes.object,
|
|
||||||
user: PropTypes.object,
|
|
||||||
offline: PropTypes.bool
|
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() {
|
get status() {
|
||||||
const { id: userId, user, offline } = this.props;
|
const { offline, status } = this.props;
|
||||||
if (user.id === userId) {
|
|
||||||
if (offline) {
|
if (offline) {
|
||||||
return 'offline';
|
return 'offline';
|
||||||
}
|
}
|
||||||
return user.status || 'offline';
|
return status;
|
||||||
}
|
|
||||||
return (this.props.activeUsers && this.props.activeUsers[userId] && this.props.activeUsers[userId].status) || 'offline';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -109,6 +109,7 @@ export default {
|
||||||
changing_avatar: 'changing avatar',
|
changing_avatar: 'changing avatar',
|
||||||
Channel_Name: 'Channel Name',
|
Channel_Name: 'Channel Name',
|
||||||
Chats: 'Chats',
|
Chats: 'Chats',
|
||||||
|
Close: 'Close',
|
||||||
Close_emoji_selector: 'Close emoji selector',
|
Close_emoji_selector: 'Close emoji selector',
|
||||||
Code: 'Code',
|
Code: 'Code',
|
||||||
Colaborative: 'Colaborative',
|
Colaborative: 'Colaborative',
|
||||||
|
@ -141,6 +142,7 @@ export default {
|
||||||
Forgot_my_password: 'Forgot my password',
|
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_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',
|
||||||
|
Forgot_Password: 'Forgot Password',
|
||||||
Has_joined_the_channel: 'Has joined the channel',
|
Has_joined_the_channel: 'Has joined the channel',
|
||||||
Has_left_the_channel: 'Has left the channel',
|
Has_left_the_channel: 'Has left the channel',
|
||||||
I_have_an_account: 'I have an account',
|
I_have_an_account: 'I have an account',
|
||||||
|
@ -164,6 +166,7 @@ export default {
|
||||||
Message_actions: 'Message actions',
|
Message_actions: 'Message actions',
|
||||||
Message_pinned: 'Message pinned',
|
Message_pinned: 'Message pinned',
|
||||||
Message_removed: 'Message removed',
|
Message_removed: 'Message removed',
|
||||||
|
Messages: 'Messages',
|
||||||
Microphone_Permission_Message: 'Rocket Chat needs access to your microphone so you can send audio message.',
|
Microphone_Permission_Message: 'Rocket Chat needs access to your microphone so you can send audio message.',
|
||||||
Microphone_Permission: 'Microphone Permission',
|
Microphone_Permission: 'Microphone Permission',
|
||||||
Mute: 'Mute',
|
Mute: 'Mute',
|
||||||
|
@ -294,6 +297,7 @@ export default {
|
||||||
Validating: 'Validating',
|
Validating: 'Validating',
|
||||||
Video_call: 'Video call',
|
Video_call: 'Video call',
|
||||||
Voice_call: 'Voice call',
|
Voice_call: 'Voice call',
|
||||||
|
Welcome: 'Welcome',
|
||||||
Welcome_title_pt_1: 'Prepare to take off with',
|
Welcome_title_pt_1: 'Prepare to take off with',
|
||||||
Welcome_title_pt_2: 'the ultimate chat platform',
|
Welcome_title_pt_2: 'the ultimate chat platform',
|
||||||
Yes_action_it: 'Yes, {{action}} it!',
|
Yes_action_it: 'Yes, {{action}} it!',
|
||||||
|
|
98
app/index.js
|
@ -1,14 +1,94 @@
|
||||||
import React from 'react';
|
import { Component } from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Linking } from 'react-native';
|
||||||
|
import { Navigation } from 'react-native-navigation';
|
||||||
|
|
||||||
import store from './lib/createStore';
|
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 = () => (
|
const startNotLogged = (route) => {
|
||||||
<Provider store={store}>
|
Navigation.startSingleScreenApp({
|
||||||
<Routes />
|
screen: {
|
||||||
</Provider>
|
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 */
|
/* eslint-disable global-require */
|
||||||
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
||||||
sagaMiddleware = createSagaMiddleware({
|
sagaMiddleware = createSagaMiddleware({
|
||||||
sagaMonitor: Reactotron.createSagaMonitor()
|
// sagaMonitor: Reactotron.createSagaMonitor()
|
||||||
});
|
});
|
||||||
|
|
||||||
enhancers = compose(
|
enhancers = compose(
|
||||||
|
|
|
@ -54,5 +54,6 @@ export default async function canOpenRoom({ rid, path }) {
|
||||||
return data;
|
return data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('canOpenRoom', e);
|
log('canOpenRoom', e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ const attachment = {
|
||||||
video_url: { type: 'string', optional: true },
|
video_url: { type: 'string', optional: true },
|
||||||
title: { type: 'string', optional: true },
|
title: { type: 'string', optional: true },
|
||||||
title_link: { 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 },
|
type: { type: 'string', optional: true },
|
||||||
author_icon: { type: 'string', optional: true },
|
author_icon: { type: 'string', optional: true },
|
||||||
author_name: { 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 sendMessage, { getMessage, _sendMessageCall } from './methods/sendMessage';
|
||||||
|
|
||||||
|
import { getDeviceToken } from '../push';
|
||||||
|
|
||||||
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
||||||
const call = (method, ...params) => RocketChat.ddp.call(method, ...params); // eslint-disable-line
|
const call = (method, ...params) => RocketChat.ddp.call(method, ...params); // eslint-disable-line
|
||||||
const returnAnArray = obj => obj || [];
|
const returnAnArray = obj => obj || [];
|
||||||
|
@ -119,7 +121,7 @@ const RocketChat = {
|
||||||
reduxStore.dispatch(setActiveUser(this.activeUsers));
|
reduxStore.dispatch(setActiveUser(this.activeUsers));
|
||||||
this._setUserTimer = null;
|
this._setUserTimer = null;
|
||||||
return this.activeUsers = {};
|
return this.activeUsers = {};
|
||||||
}, 5000);
|
}, 2000);
|
||||||
|
|
||||||
const activeUser = reduxStore.getState().activeUsers[ddpMessage.id];
|
const activeUser = reduxStore.getState().activeUsers[ddpMessage.id];
|
||||||
if (!ddpMessage.fields) {
|
if (!ddpMessage.fields) {
|
||||||
|
@ -175,8 +177,8 @@ const RocketChat = {
|
||||||
this.ddp.on('disconnected', () => console.log('disconnected'));
|
this.ddp.on('disconnected', () => console.log('disconnected'));
|
||||||
|
|
||||||
this.ddp.on('logged', protectedFunction((user) => {
|
this.ddp.on('logged', protectedFunction((user) => {
|
||||||
this.getRooms().catch(e => log('logged getRooms', e));
|
|
||||||
this.loginSuccess(user);
|
this.loginSuccess(user);
|
||||||
|
this.getRooms().catch(e => log('logged getRooms', e));
|
||||||
}));
|
}));
|
||||||
this.ddp.once('logged', protectedFunction(({ id }) => {
|
this.ddp.once('logged', protectedFunction(({ id }) => {
|
||||||
this.subscribeRooms(id);
|
this.subscribeRooms(id);
|
||||||
|
@ -556,21 +558,24 @@ const RocketChat = {
|
||||||
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
||||||
},
|
},
|
||||||
|
|
||||||
registerPushToken(id, token) {
|
registerPushToken(userId) {
|
||||||
|
const deviceToken = getDeviceToken();
|
||||||
|
if (deviceToken) {
|
||||||
const key = Platform.OS === 'ios' ? 'apn' : 'gcm';
|
const key = Platform.OS === 'ios' ? 'apn' : 'gcm';
|
||||||
const data = {
|
const data = {
|
||||||
id: `RocketChatRN${ id }`,
|
id: `RocketChatRN${ userId }`,
|
||||||
token: { [key]: token },
|
token: { [key]: deviceToken },
|
||||||
appName: 'chat.rocket.reactnative', // TODO: try to get from config file
|
appName: 'chat.rocket.reactnative', // TODO: try to get from config file
|
||||||
userId: id,
|
userId,
|
||||||
metadata: {}
|
metadata: {}
|
||||||
};
|
};
|
||||||
return call('raix:push-update', data);
|
return call('raix:push-update', data);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updatePushToken(pushId) {
|
// updatePushToken(pushId) {
|
||||||
return call('raix:push-setuser', pushId);
|
// return call('raix:push-setuser', pushId);
|
||||||
},
|
// },
|
||||||
loadMissedMessages,
|
loadMissedMessages,
|
||||||
loadMessagesForRoom,
|
loadMessagesForRoom,
|
||||||
getMessage,
|
getMessage,
|
||||||
|
|
|
@ -121,7 +121,7 @@ const renderNumber = (unread, userMentions) => {
|
||||||
|
|
||||||
const attrs = ['name', 'unread', 'userMentions', 'alert', 'showLastMessage', 'type'];
|
const attrs = ['name', 'unread', 'userMentions', 'alert', 'showLastMessage', 'type'];
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
user: state.login && state.login.user,
|
username: state.login.user && state.login.user.username,
|
||||||
StoreLastMessage: state.settings.Store_Last_Message
|
StoreLastMessage: state.settings.Store_Last_Message
|
||||||
}))
|
}))
|
||||||
export default class RoomItem extends React.Component {
|
export default class RoomItem extends React.Component {
|
||||||
|
@ -139,7 +139,7 @@ export default class RoomItem extends React.Component {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
onLongPress: PropTypes.func,
|
onLongPress: PropTypes.func,
|
||||||
user: PropTypes.object,
|
username: PropTypes.string,
|
||||||
avatarSize: PropTypes.number,
|
avatarSize: PropTypes.number,
|
||||||
statusStyle: ViewPropTypes.style,
|
statusStyle: ViewPropTypes.style,
|
||||||
testID: PropTypes.string
|
testID: PropTypes.string
|
||||||
|
@ -182,7 +182,7 @@ export default class RoomItem extends React.Component {
|
||||||
|
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
|
|
||||||
if (lastMessage.u.username === this.props.user.username) {
|
if (lastMessage.u.username === this.props.username) {
|
||||||
prefix = I18n.t('You_colon');
|
prefix = I18n.t('You_colon');
|
||||||
} else if (type !== 'd') {
|
} else if (type !== 'd') {
|
||||||
prefix = `${ lastMessage.u.username }: `;
|
prefix = `${ lastMessage.u.username }: `;
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import PushNotification from 'react-native-push-notification';
|
import PushNotification from 'react-native-push-notification';
|
||||||
import { AsyncStorage } from 'react-native';
|
import { AsyncStorage } from 'react-native';
|
||||||
import EJSON from 'ejson';
|
import EJSON from 'ejson';
|
||||||
import { goRoom } from './containers/routes/NavigationService';
|
|
||||||
|
import { NavigationActions } from './Navigation';
|
||||||
|
|
||||||
const handleNotification = (notification) => {
|
const handleNotification = (notification) => {
|
||||||
if (!notification.userInteraction) {
|
if (notification.userInteraction) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {
|
const {
|
||||||
rid, name, sender, type
|
rid, name, sender, type
|
||||||
} = EJSON.parse(notification.ejson || notification.data.ejson);
|
} = 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({
|
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';
|
import { APP } from '../actions/actionsTypes';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
root: null,
|
||||||
starting: true,
|
starting: true,
|
||||||
ready: false,
|
ready: false,
|
||||||
inactive: false,
|
inactive: false,
|
||||||
|
@ -31,6 +32,11 @@ export default function app(state = initialState, action) {
|
||||||
foreground: false,
|
foreground: false,
|
||||||
background: false
|
background: false
|
||||||
};
|
};
|
||||||
|
case APP.START:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
root: action.root
|
||||||
|
};
|
||||||
case APP.INIT:
|
case APP.INIT:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -19,6 +19,7 @@ export default function login(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
isFetching: true,
|
isFetching: true,
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
|
isRegistering: false,
|
||||||
failure: false,
|
failure: false,
|
||||||
error: ''
|
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 { AsyncStorage } from 'react-native';
|
||||||
import { METEOR } from '../actions/actionsTypes';
|
import { METEOR } from '../actions/actionsTypes';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
@ -31,7 +31,8 @@ const test = function* test() {
|
||||||
const server = yield select(getServer);
|
const server = yield select(getServer);
|
||||||
const user = yield call(getToken);
|
const user = yield call(getToken);
|
||||||
// const response =
|
// 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));
|
// yield put(connectSuccess(response));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('test', err);
|
console.warn('test', err);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { delay } from 'redux-saga';
|
import { delay } from 'redux-saga';
|
||||||
import { select, put, call, take, takeLatest } from 'redux-saga/effects';
|
import { select, put, call, take, takeLatest } from 'redux-saga/effects';
|
||||||
|
import { NavigationActions } from '../Navigation';
|
||||||
|
|
||||||
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
||||||
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
|
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { goRoom } from '../containers/routes/NavigationService';
|
|
||||||
|
|
||||||
const create = function* create(data) {
|
const create = function* create(data) {
|
||||||
return yield RocketChat.createChannel(data);
|
return yield RocketChat.createChannel(data);
|
||||||
|
@ -18,7 +19,17 @@ const handleRequest = function* handleRequest({ data }) {
|
||||||
}
|
}
|
||||||
const result = yield call(create, data);
|
const result = yield call(create, data);
|
||||||
const { rid, name } = result;
|
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));
|
yield put(createChannelSuccess(result));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
yield put(createChannelFailure(err));
|
yield put(createChannelFailure(err));
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
import { AsyncStorage } from 'react-native';
|
import { AsyncStorage } from 'react-native';
|
||||||
import { delay } from 'redux-saga';
|
import { takeLatest, take, select, put } from 'redux-saga/effects';
|
||||||
import { takeLatest, take, select, call, put } from 'redux-saga/effects';
|
|
||||||
import * as types from '../actions/actionsTypes';
|
import * as types from '../actions/actionsTypes';
|
||||||
import { setServer, addServer } from '../actions/server';
|
import { appStart } from '../actions';
|
||||||
import * as NavigationService from '../containers/routes/NavigationService';
|
import { selectServer, addServer } from '../actions/server';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
import { NavigationActions } from '../Navigation';
|
||||||
|
|
||||||
const navigate = function* go({ server, params, sameServer = true }) {
|
const navigate = function* go({ 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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!sameServer) {
|
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;
|
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 {
|
try {
|
||||||
yield RocketChat.testServer(host);
|
yield RocketChat.testServer(host);
|
||||||
|
@ -43,18 +52,26 @@ const handleOpen = function* handleOpen({ params }) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const token = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ host }`);
|
||||||
|
|
||||||
// TODO: needs better test
|
// TODO: needs better test
|
||||||
// if deep link is from same server
|
// if deep link is from same server
|
||||||
if (server === host) {
|
if (server === host) {
|
||||||
yield navigate({ server, params });
|
if (token) {
|
||||||
|
yield navigate({ params });
|
||||||
|
}
|
||||||
} else { // if deep link is from a different server
|
} else { // if deep link is from a different server
|
||||||
// search if deep link's server already exists
|
// search if deep link's server already exists
|
||||||
const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test
|
const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test
|
||||||
if (servers.length) {
|
if (servers.length) {
|
||||||
// if server exists, select it
|
const deepLinkServer = servers[0].id;
|
||||||
yield put(setServer(servers[0].id));
|
if (!token) {
|
||||||
yield delay(2000);
|
yield put(appStart('outside'));
|
||||||
yield navigate({ server: servers[0].id, params, sameServer: false });
|
} else {
|
||||||
|
yield put(selectServer(deepLinkServer));
|
||||||
|
yield take(types.METEOR.REQUEST);
|
||||||
|
yield navigate({ params, sameServer: false });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
yield put(addServer(host));
|
yield put(addServer(host));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { AsyncStorage } from 'react-native';
|
||||||
import { call, put, takeLatest } from 'redux-saga/effects';
|
import { call, put, takeLatest } from 'redux-saga/effects';
|
||||||
|
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
import { setServer } from '../actions/server';
|
import { selectServer } from '../actions/server';
|
||||||
import { restoreToken, setUser } from '../actions/login';
|
import { restoreToken, setUser } from '../actions/login';
|
||||||
import { APP } from '../actions/actionsTypes';
|
import { APP } from '../actions/actionsTypes';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
@ -13,11 +13,16 @@ const restore = function* restore() {
|
||||||
const token = yield call([AsyncStorage, 'getItem'], RocketChat.TOKEN_KEY);
|
const token = yield call([AsyncStorage, 'getItem'], RocketChat.TOKEN_KEY);
|
||||||
if (token) {
|
if (token) {
|
||||||
yield put(restoreToken(token));
|
yield put(restoreToken(token));
|
||||||
|
} else {
|
||||||
|
yield put(actions.appStart('outside'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
||||||
if (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 }`);
|
const login = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
|
||||||
if (login) {
|
if (login) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { AsyncStorage } from 'react-native';
|
import { AsyncStorage } from 'react-native';
|
||||||
|
import { delay } from 'redux-saga';
|
||||||
import { put, call, take, takeLatest, select, all } from 'redux-saga/effects';
|
import { put, call, take, takeLatest, select, all } from 'redux-saga/effects';
|
||||||
|
|
||||||
import * as types from '../actions/actionsTypes';
|
import * as types from '../actions/actionsTypes';
|
||||||
|
import { appStart } from '../actions';
|
||||||
import {
|
import {
|
||||||
// loginRequest,
|
// loginRequest,
|
||||||
// loginSubmit,
|
// loginSubmit,
|
||||||
|
@ -18,7 +20,6 @@ import {
|
||||||
forgotPasswordFailure
|
forgotPasswordFailure
|
||||||
} from '../actions/login';
|
} from '../actions/login';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import * as NavigationService from '../containers/routes/NavigationService';
|
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
|
||||||
|
@ -26,7 +27,6 @@ const getUser = state => state.login;
|
||||||
const getServer = state => state.server.server;
|
const getServer = state => state.server.server;
|
||||||
const getIsConnected = state => state.meteor.connected;
|
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 loginCall = args => RocketChat.loginWithPassword(args);
|
||||||
const registerCall = args => RocketChat.register(args);
|
const registerCall = args => RocketChat.register(args);
|
||||||
const setUsernameCall = args => RocketChat.setUsername(args);
|
const setUsernameCall = args => RocketChat.setUsername(args);
|
||||||
|
@ -34,67 +34,27 @@ const loginSuccessCall = () => RocketChat.loginSuccess();
|
||||||
const logoutCall = args => RocketChat.logout(args);
|
const logoutCall = args => RocketChat.logout(args);
|
||||||
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
||||||
|
|
||||||
// const getToken = function* getToken() {
|
const handleLoginSuccess = function* handleLoginSuccess() {
|
||||||
// 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() {
|
|
||||||
try {
|
try {
|
||||||
const [server, user] = yield all([select(getServer), select(getUser)]);
|
const [server, user] = yield all([select(getServer), select(getUser)]);
|
||||||
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
|
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
|
||||||
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
||||||
const token = yield AsyncStorage.getItem('pushId');
|
// const token = yield AsyncStorage.getItem('pushId');
|
||||||
if (token) {
|
// if (token) {
|
||||||
yield RocketChat.registerPushToken(user.user.id, token);
|
// yield RocketChat.registerPushToken(user.user.id, token);
|
||||||
}
|
// }
|
||||||
if (!user.user.username && !user.isRegistering) {
|
yield RocketChat.registerPushToken(user.user.id);
|
||||||
|
if (!user.user.username || user.isRegistering) {
|
||||||
yield put(registerIncomplete());
|
yield put(registerIncomplete());
|
||||||
|
} else {
|
||||||
|
yield delay(300);
|
||||||
|
yield put(appStart('inside'));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} 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 }) {
|
const handleRegisterSubmit = function* handleRegisterSubmit({ credentials }) {
|
||||||
yield put(registerRequest(credentials));
|
yield put(registerRequest(credentials));
|
||||||
};
|
};
|
||||||
|
@ -137,6 +97,8 @@ const handleLogout = function* handleLogout() {
|
||||||
const server = yield select(getServer);
|
const server = yield select(getServer);
|
||||||
if (server) {
|
if (server) {
|
||||||
try {
|
try {
|
||||||
|
yield put(appStart('outside'));
|
||||||
|
yield delay(300);
|
||||||
yield call(logoutCall, { server });
|
yield call(logoutCall, { server });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('handleLogout', e);
|
log('handleLogout', e);
|
||||||
|
@ -145,7 +107,7 @@ const handleLogout = function* handleLogout() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRegisterIncomplete = function* handleRegisterIncomplete() {
|
const handleRegisterIncomplete = function* handleRegisterIncomplete() {
|
||||||
yield call(NavigationService.navigate, 'Register');
|
yield put(appStart('outside'));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
|
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
|
||||||
|
@ -183,7 +145,7 @@ const handleSetUser = function* handleSetUser(params) {
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
// yield takeLatest(types.METEOR.SUCCESS, handleLoginWhenServerChanges);
|
// yield takeLatest(types.METEOR.SUCCESS, handleLoginWhenServerChanges);
|
||||||
// yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
|
// 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.SUBMIT, handleLoginSubmit);
|
||||||
yield takeLatest(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
|
yield takeLatest(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
|
||||||
yield takeLatest(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
|
yield takeLatest(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { delay } from 'redux-saga';
|
import { delay } from 'redux-saga';
|
||||||
import { takeLatest, put, call, select } from 'redux-saga/effects';
|
import { takeLatest, put, call, select } from 'redux-saga/effects';
|
||||||
|
|
||||||
import { MESSAGES } from '../actions/actionsTypes';
|
import { MESSAGES } from '../actions/actionsTypes';
|
||||||
import {
|
import {
|
||||||
messagesSuccess,
|
messagesSuccess,
|
||||||
|
@ -16,8 +17,8 @@ import {
|
||||||
} from '../actions/messages';
|
} from '../actions/messages';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import { goRoom } from '../containers/routes/NavigationService';
|
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
import { NavigationActions } from '../Navigation';
|
||||||
|
|
||||||
const deleteMessage = message => RocketChat.deleteMessage(message);
|
const deleteMessage = message => RocketChat.deleteMessage(message);
|
||||||
const editMessage = message => RocketChat.editMessage(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 }) {
|
const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
||||||
try {
|
try {
|
||||||
const { username } = message.u;
|
const { username } = message.u;
|
||||||
const subscriptions = database.objects('subscriptions').filtered('name = $0', username);
|
const subscriptions = database.objects('subscriptions').filtered('name = $0', username);
|
||||||
if (subscriptions.length) {
|
if (subscriptions.length) {
|
||||||
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
yield goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||||
} else {
|
} else {
|
||||||
const room = yield RocketChat.createDirectMessage(username);
|
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 server = yield select(state => state.server.server);
|
||||||
const msg = `[ ](${ server }/direct/${ username }?msg=${ message._id }) `;
|
const msg = `[ ](${ server }/direct/${ username }?msg=${ message._id }) `;
|
||||||
yield put(setInput({ msg }));
|
yield put(setInput({ msg }));
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
|
||||||
import { messagesRequest, editCancel } from '../actions/messages';
|
import { messagesRequest, editCancel } from '../actions/messages';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import * as NavigationService from '../containers/routes/NavigationService';
|
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
import { NavigationActions } from '../Navigation';
|
||||||
|
|
||||||
const leaveRoom = rid => RocketChat.leaveRoom(rid);
|
const leaveRoom = rid => RocketChat.leaveRoom(rid);
|
||||||
const eraseRoom = rid => RocketChat.eraseRoom(rid);
|
const eraseRoom = rid => RocketChat.eraseRoom(rid);
|
||||||
|
@ -139,7 +139,7 @@ const updateLastOpen = function* updateLastOpen() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
|
const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
|
||||||
NavigationService.goRoomsList();
|
NavigationActions.popToRoot();
|
||||||
yield delay(1000);
|
yield delay(1000);
|
||||||
try {
|
try {
|
||||||
database.write(() => {
|
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 { delay } from 'redux-saga';
|
||||||
import { AsyncStorage } from 'react-native';
|
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 * as actions from '../actions';
|
||||||
import { connectRequest } from '../actions/connect';
|
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 { setRoles } from '../actions/roles';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import { navigate } from '../containers/routes/NavigationService';
|
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
import I18n from '../i18n';
|
||||||
|
|
||||||
const validate = function* validate(server) {
|
const validate = function* validate(server) {
|
||||||
return yield RocketChat.testServer(server);
|
return yield RocketChat.testServer(server);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectServer = function* selectServer({ server }) {
|
const handleSelectServer = function* handleSelectServer({ server }) {
|
||||||
try {
|
try {
|
||||||
yield database.setActiveDB(server);
|
yield database.setActiveDB(server);
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ const selectServer = function* selectServer({ server }) {
|
||||||
|
|
||||||
yield put(connectRequest());
|
yield put(connectRequest());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('selectServer', e);
|
log('handleSelectServer', e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,12 +54,12 @@ const validateServer = function* validateServer({ server }) {
|
||||||
|
|
||||||
const addServer = function* addServer({ server }) {
|
const addServer = function* addServer({ server }) {
|
||||||
try {
|
try {
|
||||||
|
yield put(actions.appStart('outside'));
|
||||||
|
yield call(NavigationActions.resetTo, { screen: 'ListServerView', title: I18n.t('Servers') });
|
||||||
database.databases.serversDB.write(() => {
|
database.databases.serversDB.write(() => {
|
||||||
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
||||||
});
|
});
|
||||||
yield put(setServer(server));
|
yield put(selectServer(server));
|
||||||
yield take(LOGIN.SET_TOKEN);
|
|
||||||
navigate('LoginSignup');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('addServer', e);
|
log('addServer', e);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,7 @@ const addServer = function* addServer({ server }) {
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
yield takeLatest(SERVER.REQUEST, validateServer);
|
yield takeLatest(SERVER.REQUEST, validateServer);
|
||||||
yield takeLatest(SERVER.SELECT, selectServer);
|
yield takeLatest(SERVER.SELECT, handleSelectServer);
|
||||||
yield takeLatest(SERVER.ADD, addServer);
|
yield takeLatest(SERVER.ADD, addServer);
|
||||||
};
|
};
|
||||||
export default root;
|
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 Button from '../containers/Button';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
|
||||||
@connect(
|
@connect(state => ({
|
||||||
state => ({
|
|
||||||
createChannel: state.createChannel,
|
createChannel: state.createChannel,
|
||||||
users: state.selectedUsers.users
|
users: state.selectedUsers.users
|
||||||
}),
|
}), dispatch => ({
|
||||||
dispatch => ({
|
|
||||||
create: data => dispatch(createChannelRequest(data))
|
create: data => dispatch(createChannelRequest(data))
|
||||||
})
|
}))
|
||||||
)
|
/** @extends React.Component */
|
||||||
export default class CreateChannelView extends LoggedView {
|
export default class CreateChannelView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigator: PropTypes.object,
|
||||||
create: PropTypes.func.isRequired,
|
create: PropTypes.func.isRequired,
|
||||||
createChannel: PropTypes.object.isRequired,
|
createChannel: PropTypes.object.isRequired,
|
||||||
users: PropTypes.array.isRequired,
|
users: PropTypes.array.isRequired
|
||||||
navigation: PropTypes.object.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
|
@ -20,12 +20,13 @@ import I18n from '../i18n';
|
||||||
forgotPasswordInit: () => dispatch(forgotPasswordInit()),
|
forgotPasswordInit: () => dispatch(forgotPasswordInit()),
|
||||||
forgotPasswordRequest: email => dispatch(forgotPasswordRequest(email))
|
forgotPasswordRequest: email => dispatch(forgotPasswordRequest(email))
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class ForgotPasswordView extends LoggedView {
|
export default class ForgotPasswordView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigator: PropTypes.object,
|
||||||
forgotPasswordInit: PropTypes.func.isRequired,
|
forgotPasswordInit: PropTypes.func.isRequired,
|
||||||
forgotPasswordRequest: PropTypes.func.isRequired,
|
forgotPasswordRequest: PropTypes.func.isRequired,
|
||||||
login: PropTypes.object,
|
login: PropTypes.object
|
||||||
navigation: PropTypes.object.isRequired
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -44,7 +45,7 @@ export default class ForgotPasswordView extends LoggedView {
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
const { login } = this.props;
|
const { login } = this.props;
|
||||||
if (login.success) {
|
if (login.success) {
|
||||||
this.props.navigation.goBack();
|
this.props.navigator.pop();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert'));
|
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 Icon from 'react-native-vector-icons/Ionicons';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
// import Zeroconf from 'react-native-zeroconf';
|
import { View, Text, SectionList, StyleSheet } from 'react-native';
|
||||||
import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withNavigationFocus } from 'react-navigation';
|
|
||||||
|
|
||||||
import LoggedView from './View';
|
import LoggedView from './View';
|
||||||
import { setServer } from '../actions/server';
|
import { selectServer } from '../actions/server';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import Fade from '../animations/fade';
|
import Fade from '../animations/fade';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
import { iconsMap } from '../Icons';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
view: {
|
view: {
|
||||||
|
@ -63,22 +62,19 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// const zeroconf = new Zeroconf();
|
|
||||||
|
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
login: state.login,
|
login: state.login,
|
||||||
connected: state.meteor.connected
|
connected: state.meteor.connected
|
||||||
}), dispatch => ({
|
}), 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 = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object.isRequired,
|
navigator: PropTypes.object,
|
||||||
login: PropTypes.object.isRequired,
|
login: PropTypes.object.isRequired,
|
||||||
selectServer: PropTypes.func.isRequired,
|
selectServer: PropTypes.func.isRequired,
|
||||||
connected: PropTypes.bool.isRequired,
|
|
||||||
server: PropTypes.string
|
server: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,51 +84,36 @@ class ListServerView extends LoggedView {
|
||||||
sections: []
|
sections: []
|
||||||
};
|
};
|
||||||
this.data = database.databases.serversDB.objects('servers');
|
this.data = database.databases.serversDB.objects('servers');
|
||||||
// this.redirected = false;
|
|
||||||
this.data.addListener(this.updateState);
|
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() {
|
componentDidMount() {
|
||||||
// zeroconf.on('update', this.updateState);
|
|
||||||
// zeroconf.scan('http', 'tcp', 'local.');
|
|
||||||
this.updateState();
|
this.updateState();
|
||||||
this.jumpToSelectedServer();
|
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() {
|
componentWillUnmount() {
|
||||||
// zeroconf.stop();
|
|
||||||
this.data.removeAllListeners();
|
this.data.removeAllListeners();
|
||||||
// zeroconf.removeListener('update', this.updateState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openLogin = () => {
|
onNavigatorEvent(event) {
|
||||||
this.props.navigation.navigate({ key: 'LoginSignup', routeName: 'LoginSignup' });
|
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'),
|
title: I18n.t('My_servers'),
|
||||||
data: this.data
|
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 {
|
return {
|
||||||
...this.state,
|
...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 = () => {
|
updateState = () => {
|
||||||
this.setState(this.getState());
|
this.setState(this.getState());
|
||||||
}
|
}
|
||||||
|
@ -210,7 +193,7 @@ class ListServerView extends LoggedView {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.view} testID='list-server-view'>
|
<View style={styles.view} testID='list-server-view'>
|
||||||
<SectionList
|
<SectionList
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
sections={this.state.sections}
|
sections={this.state.sections}
|
||||||
|
@ -219,8 +202,7 @@ class ListServerView extends LoggedView {
|
||||||
keyExtractor={item => item.id}
|
keyExtractor={item => item.id}
|
||||||
ItemSeparatorComponent={this.renderSeparator}
|
ItemSeparatorComponent={this.renderSeparator}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default withNavigationFocus(ListServerView);
|
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
import Modal from 'react-native-modal';
|
|
||||||
|
|
||||||
import RocketChat from '../lib/rocketchat';
|
|
||||||
import { open, close } from '../actions/login';
|
import { open, close } from '../actions/login';
|
||||||
import LoggedView from './View';
|
import LoggedView from './View';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
|
@ -17,9 +15,6 @@ import Button from '../containers/Button';
|
||||||
import Loading from '../containers/Loading';
|
import Loading from '../containers/Loading';
|
||||||
import I18n from '../i18n';
|
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({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -44,14 +39,13 @@ const styles = StyleSheet.create({
|
||||||
planetImage: {
|
planetImage: {
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 162,
|
height: 162,
|
||||||
marginVertical: 20,
|
marginVertical: 20
|
||||||
opacity: 0.6
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
login: state.login,
|
isFetching: state.login.isFetching,
|
||||||
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
|
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
|
||||||
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
|
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
|
||||||
Accounts_OAuth_Facebook: state.settings.Accounts_OAuth_Facebook,
|
Accounts_OAuth_Facebook: state.settings.Accounts_OAuth_Facebook,
|
||||||
|
@ -63,17 +57,16 @@ const styles = StyleSheet.create({
|
||||||
Accounts_OAuth_Twitter: state.settings.Accounts_OAuth_Twitter,
|
Accounts_OAuth_Twitter: state.settings.Accounts_OAuth_Twitter,
|
||||||
services: state.login.services
|
services: state.login.services
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
loginOAuth: params => RocketChat.login(params),
|
|
||||||
open: () => dispatch(open()),
|
open: () => dispatch(open()),
|
||||||
close: () => dispatch(close())
|
close: () => dispatch(close())
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class LoginSignupView extends LoggedView {
|
export default class LoginSignupView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
loginOAuth: PropTypes.func.isRequired,
|
navigator: PropTypes.object,
|
||||||
open: PropTypes.func.isRequired,
|
open: PropTypes.func.isRequired,
|
||||||
close: PropTypes.func.isRequired,
|
close: PropTypes.func.isRequired,
|
||||||
navigation: PropTypes.object.isRequired,
|
isFetching: PropTypes.bool,
|
||||||
login: PropTypes.object,
|
|
||||||
server: PropTypes.string,
|
server: PropTypes.string,
|
||||||
Accounts_EmailOrUsernamePlaceholder: PropTypes.bool,
|
Accounts_EmailOrUsernamePlaceholder: PropTypes.bool,
|
||||||
Accounts_PasswordPlaceholder: PropTypes.string,
|
Accounts_PasswordPlaceholder: PropTypes.string,
|
||||||
|
@ -89,13 +82,6 @@ export default class LoginSignupView extends LoggedView {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super('LoginSignupView', props);
|
super('LoginSignupView', props);
|
||||||
|
|
||||||
this.state = {
|
|
||||||
modalVisible: false,
|
|
||||||
oAuthUrl: '',
|
|
||||||
showSocialButtons: false
|
|
||||||
};
|
|
||||||
this.redirectRegex = new RegExp(`(?=.*(${ this.props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -183,19 +169,29 @@ export default class LoginSignupView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
openOAuth = (oAuthUrl) => {
|
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 = () => {
|
register = () => {
|
||||||
this.props.navigation.navigate({ key: 'Register', routeName: 'Register' });
|
this.props.navigator.push({
|
||||||
}
|
screen: 'RegisterView',
|
||||||
|
title: this.props.server,
|
||||||
closeOAuth = () => {
|
backButtonTitle: I18n.t('Welcome')
|
||||||
this.setState({ modalVisible: false });
|
});
|
||||||
}
|
|
||||||
|
|
||||||
toggleSocialButtons = () => {
|
|
||||||
this.setState({ showSocialButtons: !this.state.showSocialButtons });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderServices = () => {
|
renderServices = () => {
|
||||||
|
@ -279,13 +275,11 @@ export default class LoginSignupView extends LoggedView {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
[
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
key='login-view'
|
|
||||||
style={[sharedStyles.container, sharedStyles.containerScrollView]}
|
style={[sharedStyles.container, sharedStyles.containerScrollView]}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<SafeAreaView testID='welcome-view'>
|
<View testID='welcome-view'>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Image
|
<Image
|
||||||
source={require('../static/images/logo.png')}
|
source={require('../static/images/logo.png')}
|
||||||
|
@ -301,44 +295,20 @@ export default class LoginSignupView extends LoggedView {
|
||||||
<Button
|
<Button
|
||||||
title={I18n.t('I_have_an_account')}
|
title={I18n.t('I_have_an_account')}
|
||||||
type='primary'
|
type='primary'
|
||||||
onPress={() => this.props.navigation.navigate({ key: 'Login', routeName: 'Login' })}
|
onPress={() => this.login()}
|
||||||
testID='welcome-view-login'
|
testID='welcome-view-login'
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
title={I18n.t('Create_account')}
|
title={I18n.t('Create_account')}
|
||||||
type='secondary'
|
type='secondary'
|
||||||
onPress={() => this.props.navigation.navigate({ key: 'Register', routeName: 'Register' })}
|
onPress={() => this.register()}
|
||||||
testID='welcome-view-register'
|
testID='welcome-view-register'
|
||||||
/>
|
/>
|
||||||
{this.renderServices()}
|
{this.renderServices()}
|
||||||
</View>
|
</View>
|
||||||
<Loading visible={this.props.login.isFetching} />
|
<Loading visible={this.props.isFetching} />
|
||||||
</SafeAreaView>
|
</View>
|
||||||
</ScrollView>,
|
</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>
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
import { Answers } from 'react-native-fabric';
|
import { Answers } from 'react-native-fabric';
|
||||||
|
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import KeyboardView from '../presentation/KeyboardView';
|
import KeyboardView from '../presentation/KeyboardView';
|
||||||
import TextInput from '../containers/TextInput';
|
import TextInput from '../containers/TextInput';
|
||||||
import CloseModalButton from '../containers/CloseModalButton';
|
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
import Loading from '../containers/Loading';
|
import Loading from '../containers/Loading';
|
||||||
import styles from './Styles';
|
import styles from './Styles';
|
||||||
|
@ -26,12 +25,19 @@ import I18n from '../i18n';
|
||||||
}), () => ({
|
}), () => ({
|
||||||
loginSubmit: params => RocketChat.loginWithPassword(params)
|
loginSubmit: params => RocketChat.loginWithPassword(params)
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class LoginView extends LoggedView {
|
export default class LoginView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigator: PropTypes.object,
|
||||||
loginSubmit: PropTypes.func.isRequired,
|
loginSubmit: PropTypes.func.isRequired,
|
||||||
navigation: PropTypes.object.isRequired,
|
|
||||||
login: PropTypes.object,
|
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) {
|
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 = () => {
|
renderTOTP = () => {
|
||||||
if (/totp/ig.test(this.props.error)) {
|
if (/totp/ig.test(this.props.error)) {
|
||||||
return (
|
return (
|
||||||
|
@ -84,8 +106,7 @@ export default class LoginView extends LoggedView {
|
||||||
key='login-view'
|
key='login-view'
|
||||||
>
|
>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<SafeAreaView testID='login-view'>
|
<View testID='login-view'>
|
||||||
<CloseModalButton navigation={this.props.navigation} />
|
|
||||||
<Text style={[styles.loginText, styles.loginTitle]}>Login</Text>
|
<Text style={[styles.loginText, styles.loginTitle]}>Login</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={I18n.t('Username')}
|
label={I18n.t('Username')}
|
||||||
|
@ -122,14 +143,14 @@ export default class LoginView extends LoggedView {
|
||||||
<Text
|
<Text
|
||||||
style={[styles.loginText, { marginTop: 10 }]}
|
style={[styles.loginText, { marginTop: 10 }]}
|
||||||
testID='login-view-register'
|
testID='login-view-register'
|
||||||
onPress={() => this.props.navigation.navigate('Register')}
|
onPress={() => this.register()}
|
||||||
>{I18n.t('New_in_RocketChat_question_mark')}
|
>{I18n.t('New_in_RocketChat_question_mark')}
|
||||||
<Text style={{ color: COLOR_BUTTON_PRIMARY }}>{I18n.t('Sign_Up')}
|
<Text style={{ color: COLOR_BUTTON_PRIMARY }}>{I18n.t('Sign_Up')}
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[styles.loginText, { marginTop: 20, fontSize: 13 }]}
|
style={[styles.loginText, { marginTop: 20, fontSize: 13 }]}
|
||||||
onPress={() => this.props.navigation.navigate('ForgotPassword')}
|
onPress={() => this.forgotPassword()}
|
||||||
testID='login-view-forgot-password'
|
testID='login-view-forgot-password'
|
||||||
>{I18n.t('Forgot_password')}
|
>{I18n.t('Forgot_password')}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -137,7 +158,7 @@ export default class LoginView extends LoggedView {
|
||||||
|
|
||||||
{this.props.failure ? <Text style={styles.error}>{this.props.reason}</Text> : null}
|
{this.props.failure ? <Text style={styles.error}>{this.props.reason}</Text> : null}
|
||||||
<Loading visible={this.props.isFetching} />
|
<Loading visible={this.props.isFetching} />
|
||||||
</SafeAreaView>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,21 +10,23 @@ import Message from '../../containers/message';
|
||||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
||||||
@connect(
|
@connect(state => ({
|
||||||
state => ({
|
|
||||||
messages: state.mentionedMessages.messages,
|
messages: state.mentionedMessages.messages,
|
||||||
ready: state.mentionedMessages.ready,
|
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 : ''
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
}),
|
}), dispatch => ({
|
||||||
dispatch => ({
|
|
||||||
openMentionedMessages: (rid, limit) => dispatch(openMentionedMessages(rid, limit)),
|
openMentionedMessages: (rid, limit) => dispatch(openMentionedMessages(rid, limit)),
|
||||||
closeMentionedMessages: () => dispatch(closeMentionedMessages())
|
closeMentionedMessages: () => dispatch(closeMentionedMessages())
|
||||||
})
|
}))
|
||||||
)
|
/** @extends React.Component */
|
||||||
export default class MentionedMessagesView extends LoggedView {
|
export default class MentionedMessagesView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
rid: PropTypes.string,
|
||||||
messages: PropTypes.array,
|
messages: PropTypes.array,
|
||||||
ready: PropTypes.bool,
|
ready: PropTypes.bool,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
|
@ -57,7 +59,7 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
load = () => {
|
load = () => {
|
||||||
this.props.openMentionedMessages(this.props.navigation.state.params.rid, this.limit);
|
this.props.openMentionedMessages(this.props.rid, this.limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
moreData = () => {
|
moreData = () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
|
|
||||||
import { serverRequest, addServer } from '../actions/server';
|
import { serverRequest, addServer } from '../actions/server';
|
||||||
|
@ -21,14 +21,15 @@ import I18n from '../i18n';
|
||||||
validateServer: url => dispatch(serverRequest(url)),
|
validateServer: url => dispatch(serverRequest(url)),
|
||||||
addServer: url => dispatch(addServer(url))
|
addServer: url => dispatch(addServer(url))
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class NewServerView extends LoggedView {
|
export default class NewServerView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigator: PropTypes.object,
|
||||||
validateServer: PropTypes.func.isRequired,
|
validateServer: PropTypes.func.isRequired,
|
||||||
addServer: PropTypes.func.isRequired,
|
addServer: PropTypes.func.isRequired,
|
||||||
validating: PropTypes.bool.isRequired,
|
validating: PropTypes.bool.isRequired,
|
||||||
validInstance: PropTypes.bool.isRequired,
|
validInstance: PropTypes.bool.isRequired,
|
||||||
addingServer: PropTypes.bool.isRequired,
|
addingServer: PropTypes.bool.isRequired
|
||||||
navigation: PropTypes.object.isRequired
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -36,11 +37,7 @@ export default class NewServerView extends LoggedView {
|
||||||
this.state = {
|
this.state = {
|
||||||
defaultServer: 'https://open.rocket.chat'
|
defaultServer: 'https://open.rocket.chat'
|
||||||
};
|
};
|
||||||
this.props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field
|
props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.input.focus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeText = (text) => {
|
onChangeText = (text) => {
|
||||||
|
@ -109,7 +106,7 @@ export default class NewServerView extends LoggedView {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<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>
|
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_in_your_server')}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputRef={e => this.input = e}
|
inputRef={e => this.input = e}
|
||||||
|
@ -132,7 +129,7 @@ export default class NewServerView extends LoggedView {
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Loading visible={this.props.addingServer} />
|
<Loading visible={this.props.addingServer} />
|
||||||
</SafeAreaView>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</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 CANCEL_INDEX = 1;
|
||||||
const options = [I18n.t('Unpin'), I18n.t('Cancel')];
|
const options = [I18n.t('Unpin'), I18n.t('Cancel')];
|
||||||
|
|
||||||
@connect(
|
@connect(state => ({
|
||||||
state => ({
|
|
||||||
messages: state.pinnedMessages.messages,
|
messages: state.pinnedMessages.messages,
|
||||||
ready: state.pinnedMessages.ready,
|
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 : ''
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
}),
|
}), dispatch => ({
|
||||||
dispatch => ({
|
|
||||||
openPinnedMessages: (rid, limit) => dispatch(openPinnedMessages(rid, limit)),
|
openPinnedMessages: (rid, limit) => dispatch(openPinnedMessages(rid, limit)),
|
||||||
closePinnedMessages: () => dispatch(closePinnedMessages()),
|
closePinnedMessages: () => dispatch(closePinnedMessages()),
|
||||||
togglePinRequest: message => dispatch(togglePinRequest(message))
|
togglePinRequest: message => dispatch(togglePinRequest(message))
|
||||||
})
|
}))
|
||||||
)
|
/** @extends React.Component */
|
||||||
export default class PinnedMessagesView extends LoggedView {
|
export default class PinnedMessagesView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
rid: PropTypes.string,
|
||||||
messages: PropTypes.array,
|
messages: PropTypes.array,
|
||||||
ready: PropTypes.bool,
|
ready: PropTypes.bool,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
|
@ -81,7 +83,7 @@ export default class PinnedMessagesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
load = () => {
|
load = () => {
|
||||||
this.props.openPinnedMessages(this.props.navigation.state.params.rid, this.limit);
|
this.props.openPinnedMessages(this.props.rid, this.limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
moreData = () => {
|
moreData = () => {
|
||||||
|
|
|
@ -3,7 +3,10 @@ import PropTypes from 'prop-types';
|
||||||
import { WebView } from 'react-native';
|
import { WebView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
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 = {
|
static propTypes = {
|
||||||
privacyPolicy: PropTypes.string
|
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 React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
import Dialog from 'react-native-dialog';
|
import Dialog from 'react-native-dialog';
|
||||||
import SHA256 from 'js-sha256';
|
import SHA256 from 'js-sha256';
|
||||||
|
@ -22,17 +22,24 @@ import I18n from '../../i18n';
|
||||||
import Button from '../../containers/Button';
|
import Button from '../../containers/Button';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
|
import { iconsMap } from '../../Icons';
|
||||||
|
|
||||||
@connect(state => ({
|
@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
|
Accounts_CustomFields: state.settings.Accounts_CustomFields
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class ProfileView extends LoggedView {
|
export default class ProfileView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigator: PropTypes.object,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
Accounts_CustomFields: PropTypes.string
|
Accounts_CustomFields: PropTypes.string
|
||||||
};
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super('ProfileView', props);
|
super('ProfileView', props);
|
||||||
|
@ -49,11 +56,26 @@ export default class ProfileView extends LoggedView {
|
||||||
avatarSuggestions: {},
|
avatarSuggestions: {},
|
||||||
customFields: {}
|
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() {
|
async componentDidMount() {
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
|
this.props.navigator.setDrawerEnabled({
|
||||||
|
side: 'left',
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await RocketChat.getAvatarSuggestion();
|
const result = await RocketChat.getAvatarSuggestion();
|
||||||
this.setState({ avatarSuggestions: result });
|
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) => {
|
init = (user) => {
|
||||||
const {
|
const {
|
||||||
name, username, emails, customFields
|
name, username, emails, customFields
|
||||||
|
@ -195,10 +233,6 @@ export default class ProfileView extends LoggedView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setAvatar = (avatar) => {
|
|
||||||
this.setState({ avatar });
|
|
||||||
}
|
|
||||||
|
|
||||||
resetAvatar = async() => {
|
resetAvatar = async() => {
|
||||||
try {
|
try {
|
||||||
await RocketChat.resetAvatar();
|
await RocketChat.resetAvatar();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
|
|
||||||
import { registerSubmit, setUsernameSubmit } from '../actions/login';
|
import { registerSubmit, setUsernameSubmit } from '../actions/login';
|
||||||
|
@ -10,7 +10,6 @@ import Loading from '../containers/Loading';
|
||||||
import KeyboardView from '../presentation/KeyboardView';
|
import KeyboardView from '../presentation/KeyboardView';
|
||||||
import styles from './Styles';
|
import styles from './Styles';
|
||||||
import { showToast } from '../utils/info';
|
import { showToast } from '../utils/info';
|
||||||
import CloseModalButton from '../containers/CloseModalButton';
|
|
||||||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||||
import LoggedView from './View';
|
import LoggedView from './View';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
@ -26,8 +25,11 @@ import I18n from '../i18n';
|
||||||
registerSubmit: params => dispatch(registerSubmit(params)),
|
registerSubmit: params => dispatch(registerSubmit(params)),
|
||||||
setUsernameSubmit: params => dispatch(setUsernameSubmit(params))
|
setUsernameSubmit: params => dispatch(setUsernameSubmit(params))
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class RegisterView extends LoggedView {
|
export default class RegisterView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigator: PropTypes.object,
|
||||||
|
server: PropTypes.string,
|
||||||
registerSubmit: PropTypes.func.isRequired,
|
registerSubmit: PropTypes.func.isRequired,
|
||||||
setUsernameSubmit: PropTypes.func,
|
setUsernameSubmit: PropTypes.func,
|
||||||
Accounts_UsernamePlaceholder: PropTypes.string,
|
Accounts_UsernamePlaceholder: PropTypes.string,
|
||||||
|
@ -88,11 +90,19 @@ export default class RegisterView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
termsService = () => {
|
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 = () => {
|
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() {
|
_renderRegister() {
|
||||||
|
@ -202,8 +212,7 @@ export default class RegisterView extends LoggedView {
|
||||||
return (
|
return (
|
||||||
<KeyboardView contentContainerStyle={styles.container}>
|
<KeyboardView contentContainerStyle={styles.container}>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<SafeAreaView testID='register-view'>
|
<View testID='register-view'>
|
||||||
<CloseModalButton navigation={this.props.navigation} />
|
|
||||||
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_Up')}</Text>
|
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_Up')}</Text>
|
||||||
{this._renderRegister()}
|
{this._renderRegister()}
|
||||||
{this._renderUsername()}
|
{this._renderUsername()}
|
||||||
|
@ -214,7 +223,7 @@ export default class RegisterView extends LoggedView {
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<Loading visible={this.props.login.isFetching} />
|
<Loading visible={this.props.login.isFetching} />
|
||||||
</SafeAreaView>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,6 @@ import Touch from '../../utils/touch';
|
||||||
import database from '../../lib/realm';
|
import database from '../../lib/realm';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import { leaveRoom } from '../../actions/room';
|
import { leaveRoom } from '../../actions/room';
|
||||||
import { setLoading } from '../../actions/selectedUsers';
|
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||||
import I18n from '../../i18n';
|
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>);
|
const getRoomTitle = room => (room.t === 'd' ? <Text>{room.fname}</Text> : <Text><RoomTypeIcon type={room.t} /> {room.name}</Text>);
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
user_id: state.login.user.id,
|
userId: state.login.user && state.login.user.id,
|
||||||
user_username: state.login.user.username
|
username: state.login.user && state.login.user.username
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
leaveRoom: rid => dispatch(leaveRoom(rid)),
|
leaveRoom: rid => dispatch(leaveRoom(rid))
|
||||||
setLoadingInvite: loading => dispatch(setLoading(loading))
|
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class RoomActionsView extends LoggedView {
|
export default class RoomActionsView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
baseUrl: PropTypes.string,
|
rid: PropTypes.string,
|
||||||
user: PropTypes.object,
|
navigator: PropTypes.object,
|
||||||
navigation: PropTypes.object,
|
userId: PropTypes.string,
|
||||||
|
username: PropTypes.string,
|
||||||
leaveRoom: PropTypes.func
|
leaveRoom: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super('RoomActionsView', props);
|
super('RoomActionsView', props);
|
||||||
const { rid } = props.navigation.state.params;
|
const { rid } = props;
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
[this.room] = this.rooms;
|
[this.room] = this.rooms;
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -63,59 +62,24 @@ export default class RoomActionsView extends LoggedView {
|
||||||
|
|
||||||
onPressTouchable = (item) => {
|
onPressTouchable = (item) => {
|
||||||
if (item.route) {
|
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) {
|
if (item.event) {
|
||||||
return 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
|
get canAddUser() { // Invite user
|
||||||
const {
|
const {
|
||||||
rid, t
|
rid, t
|
||||||
} = this.room;
|
} = this.room;
|
||||||
const { allMembers } = this.state;
|
const { allMembers } = this.state;
|
||||||
// TODO: same test joined
|
// 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);
|
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']) {
|
if (userInRoom && permissions['add-user-to-joined-room']) {
|
||||||
|
@ -149,8 +113,8 @@ export default class RoomActionsView extends LoggedView {
|
||||||
const sections = [{
|
const sections = [{
|
||||||
data: [{
|
data: [{
|
||||||
icon: 'ios-star',
|
icon: 'ios-star',
|
||||||
name: 'USER',
|
name: I18n.t('Room_Info'),
|
||||||
route: 'RoomInfo',
|
route: 'RoomInfoView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-info'
|
testID: 'room-actions-info'
|
||||||
}],
|
}],
|
||||||
|
@ -176,28 +140,28 @@ export default class RoomActionsView extends LoggedView {
|
||||||
{
|
{
|
||||||
icon: 'ios-attach',
|
icon: 'ios-attach',
|
||||||
name: I18n.t('Files'),
|
name: I18n.t('Files'),
|
||||||
route: 'RoomFiles',
|
route: 'RoomFilesView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-files'
|
testID: 'room-actions-files'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'ios-at-outline',
|
icon: 'ios-at-outline',
|
||||||
name: I18n.t('Mentions'),
|
name: I18n.t('Mentions'),
|
||||||
route: 'MentionedMessages',
|
route: 'MentionedMessagesView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-mentioned'
|
testID: 'room-actions-mentioned'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'ios-star-outline',
|
icon: 'ios-star-outline',
|
||||||
name: I18n.t('Starred'),
|
name: I18n.t('Starred'),
|
||||||
route: 'StarredMessages',
|
route: 'StarredMessagesView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-starred'
|
testID: 'room-actions-starred'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'ios-search',
|
icon: 'ios-search',
|
||||||
name: I18n.t('Search'),
|
name: I18n.t('Search'),
|
||||||
route: 'SearchMessages',
|
route: 'SearchMessagesView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-search'
|
testID: 'room-actions-search'
|
||||||
},
|
},
|
||||||
|
@ -210,14 +174,14 @@ export default class RoomActionsView extends LoggedView {
|
||||||
{
|
{
|
||||||
icon: 'ios-pin',
|
icon: 'ios-pin',
|
||||||
name: I18n.t('Pinned'),
|
name: I18n.t('Pinned'),
|
||||||
route: 'PinnedMessages',
|
route: 'PinnedMessagesView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-pinned'
|
testID: 'room-actions-pinned'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'ios-code',
|
icon: 'ios-code',
|
||||||
name: I18n.t('Snippets'),
|
name: I18n.t('Snippets'),
|
||||||
route: 'SnippetedMessages',
|
route: 'SnippetedMessagesView',
|
||||||
params: { rid },
|
params: { rid },
|
||||||
testID: 'room-actions-snippeted'
|
testID: 'room-actions-snippeted'
|
||||||
},
|
},
|
||||||
|
@ -254,7 +218,7 @@ export default class RoomActionsView extends LoggedView {
|
||||||
description: (onlineMembers.length === 1 ?
|
description: (onlineMembers.length === 1 ?
|
||||||
I18n.t('1_online_member') :
|
I18n.t('1_online_member') :
|
||||||
I18n.t('N_online_members', { n: onlineMembers.length })),
|
I18n.t('N_online_members', { n: onlineMembers.length })),
|
||||||
route: 'RoomMembers',
|
route: 'RoomMembersView',
|
||||||
params: { rid, members: onlineMembers },
|
params: { rid, members: onlineMembers },
|
||||||
testID: 'room-actions-members'
|
testID: 'room-actions-members'
|
||||||
});
|
});
|
||||||
|
@ -264,19 +228,10 @@ export default class RoomActionsView extends LoggedView {
|
||||||
actions.push({
|
actions.push({
|
||||||
icon: 'ios-person-add',
|
icon: 'ios-person-add',
|
||||||
name: I18n.t('Add_user'),
|
name: I18n.t('Add_user'),
|
||||||
route: 'SelectedUsers',
|
route: 'SelectedUsersView',
|
||||||
params: {
|
params: {
|
||||||
nextAction: async() => {
|
nextAction: 'ADD_USER',
|
||||||
try {
|
rid
|
||||||
this.props.setLoadingInvite(true);
|
|
||||||
await RocketChat.addUsersToRoom(rid);
|
|
||||||
this.props.navigation.goBack();
|
|
||||||
} catch (e) {
|
|
||||||
log('RoomActions Add User', e);
|
|
||||||
} finally {
|
|
||||||
this.props.setLoadingInvite(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
testID: 'room-actions-add-user'
|
testID: 'room-actions-add-user'
|
||||||
});
|
});
|
||||||
|
@ -298,6 +253,46 @@ export default class RoomActionsView extends LoggedView {
|
||||||
return sections;
|
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() => {
|
toggleBlockUser = async() => {
|
||||||
const { rid, blocker } = this.state.room;
|
const { rid, blocker } = this.state.room;
|
||||||
const { member } = this.state;
|
const { member } = this.state;
|
||||||
|
|
|
@ -10,25 +10,25 @@ import Message from '../../containers/message';
|
||||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
||||||
@connect(
|
@connect(state => ({
|
||||||
state => ({
|
|
||||||
messages: state.roomFiles.messages,
|
messages: state.roomFiles.messages,
|
||||||
ready: state.roomFiles.ready,
|
ready: state.roomFiles.ready,
|
||||||
user: state.login.user
|
user: {
|
||||||
// baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
id: state.login.user && state.login.user.id,
|
||||||
}),
|
username: state.login.user && state.login.user.username,
|
||||||
dispatch => ({
|
token: state.login.user && state.login.user.token
|
||||||
|
}
|
||||||
|
}), dispatch => ({
|
||||||
openRoomFiles: (rid, limit) => dispatch(openRoomFiles(rid, limit)),
|
openRoomFiles: (rid, limit) => dispatch(openRoomFiles(rid, limit)),
|
||||||
closeRoomFiles: () => dispatch(closeRoomFiles())
|
closeRoomFiles: () => dispatch(closeRoomFiles())
|
||||||
})
|
}))
|
||||||
)
|
/** @extends React.Component */
|
||||||
export default class RoomFilesView extends LoggedView {
|
export default class RoomFilesView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
rid: PropTypes.string,
|
||||||
messages: PropTypes.array,
|
messages: PropTypes.array,
|
||||||
ready: PropTypes.bool,
|
ready: PropTypes.bool,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
openRoomFiles: PropTypes.func,
|
openRoomFiles: PropTypes.func,
|
||||||
closeRoomFiles: PropTypes.func
|
closeRoomFiles: PropTypes.func
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ export default class RoomFilesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
load = () => {
|
load = () => {
|
||||||
this.props.openRoomFiles(this.props.navigation.state.params.rid, this.limit);
|
this.props.openRoomFiles(this.props.rid, this.limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
moreData = () => {
|
moreData = () => {
|
||||||
|
|
|
@ -34,18 +34,16 @@ const PERMISSIONS_ARRAY = [
|
||||||
PERMISSION_DELETE_P
|
PERMISSION_DELETE_P
|
||||||
];
|
];
|
||||||
|
|
||||||
@connect(null, dispatch => ({
|
/** @extends React.Component */
|
||||||
eraseRoom: rid => dispatch(eraseRoom(rid))
|
class RoomInfoEditView extends LoggedView {
|
||||||
}))
|
|
||||||
export default class RoomInfoEditView extends LoggedView {
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
rid: PropTypes.string,
|
||||||
eraseRoom: PropTypes.func
|
eraseRoom: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super('RoomInfoEditView', props);
|
super('RoomInfoEditView', props);
|
||||||
const { rid } = props.navigation.state.params;
|
const { rid } = props;
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
this.permissions = {};
|
this.permissions = {};
|
||||||
this.state = {
|
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 PropTypes from 'prop-types';
|
||||||
import { View, Text, ScrollView } from 'react-native';
|
import { View, Text, ScrollView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import LoggedView from '../View';
|
import LoggedView from '../View';
|
||||||
|
@ -12,11 +11,11 @@ import styles from './styles';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
import database from '../../lib/realm';
|
import database from '../../lib/realm';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import Touch from '../../utils/touch';
|
|
||||||
|
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
import { iconsMap } from '../../Icons';
|
||||||
|
|
||||||
const PERMISSION_EDIT_ROOM = 'edit-room';
|
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>
|
<Text testID='room-info-view-name' style={styles.roomTitle} key='room-info-name'>{room.name}</Text>
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
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,
|
activeUsers: state.activeUsers,
|
||||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||||
roles: state.roles
|
roles: state.roles
|
||||||
}))
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
export default class RoomInfoView extends LoggedView {
|
export default class RoomInfoView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
baseUrl: PropTypes.string,
|
navigator: PropTypes.object,
|
||||||
user: PropTypes.object,
|
rid: PropTypes.string,
|
||||||
navigation: PropTypes.object,
|
userId: PropTypes.string,
|
||||||
activeUsers: PropTypes.object,
|
activeUsers: PropTypes.object,
|
||||||
Message_TimeFormat: PropTypes.string,
|
Message_TimeFormat: PropTypes.string,
|
||||||
roles: PropTypes.object
|
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) {
|
constructor(props) {
|
||||||
super('RoomInfoView', props);
|
super('RoomInfoView', props);
|
||||||
const { rid } = props.navigation.state.params;
|
const { rid } = props;
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
this.sub = {
|
this.sub = {
|
||||||
unsubscribe: () => {}
|
unsubscribe: () => {}
|
||||||
|
@ -80,6 +58,7 @@ export default class RoomInfoView extends LoggedView {
|
||||||
roomUser: {},
|
roomUser: {},
|
||||||
roles: []
|
roles: []
|
||||||
};
|
};
|
||||||
|
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
@ -87,9 +66,10 @@ export default class RoomInfoView extends LoggedView {
|
||||||
this.rooms.addListener(this.updateRoom);
|
this.rooms.addListener(this.updateRoom);
|
||||||
|
|
||||||
// get user of room
|
// get user of room
|
||||||
|
if (this.state.room) {
|
||||||
if (this.state.room.t === 'd') {
|
if (this.state.room.t === 'd') {
|
||||||
try {
|
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 });
|
this.setState({ roomUser });
|
||||||
const username = this.state.room.name;
|
const username = this.state.room.name;
|
||||||
|
|
||||||
|
@ -112,7 +92,16 @@ export default class RoomInfoView extends LoggedView {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], this.state.room.rid);
|
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();
|
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) => {
|
getFullUserData = async(username) => {
|
||||||
try {
|
try {
|
||||||
const result = await RocketChat.subscribe('fullUserData', username);
|
const result = await RocketChat.subscribe('fullUserData', username);
|
||||||
|
@ -170,7 +173,6 @@ export default class RoomInfoView extends LoggedView {
|
||||||
if (!utcOffset) {
|
if (!utcOffset) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO: translate
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.item}>
|
<View style={styles.item}>
|
||||||
<Text style={styles.itemLabel}>{I18n.t('Timezone')}</Text>
|
<Text style={styles.itemLabel}>{I18n.t('Timezone')}</Text>
|
||||||
|
|
|
@ -1,57 +1,46 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { connect } from 'react-redux';
|
||||||
import ActionSheet from 'react-native-actionsheet';
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
|
||||||
import LoggedView from '../View';
|
import LoggedView from '../View';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import sharedStyles from '../Styles';
|
|
||||||
import RoomItem from '../../presentation/RoomItem';
|
import RoomItem from '../../presentation/RoomItem';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import { goRoom } from '../../containers/routes/NavigationService';
|
|
||||||
import database from '../../lib/realm';
|
import database from '../../lib/realm';
|
||||||
import { showToast } from '../../utils/info';
|
import { showToast } from '../../utils/info';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
user: state.login.user,
|
|
||||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
}))
|
}))
|
||||||
export default class MentionedMessagesView extends LoggedView {
|
/** @extends React.Component */
|
||||||
static propTypes = {
|
export default class RoomMembersView extends LoggedView {
|
||||||
navigation: PropTypes.object
|
static navigatorButtons = {
|
||||||
}
|
rightButtons: [{
|
||||||
|
title: 'All',
|
||||||
|
id: 'toggleOnline',
|
||||||
|
testID: 'room-members-view-toggle-status'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
static navigationOptions = ({ navigation }) => {
|
static propTypes = {
|
||||||
const params = navigation.state.params || {};
|
navigator: PropTypes.object,
|
||||||
const label = params.allUsers ? I18n.t('All') : I18n.t('Online');
|
rid: PropTypes.string,
|
||||||
if (params.allUsers === undefined) {
|
members: PropTypes.array,
|
||||||
return;
|
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) {
|
constructor(props) {
|
||||||
super('MentionedMessagesView', props);
|
super('MentionedMessagesView', props);
|
||||||
this.CANCEL_INDEX = 0;
|
this.CANCEL_INDEX = 0;
|
||||||
this.MUTE_INDEX = 1;
|
this.MUTE_INDEX = 1;
|
||||||
this.actionSheetOptions = [''];
|
this.actionSheetOptions = [''];
|
||||||
const { rid, members } = props.navigation.state.params;
|
const { rid, members } = props;
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
this.permissions = RocketChat.hasPermission(['mute-user'], rid);
|
this.permissions = RocketChat.hasPermission(['mute-user'], rid);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -63,13 +52,10 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
userLongPressed: {},
|
userLongPressed: {},
|
||||||
room: {}
|
room: {}
|
||||||
};
|
};
|
||||||
|
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.navigation.setParams({
|
|
||||||
onPressToogleStatus: this.onPressToogleStatus,
|
|
||||||
allUsers: this.state.allUsers
|
|
||||||
});
|
|
||||||
this.rooms.addListener(this.updateRoom);
|
this.rooms.addListener(this.updateRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +63,26 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
this.rooms.removeAllListeners();
|
this.rooms.removeAllListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRoom = async() => {
|
async onNavigatorEvent(event) {
|
||||||
const [room] = this.rooms;
|
if (event.type === 'NavBarButtonPress') {
|
||||||
await this.setState({ room });
|
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) => {
|
onSearchChangeText = (text) => {
|
||||||
|
@ -90,26 +93,14 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
this.setState({ filtering: !!text, membersFiltered });
|
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) => {
|
onPressUser = async(item) => {
|
||||||
try {
|
try {
|
||||||
const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username);
|
const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username);
|
||||||
if (subscriptions.length) {
|
if (subscriptions.length) {
|
||||||
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
this.goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||||
} else {
|
} else {
|
||||||
const room = await RocketChat.createDirectMessage(item.username);
|
const room = await RocketChat.createDirectMessage(item.username);
|
||||||
goRoom({ rid: room.rid, name: item.username });
|
this.goRoom({ rid: room.rid, name: item.username });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('onPressUser', e);
|
log('onPressUser', e);
|
||||||
|
@ -134,6 +125,26 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
this.ActionSheet.show();
|
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() => {
|
handleMute = async() => {
|
||||||
const { rid, userLongPressed } = this.state;
|
const { rid, userLongPressed } = this.state;
|
||||||
try {
|
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}
|
onEndReachedThreshold={100}
|
||||||
renderFooter={this.props.renderFooter}
|
renderFooter={this.props.renderFooter}
|
||||||
renderHeader={() => <Typing />}
|
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}
|
dataSource={this.dataSource}
|
||||||
renderRow={(item, previousItem) => this.props.renderRow(item, previousItem)}
|
renderRow={(item, previousItem) => this.props.renderRow(item, previousItem)}
|
||||||
initialListSize={20}
|
initialListSize={20}
|
||||||
|
|