forked from Ivasoft/mattermost-mobile
MM-35115 [Gekidou] Login flow - Email and Password (#5402)
* MM_35115: ADDED select_server screen
* MM_35115: ADDED select_server screen
* MM_35115: ADDED files on which select_server is dependent
* MM_35115: ADDED react-native-button
* MM_35115: Fixing TS issues [IN PROGRESS]
* MM_35115: Started withObservables [IN PROGRESS]
* MM_35115: Started withObservables [IN PROGRESS]
* MM_35115: withObservables - defaulting when no connection is available [IN PROGRESS]
* MM_35115: withObservables - some code clean up [IN PROGRESS]
* MM_35115: withObservables - some code clean up [IN PROGRESS]
* MM_35115: withObservables - some code clean up [IN PROGRESS]
* MM_35115: Substituting mapDispatchToProps [IN PROGRESS]
* MM_35115: Substituting mapDispatchToProps [IN PROGRESS]
* MM_35115: Substituting mapDispatchToProps [IN PROGRESS]
* MM_35115: Removed resetPing action [IN PROGRESS]
* MM_35115: ADDED app/client
* MM_35115: Preparing scheduleExpiredNotification
* MM_35115: Adding some todos
* Server & LoginOptions
* Use default server if available and autoconnect if configured
* Fix login header & manual server url
* MM_35115: Login Options[IN PROGRESS]
* MM_35115: Login screen - email [IN PROGRESS]
* MM_35115: Login screen - email [IN PROGRESS]
* MM_35115: Login screen - email - login api call [IN PROGRESS]
* MM_35115: Login screen - email - login api call [IN PROGRESS]
* MM_35115: Login screen - email - saving to server db [IN PROGRESS]
* MM_35115: Login screen - email - saving to System, Preferences to db [IN PROGRESS]
* MM_35115: Login screen - enforcing unique check on System entity [IN PROGRESS]
* MM_35115: Login screen - writing TeamMembership [IN PROGRESS]
* MM_35115: Login screen - writing Teams [IN PROGRESS]
* MM_35115: Login screen [IN PROGRESS]
* MM_35115: Login screen- Refactored DataOperator handlers [IN PROGRESS]
* MM_35115: Login screen - Proper clean up [IN PROGRESS]
* MM_35115: Login screen - completeLogin [IN PROGRESS]
* MM_35115: Improving DataOperator
* MM_35115: Improving DataOperator
* MM_35115: 80% DONE - login with email and password - some todos
* MM_35115: 80% DONE - login with email and password - some todos
* MM_35115: 80% DONE - login with email and password - some todos
* MM_35115: Removing unused app/queries folder
* MM_35115: Clean up
* MM_35115: Clean up
* MM_35115: Clean up
* MM_35115: Clean up
* MM_35115: Clean up
* MM_35115: Adding roles for MYTEAM
* MM_35115: Code clean up
* MM_35115: Code clean up
* MM_35115: Code clean up
* MM_35115: Adding rn-fetch-blob for Android
* MM_35115: Code clean up
* MM_35115: Code clean up
* MM_35115: Added test setup
* MM_35115: Fix database utils
* MM_35115: ADDED loadRolesIfNeeded
* MM_35115: Fix TS issue
* MM_35115: ADDED Tests setup
* MM_35115: Fix TS issues
* MM_35115: Fix TS issues
* MM_35115: Fix TS issues
* MM_35115: Added alternative to site name
* MM_35115: Added alternative to site name
* MM_35115: Removed hardcoded values
* MM_35115: Clean up
* MM_35115 - Fixed Android platform check instead of hermes
* MM_35115 - Replaced emptyErrorHandlingFunction with emptyFunction
* MM_35115 : Implemented TS fixes
* Update index.ts
* MM-35115 - Fix react-test-renderer issue
* MM_35115 - Optimizing DatabaseManager
* MM_35115 : Implemented getDatabaseConnection
* MM_35115 : Refactoring set/getActiveDatabase to use flag record
* MM_35115 : Refactored active database to use flag in Global entity
* MM_35115 : Updated manual database manager test
* MM_35115 : Fix operator/utils/test
* MM_35115 : Fix for base_handler
* MM_35115 : Fix test issues with Handlers
* MM_35115 : Fix test issues with prepareRecords
* MM_35115 : Fix wrapper test issue
* MM_35115 : Updated getMostRecentServerConnection to return the serverUrl as well as the connection
* MM_35115 : Refactored the way we call DataOperator
* MM_35115 : Updated database manager mock
* Add getMostRecentServerUrl function (#5440)
* fix: add getMostRecentServerUrl func
* fix: add ts and tsx to editorconfig
* fix: rename functions
* fix: return type
* Fix unit test setup
* fix login screen unit tests
* MM-36205 [GEKIDOU] Login Flow SSO (#5454)
* MM_35115: Starting LoginOptions SSO
* MM_36205: SSO [IN PROGRESS]
* MM_36205 : SSO [ IN PROGRESS ]
* Update sso_with_redirect_url.tsx
* MM_36205 : SSO Tests [ IN PROGRESS ]
* MM_36205 : Passing serverUrl to SSO screen
* Update sso.test.tsx
* Fix ViewTypes imports and keyMirror method
* MM_36205 : Code clean up
* Fix : Clean up imports
* Update: Aligning with PR 5452
* Fix: AndroidManifest file to include redirection ofr scheme mmauthbeta
* refactor: SSO Login method via Gitlab now navigates to Channel screen
* refactor: SSO Login without redirectURL is also working
* feat: SSO - main test completed
* feat: ADDED test for sso_with_redirect_url
* fix : eslint correction
* fix: Updated Loading component name
* fix : code clean up from reviews
* fix: reviews check
* fix: Added mmauthbeta into info.plist
* Revert "fix: Added mmauthbeta into info.plist"
This reverts commit d87cc23f5b.
* Update Info.plist
* Update AppDelegate.m
* feat: ADDED Forgot Password - Test [ IN PROGRESS ]
* feat: Forgot Password - Completed & Tested
* fix: Including MFA screen [ IN PROGRESS ]
* MFA - Properly tested
* Properly testing forgot_password screen
* Fix login.test.tsx
* Fix SSO method calls chain
* Update index.tsx
* Sort imports for sceen/navigation
* fix: Reviews
* Update signing + act in test
* Removed todo comment on MFA
* feedback review
* fix login tests
Co-authored-by: Avinash Lingaloo <>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
* App initialization refactor (#5430)
* fix: initial init refactor
* fix: await isServerPresent
* fix: more refactor
* fix: move out launch functions
* fix: remove comment
* fix: update credential functions
* fix: refactor launch functions
* fix: deep link parsing
* fix: lint change
* fix: update deeplink and notification handlers
* fix: indentation
* fix: add relaunchApp
* fix eslint
* refactor launchProps and autoconnect server for deeplink
* fix: use undefined
* fix: define OptionalLaunchProps
* fix: Android - handle server URL in push notification
* fix: rename func
* fix: use boolean launchError instead
* fix: use DatabaseModule
* fix: use DatabaseHelper instead
* fix: remove unnecessary null check
* fix: iOS - support for serverUrl
* fix: iOS - extract serverUrl in reply action
* fix: iOS - expose objc specific func
* fix: remove unnecessary deviceToken param
* fix: return if device is untrusted
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
* fix: bye bye modulePaths
* fix: ios build
* chore: remove unused aliases from babel.config
* chore: fix dependency format in package-lock.json
* chore: remove transparent window background color for android AppTheme
* chore: remove mattermost.js and use index.ts as app entry
* fix: login flow screens theme
* fix: Launch types
* chore: remove OptionalLaunchProps type
* fix: url utils unit tests
* chore: update en.json
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
Co-authored-by: Avinash Lingaloo <>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mattermost.rnbeta">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mattermost.rnbeta">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
@@ -44,17 +43,16 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="mattermost" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="mmauthbeta" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".NotificationDismissService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<receiver android:name=".NotificationReplyBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name="com.reactnativenavigation.controllers.NavigationActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:resizeableActivity="true"/>
|
||||
<service android:name=".NotificationDismissService" android:enabled="true" android:exported="false" />
|
||||
<receiver android:name=".NotificationReplyBroadcastReceiver" android:enabled="true" android:exported="false" />
|
||||
<activity android:name="com.reactnativenavigation.controllers.NavigationActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:resizeableActivity="true"/>
|
||||
<activity
|
||||
android:name="com.mattermost.share.ShareActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.mattermost.helpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
@@ -10,27 +7,9 @@ import com.oblador.keychain.KeychainModule;
|
||||
|
||||
|
||||
public class Credentials {
|
||||
static final String CURRENT_SERVER_URL = "@currentServerUrl";
|
||||
|
||||
public static void getCredentialsForCurrentServer(ReactApplicationContext context, ResolvePromise promise) {
|
||||
public static void getCredentialsForServer(ReactApplicationContext context, String serverUrl, ResolvePromise promise) {
|
||||
final KeychainModule keychainModule = new KeychainModule(context);
|
||||
final AsyncStorage asyncStorage = new AsyncStorage(context);
|
||||
final ArrayList<String> keys = new ArrayList<String>(1);
|
||||
keys.add(CURRENT_SERVER_URL);
|
||||
KeysReadableArray asyncStorageKeys = new KeysReadableArray() {
|
||||
@Override
|
||||
public int size() {
|
||||
return keys.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int index) {
|
||||
return keys.get(index);
|
||||
}
|
||||
};
|
||||
|
||||
HashMap<String, String> asyncStorageResults = asyncStorage.multiGet(asyncStorageKeys);
|
||||
String serverUrl = asyncStorageResults.get(CURRENT_SERVER_URL);
|
||||
final WritableMap options = Arguments.createMap();
|
||||
// KeyChain module fails if `authenticationPrompt` is not set
|
||||
final WritableMap authPrompt = Arguments.createMap();
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.mattermost.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.nozbe.watermelondb.Database;
|
||||
|
||||
public class DatabaseHelper {
|
||||
private static final String DEFAULT_DATABASE_NAME = "default.db";
|
||||
private static Database defaultDatabase;
|
||||
|
||||
private static void setDefaultDatabase(Context context) {
|
||||
String databaseName = Uri.fromFile(context.getFilesDir()).toString() + "/" + DEFAULT_DATABASE_NAME;
|
||||
defaultDatabase = new Database(databaseName, context);
|
||||
}
|
||||
|
||||
public static String getOnlyServerUrl(Context context) {
|
||||
if (defaultDatabase == null) {
|
||||
setDefaultDatabase(context);
|
||||
}
|
||||
|
||||
String emptyArray[] = {};
|
||||
String query = "SELECT url FROM servers";
|
||||
Cursor cursor = defaultDatabase.rawQuery(query, emptyArray);
|
||||
|
||||
if (cursor.getCount() == 1) {
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mattermost.helpers.DatabaseHelper;
|
||||
import com.mattermost.helpers.ResolvePromise;
|
||||
import com.wix.reactnativenotifications.core.notification.PushNotification;
|
||||
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
||||
import com.wix.reactnativenotifications.core.AppLaunchHelper;
|
||||
@@ -35,11 +37,10 @@ import com.wix.reactnativenotifications.core.JsIOHelper;
|
||||
|
||||
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
|
||||
|
||||
import com.mattermost.helpers.ResolvePromise;
|
||||
|
||||
public class CustomPushNotification extends PushNotification {
|
||||
public static final int MESSAGE_NOTIFICATION_ID = 435345;
|
||||
public static final String GROUP_KEY_MESSAGES = "mm_group_key_messages";
|
||||
public static final String NOTIFICATION = "notification";
|
||||
public static final String NOTIFICATION_ID = "notificationId";
|
||||
public static final String KEY_TEXT_REPLY = "CAN_REPLY";
|
||||
public static final String NOTIFICATION_REPLIED_EVENT_NAME = "notificationReplied";
|
||||
@@ -61,6 +62,7 @@ public class CustomPushNotification extends PushNotification {
|
||||
public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
|
||||
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
|
||||
this.context = context;
|
||||
|
||||
createNotificationChannels();
|
||||
}
|
||||
|
||||
@@ -106,8 +108,10 @@ public class CustomPushNotification extends PushNotification {
|
||||
final boolean isIdLoaded = initialData.getString("id_loaded") != null ? initialData.getString("id_loaded").equals("true") : false;
|
||||
int notificationId = MESSAGE_NOTIFICATION_ID;
|
||||
|
||||
if (ackId != null) {
|
||||
notificationReceiptDelivery(ackId, postId, type, isIdLoaded, new ResolvePromise() {
|
||||
String serverUrl = initialData.getString("server_url", DatabaseHelper.getOnlyServerUrl(context));
|
||||
|
||||
if (ackId != null && serverUrl != null) {
|
||||
notificationReceiptDelivery(ackId, serverUrl, postId, type, isIdLoaded, new ResolvePromise() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
if (isIdLoaded) {
|
||||
@@ -127,6 +131,11 @@ public class CustomPushNotification extends PushNotification {
|
||||
// so we fetch the bundle again
|
||||
final Bundle data = mNotificationProps.asBundle();
|
||||
|
||||
if (serverUrl == null) {
|
||||
String message = data.getString("message");
|
||||
data.putString("message", "Unknown Server\n" + message);
|
||||
}
|
||||
|
||||
if (channelId != null) {
|
||||
notificationId = channelId.hashCode();
|
||||
|
||||
@@ -465,15 +474,18 @@ public class CustomPushNotification extends PushNotification {
|
||||
|
||||
private void addNotificationReplyAction(Notification.Builder notification, int notificationId, Bundle bundle) {
|
||||
String postId = bundle.getString("post_id");
|
||||
String serverUrl = bundle.getString("server_url", DatabaseHelper.getOnlyServerUrl(context));
|
||||
|
||||
if (android.text.TextUtils.isEmpty(postId) || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
if (android.text.TextUtils.isEmpty(postId) ||
|
||||
serverUrl == null ||
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent replyIntent = new Intent(mContext, NotificationReplyBroadcastReceiver.class);
|
||||
replyIntent.setAction(KEY_TEXT_REPLY);
|
||||
replyIntent.putExtra(NOTIFICATION_ID, notificationId);
|
||||
replyIntent.putExtra("pushNotification", bundle);
|
||||
replyIntent.putExtra(NOTIFICATION, bundle);
|
||||
|
||||
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
|
||||
mContext,
|
||||
@@ -540,8 +552,8 @@ public class CustomPushNotification extends PushNotification {
|
||||
return message.replaceFirst(": ", "").trim();
|
||||
}
|
||||
|
||||
private void notificationReceiptDelivery(String ackId, String postId, String type, boolean isIdLoaded, ResolvePromise promise) {
|
||||
ReceiptDelivery.send(context, ackId, postId, type, isIdLoaded, promise);
|
||||
private void notificationReceiptDelivery(String ackId, String serverUrl, String postId, String type, boolean isIdLoaded, ResolvePromise promise) {
|
||||
ReceiptDelivery.send(context, ackId, serverUrl, postId, type, isIdLoaded, promise);
|
||||
}
|
||||
|
||||
private void createNotificationChannels() {
|
||||
|
||||
@@ -46,7 +46,6 @@ import com.facebook.soloader.SoLoader;
|
||||
|
||||
import org.unimodules.adapters.react.ModuleRegistryAdapter;
|
||||
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
|
||||
import org.unimodules.core.interfaces.SingletonModule;
|
||||
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
||||
|
||||
|
||||
@@ -48,9 +48,11 @@ public class NotificationReplyBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
||||
final int notificationId = intent.getIntExtra(CustomPushNotification.NOTIFICATION_ID, -1);
|
||||
final Bundle notification = intent.getBundleExtra(CustomPushNotification.NOTIFICATION);
|
||||
final String serverUrl = notification.getString("serverUrl");
|
||||
|
||||
|
||||
Credentials.getCredentialsForCurrentServer(reactApplicationContext, new ResolvePromise() {
|
||||
Credentials.getCredentialsForServer(reactApplicationContext, serverUrl, new ResolvePromise() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
if (value instanceof Boolean && !(Boolean)value) {
|
||||
|
||||
@@ -22,14 +22,12 @@ import com.facebook.react.bridge.WritableMap;
|
||||
import com.mattermost.helpers.*;
|
||||
|
||||
public class ReceiptDelivery {
|
||||
static final String CURRENT_SERVER_URL = "@currentServerUrl";
|
||||
|
||||
private static final int[] FIBONACCI_BACKOFFS = new int[] { 0, 1, 2, 3, 5, 8 };
|
||||
|
||||
public static void send(Context context, final String ackId, final String postId, final String type, final boolean isIdLoaded, ResolvePromise promise) {
|
||||
public static void send(Context context, final String ackId, final String serverUrl, final String postId, final String type, final boolean isIdLoaded, ResolvePromise promise) {
|
||||
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
||||
|
||||
Credentials.getCredentialsForCurrentServer(reactApplicationContext, new ResolvePromise() {
|
||||
Credentials.getCredentialsForServer(reactApplicationContext, serverUrl, new ResolvePromise() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
if (value instanceof Boolean && !(Boolean)value) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
</style>
|
||||
|
||||
<style name="LightTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
|
||||
Reference in New Issue
Block a user