Skip to content

Commit

Permalink
MM-35115 [Gekidou] Login flow - Email and Password (#5402)
Browse files Browse the repository at this point in the history
* 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 d87cc23.

* 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>
  • Loading branch information
3 people authored Jun 18, 2021
1 parent 8742a2b commit 3ee6e67
Show file tree
Hide file tree
Showing 207 changed files with 22,094 additions and 2,296 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ end_of_line = lf
insert_final_newline = true
charset = utf-8

[*.{js,jsx,json,html}]
[*.{js,jsx,json,html,ts,tsx}]
indent_style = space
indent_size = 4

Expand Down
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"extends": [
"plugin:mattermost/react",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": [
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,4 @@ detox/artifacts

#editor-settings
.vscode
.scannerwork
22 changes: 10 additions & 12 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -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" />
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
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;
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
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;
Expand All @@ -35,11 +37,10 @@

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";
Expand All @@ -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();
}

Expand Down Expand Up @@ -106,8 +108,10 @@ public void onReceived() throws InvalidNotificationException {
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) {
Expand All @@ -127,6 +131,11 @@ public void reject(String code, String message) {
// 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();

Expand Down Expand Up @@ -465,15 +474,18 @@ private void setNotificationDeleteIntent(Notification.Builder notification, int

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,
Expand Down Expand Up @@ -540,8 +552,8 @@ private String removeSenderNameFromMessage(String message, String senderName) {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@

import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
import org.unimodules.core.interfaces.SingletonModule;

import com.swmansion.reanimated.ReanimatedJSIModulePackage;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ public void onReceive(Context context, Intent intent) {

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@
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) {
Expand Down
5 changes: 1 addition & 4 deletions android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
@@ -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">
Expand Down
43 changes: 43 additions & 0 deletions app/client/rest/apps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {buildQueryString} from '@utils/helpers';

export interface ClientAppsMix {
executeAppCall: (call: AppCallRequest, type: AppCallType) => Promise<AppCallResponse>;
getAppsBindings: (userID: string, channelID: string, teamID: string) => Promise<AppBinding[]>;
}

const ClientApps = (superclass: any) => class extends superclass {
executeAppCall = async (call: AppCallRequest, type: AppCallType) => {
const callCopy = {
...call,
path: `${call.path}/${type}`,
context: {
...call.context,
user_agent: 'mobile',
},
};

return this.doFetch(
`${this.getAppsProxyRoute()}/api/v1/call`,
{method: 'post', body: JSON.stringify(callCopy)},
);
}

getAppsBindings = async (userID: string, channelID: string, teamID: string) => {
const params = {
user_id: userID,
channel_id: channelID,
team_id: teamID,
user_agent: 'mobile',
};

return this.doFetch(
`${this.getAppsProxyRoute()}/api/v1/bindings${buildQueryString(params)}`,
{method: 'get'},
);
}
};

export default ClientApps;
Loading

0 comments on commit 3ee6e67

Please sign in to comment.