diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index efad7afa..101747cc 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -45,8 +45,10 @@ const navigate = function* navigate({ params }) { if (params.path || params.rid) { let type; let name; + let jumpToThreadId; if (params.path) { - [type, name] = params.path.split('/'); + // Following this pattern: {channelType}/{channelName}/thread/{threadId} + [type, name, , jumpToThreadId] = params.path.split('/'); } if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); @@ -65,14 +67,19 @@ 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 }); + 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 }); - 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 dab69746..241cd190 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -149,6 +149,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, @@ -209,6 +210,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(); } @@ -254,6 +258,10 @@ class RoomView extends React.Component { this.jumpToMessage(route?.params?.jumpToMessageId); } + if (route?.params?.jumpToThreadId !== prevProps.route?.params?.jumpToThreadId) { + this.navToThread({ tmid: route?.params?.jumpToThreadId }); + } + if (appState === 'foreground' && appState !== prevProps.appState && this.rid) { // Fire List.query() just to keep observables working if (this.list && this.list.current) { @@ -872,7 +880,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'); diff --git a/e2e/data.js b/e2e/data.js index 979be71c..800239e7 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/data/data.docker.js b/e2e/data/data.docker.js index 104007ce..e9804e91 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: { diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js index 4c0f2d4e..4b038e42 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 27255be3..135cd574 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({