From 1b2706e60a4a2265b1e1052dfe51f31af74195ba Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Tue, 28 Sep 2021 13:12:44 -0300 Subject: [PATCH 01/11] [FIX] deeplinking to thread --- app/sagas/deepLinking.js | 12 ++++++++---- app/views/RoomView/index.js | 8 ++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index efad7afaeb8..7c692c066e5 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -45,8 +45,12 @@ const navigate = function* navigate({ params }) { if (params.path || params.rid) { let type; let name; + let jumpToThreadId; if (params.path) { - [type, name] = params.path.split('/'); + const pathSplitted = params.path.split('/'); + type = pathSplitted[0]; + name = pathSplitted[1]; + jumpToThreadId = pathSplitted.includes('thread') ? pathSplitted[pathSplitted.length - 1] : null; } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); @@ -65,14 +69,14 @@ const navigate = function* navigate({ params }) { if (focusedRooms.includes(room.rid)) { // if there's one room on the list or last room is the one if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) { - yield goRoom({ item, isMasterDetail, jumpToMessageId }); + yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId }); } else { popToRoot({ isMasterDetail }); - yield goRoom({ item, isMasterDetail, jumpToMessageId }); + yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId }); } } else { popToRoot({ isMasterDetail }); - yield goRoom({ item, isMasterDetail, jumpToMessageId }); + yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId }); } if (params.isCall) { diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index d7c9d152571..0898a654bf8 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -148,6 +148,7 @@ class RoomView extends React.Component { prid }; this.jumpToMessageId = props.route.params?.jumpToMessageId; + this.jumpToThreadId = props.route.params?.jumpToThreadId; const roomUserId = props.route.params?.roomUserId ?? RocketChat.getUidDirectMessage(room); this.state = { joined: true, @@ -208,6 +209,9 @@ class RoomView extends React.Component { if (this.jumpToMessageId) { this.jumpToMessage(this.jumpToMessageId); } + if (this.jumpToThreadId && !this.jumpToMessageId) { + this.navToThread({ tmid: this.jumpToThreadId }); + } if (isIOS && this.rid) { this.updateUnreadCount(); } @@ -253,6 +257,10 @@ class RoomView extends React.Component { this.jumpToMessage(route?.params?.jumpToMessageId); } + if (route?.params?.jumpToThreadId !== prevProps.route?.params?.jumpToThreadId) { + this.navToThread({ tmid: this.jumpToThreadId }); + } + if (appState === 'foreground' && appState !== prevProps.appState && this.rid) { // Fire List.query() just to keep observables working if (this.list && this.list.current) { From 2ff8b3abcb7360f17adec8606178dd1961d02af2 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Tue, 28 Sep 2021 18:44:26 -0300 Subject: [PATCH 02/11] fix how to find threadId --- app/sagas/deepLinking.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 7c692c066e5..38d330a7795 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -50,7 +50,9 @@ const navigate = function* navigate({ params }) { const pathSplitted = params.path.split('/'); type = pathSplitted[0]; name = pathSplitted[1]; - jumpToThreadId = pathSplitted.includes('thread') ? pathSplitted[pathSplitted.length - 1] : null; + // Check if has thread at path from params: channel/general/thread/threadId and expect that threadId is the next param + const threadIdIndexOf = pathSplitted.lastIndexOf('thread') + 1; + jumpToThreadId = threadIdIndexOf > 0 && threadIdIndexOf <= pathSplitted.length ? pathSplitted[threadIdIndexOf] : null; } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); From d0708211e40d6229874e27b5c6ec1a74fbc15b5a Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Tue, 28 Sep 2021 19:18:47 -0300 Subject: [PATCH 03/11] tmid as route params jumpToThreaId --- app/views/RoomView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 0898a654bf8..a9a750254c1 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -258,7 +258,7 @@ class RoomView extends React.Component { } if (route?.params?.jumpToThreadId !== prevProps.route?.params?.jumpToThreadId) { - this.navToThread({ tmid: this.jumpToThreadId }); + this.navToThread({ tmid: route?.params?.jumpToThreadId }); } if (appState === 'foreground' && appState !== prevProps.appState && this.rid) { From d5a6ac33392a2459fad4fb7418739d2b64ba967e Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Wed, 29 Sep 2021 11:26:10 -0300 Subject: [PATCH 04/11] minor tweak --- app/sagas/deepLinking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 38d330a7795..d6c942f2b8f 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -52,7 +52,7 @@ const navigate = function* navigate({ params }) { name = pathSplitted[1]; // Check if has thread at path from params: channel/general/thread/threadId and expect that threadId is the next param const threadIdIndexOf = pathSplitted.lastIndexOf('thread') + 1; - jumpToThreadId = threadIdIndexOf > 0 && threadIdIndexOf <= pathSplitted.length ? pathSplitted[threadIdIndexOf] : null; + jumpToThreadId = threadIdIndexOf > 0 && threadIdIndexOf < pathSplitted.length ? pathSplitted[threadIdIndexOf] : null; } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); From c8b7f043f516e3f1bc08816f61f29a954bcac76c Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 8 Oct 2021 12:12:11 -0300 Subject: [PATCH 05/11] minor tweak on logic --- app/sagas/deepLinking.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index d6c942f2b8f..46b14964cef 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -51,8 +51,7 @@ const navigate = function* navigate({ params }) { type = pathSplitted[0]; name = pathSplitted[1]; // Check if has thread at path from params: channel/general/thread/threadId and expect that threadId is the next param - const threadIdIndexOf = pathSplitted.lastIndexOf('thread') + 1; - jumpToThreadId = threadIdIndexOf > 0 && threadIdIndexOf < pathSplitted.length ? pathSplitted[threadIdIndexOf] : null; + jumpToThreadId = pathSplitted[3] && pathSplitted[2] === 'thread' ? pathSplitted[3] : null; } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); From 0d40b7d37e9046d932cbbe99a9e0e4eb3696d06a Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 8 Oct 2021 16:30:40 -0300 Subject: [PATCH 06/11] E2E Test --- e2e/data.js | 3 +++ e2e/helpers/data_setup.js | 5 +++-- e2e/tests/assorted/11-deeplinking.spec.js | 20 +++++++++++++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/e2e/data.js b/e2e/data.js index 979be71c7a4..800239e7607 100644 --- a/e2e/data.js +++ b/e2e/data.js @@ -44,6 +44,9 @@ const data = { }, alternate: { name: `detox-alternate-${value}` + }, + alternate2: { + name: `detox-alternate2-${value}` } }, teams: { diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js index 4c0f2d4e0dd..4b038e42ea3 100644 --- a/e2e/helpers/data_setup.js +++ b/e2e/helpers/data_setup.js @@ -116,11 +116,12 @@ const changeChannelJoinCode = async (roomId, joinCode) => { } }; -const sendMessage = async (user, channel, msg) => { +const sendMessage = async (user, channel, msg, tmid) => { console.log(`Sending message to ${channel}`); try { await login(user.username, user.password); - await rocketchat.post('chat.postMessage', { channel, msg }); + const response = await rocketchat.post('chat.postMessage', { channel, msg, tmid }); + return response.data; } catch (infoError) { console.log(JSON.stringify(infoError)); throw new Error('Failed to find or create private group'); diff --git a/e2e/tests/assorted/11-deeplinking.spec.js b/e2e/tests/assorted/11-deeplinking.spec.js index 27255be3c5e..135cd574e8b 100644 --- a/e2e/tests/assorted/11-deeplinking.spec.js +++ b/e2e/tests/assorted/11-deeplinking.spec.js @@ -1,6 +1,6 @@ const data = require('../../data'); const { tapBack, checkServer, navigateToRegister } = require('../../helpers/app'); -const { get, login } = require('../../helpers/data_setup'); +const { get, login, sendMessage } = require('../../helpers/data_setup'); const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' }; const getDeepLink = (method, server, params) => { @@ -12,9 +12,15 @@ const getDeepLink = (method, server, params) => { describe('Deep linking', () => { let userId; let authToken; + let threadId; + const threadMessage = `to-thread-${data.random}`; before(async () => { const loginResult = await login(data.users.regular.username, data.users.regular.password); ({ userId, authToken } = loginResult); + // create a thread with api + const result = await sendMessage(data.users.regular, data.groups.alternate2.name, threadMessage); + threadId = result.message._id; + await sendMessage(data.users.regular, result.message.rid, data.random, threadId); }); describe('Authentication', () => { @@ -87,6 +93,18 @@ describe('Deep linking', () => { .withTimeout(10000); }); + it('should navigate to the thread using path', async () => { + await device.launchApp({ + permissions: { notifications: 'YES' }, + newInstance: true, + url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.alternate2.name}/thread/${threadId}`), + sourceApp: 'com.apple.mobilesafari' + }); + await waitFor(element(by.id(`room-view-title-${threadMessage}`))) + .toExist() + .withTimeout(10000); + }); + it('should navigate to the room using rid', async () => { const roomResult = await get(`groups.info?roomName=${data.groups.private.name}`); await device.launchApp({ From a13eccc5b29882fab01f7c35465dba4d7a4ae048 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 8 Oct 2021 16:32:50 -0300 Subject: [PATCH 07/11] minor tweak --- app/sagas/deepLinking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 46b14964cef..df34e38684e 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -51,7 +51,7 @@ const navigate = function* navigate({ params }) { type = pathSplitted[0]; name = pathSplitted[1]; // Check if has thread at path from params: channel/general/thread/threadId and expect that threadId is the next param - jumpToThreadId = pathSplitted[3] && pathSplitted[2] === 'thread' ? pathSplitted[3] : null; + jumpToThreadId = !!pathSplitted[3] && pathSplitted[2] === 'thread' ? pathSplitted[3] : null; } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); From 2a3e778498457a491a18d05c13cef9c5a086ad76 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 8 Oct 2021 17:13:44 -0300 Subject: [PATCH 08/11] wait for the return of the thread --- app/views/RoomView/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index c09054e6f4d..c8eebe0f01e 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -879,7 +879,12 @@ class RoomView extends React.Component { if (item.tmid) { let name = item.tmsg; if (!name) { - name = await this.getThreadName(item.tmid, item.id); + const result = await this.getThreadName(item.tmid, item.id); + // test if there isn't a thread + if (!result) { + return; + } + name = result; } if (item.t === E2E_MESSAGE_TYPE && item.e2e !== E2E_STATUS.DONE) { name = I18n.t('Encrypted_message'); From aa0d7ec6aa0f7dcaebc875bc2505a8f72c5999cb Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 14 Oct 2021 10:06:43 -0300 Subject: [PATCH 09/11] Minor refactor --- app/sagas/deepLinking.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index df34e38684e..ceb7478aee4 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -47,11 +47,8 @@ const navigate = function* navigate({ params }) { let name; let jumpToThreadId; if (params.path) { - const pathSplitted = params.path.split('/'); - type = pathSplitted[0]; - name = pathSplitted[1]; - // Check if has thread at path from params: channel/general/thread/threadId and expect that threadId is the next param - jumpToThreadId = !!pathSplitted[3] && pathSplitted[2] === 'thread' ? pathSplitted[3] : null; + // Following this pattern: {channelType}/{channelName}/thread/{threadId} + [type, name, , jumpToThreadId] = params.path.split('/'); } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); From 2b56a7f1714c2e7b872b1d78d939d0aad3f2751f Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 14 Oct 2021 16:15:54 -0300 Subject: [PATCH 10/11] Fix e2e tests for docker --- e2e/data/data.docker.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/e2e/data/data.docker.js b/e2e/data/data.docker.js index 104007ce287..e9804e91c1d 100644 --- a/e2e/data/data.docker.js +++ b/e2e/data/data.docker.js @@ -45,6 +45,9 @@ const data = { }, alternate: { name: `detox-alternate-${value}` + }, + alternate2: { + name: `detox-alternate2-${value}` } }, teams: { From 1c1f273a6620d1702d611b1d6b30e44e0f979503 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 21 Oct 2021 12:37:18 -0300 Subject: [PATCH 11/11] popToRoot when focused and there is a jumpToThread --- app/sagas/deepLinking.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index ceb7478aee4..101747ccd4f 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -67,6 +67,11 @@ const navigate = function* navigate({ params }) { if (focusedRooms.includes(room.rid)) { // if there's one room on the list or last room is the one if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) { + if (jumpToThreadId) { + // With this conditional when there is a jumpToThreadId we can avoid the thread open again + // above other thread and the room could call again the thread + popToRoot({ isMasterDetail }); + } yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId }); } else { popToRoot({ isMasterDetail });