From cb52219b89db6050562cc07296da7a2396400c00 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 17 Jul 2025 10:41:47 -0300 Subject: [PATCH] regression: Navigation issues after upgrading to react-navigation 7 (#6492) --- .detoxrc.js | 2 +- .../InAppNotification/NotifierComponent.tsx | 2 +- app/containers/MessageActions/index.tsx | 12 +- app/ee/omnichannel/views/QueueListView.tsx | 3 +- app/lib/methods/getThreadName.ts | 40 ++-- app/lib/methods/helpers/goRoom.ts | 30 +-- app/lib/methods/helpers/log/events.ts | 1 - app/lib/methods/subscriptions/room.ts | 188 ++++++------------ app/lib/methods/updateLastOpen.ts | 6 +- app/lib/navigation/appNavigation.ts | 47 ++++- app/sagas/createChannel.js | 2 +- app/sagas/deepLinking.js | 5 +- app/sagas/messages.js | 4 +- app/sagas/room.js | 6 +- app/views/CannedResponseDetail.tsx | 2 +- app/views/CannedResponsesListView/index.tsx | 2 +- .../utils/handleSubmitEvent.ts | 2 +- app/views/DirectoryView/index.tsx | 2 +- app/views/DiscussionsView/index.tsx | 14 +- app/views/MessagesView/index.tsx | 14 +- app/views/RoomInfoView/index.tsx | 14 +- app/views/RoomMembersView/helpers.ts | 2 +- app/views/RoomView/index.tsx | 3 +- app/views/SearchMessagesView/index.tsx | 12 +- app/views/TeamChannelsView.tsx | 2 +- app/views/ThreadMessagesView/index.tsx | 9 +- e2e/jest.config.js | 2 +- e2e/tests/onboarding/01-onboarding.spec.ts | 6 +- e2e/tests/room/05-threads.spec.ts | 9 +- 29 files changed, 210 insertions(+), 233 deletions(-) diff --git a/.detoxrc.js b/.detoxrc.js index 85280d12c51..6b9b20b72e3 100644 --- a/.detoxrc.js +++ b/.detoxrc.js @@ -10,7 +10,7 @@ module.exports = { artifacts: { plugins: { screenshot: 'failing', - video: 'all', + video: 'failing', uiHierarchy: process.env.CI ? undefined : 'enabled' } }, diff --git a/app/containers/InAppNotification/NotifierComponent.tsx b/app/containers/InAppNotification/NotifierComponent.tsx index c5793a7cab9..d055c270755 100644 --- a/app/containers/InAppNotification/NotifierComponent.tsx +++ b/app/containers/InAppNotification/NotifierComponent.tsx @@ -93,7 +93,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie prid }; - goRoom({ item, isMasterDetail, jumpToMessageId: _id, popToRoot: true }); + goRoom({ item, isMasterDetail, jumpToMessageId: _id }); hideNotification(); }; diff --git a/app/containers/MessageActions/index.tsx b/app/containers/MessageActions/index.tsx index 0b5f8d19920..d985ad0cac6 100644 --- a/app/containers/MessageActions/index.tsx +++ b/app/containers/MessageActions/index.tsx @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import moment from 'moment'; import database from '../../lib/database'; +import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription'; import I18n from '../../i18n'; import log, { logEvent } from '../../lib/methods/helpers/log'; import Navigation from '../../lib/navigation/appNavigation'; @@ -219,8 +220,11 @@ const MessageActions = React.memo( const db = database.active; const result = await Services.markAsUnread({ messageId }); if (result.success) { - const subCollection = db.get('subscriptions'); - const subRecord = await subCollection.find(rid); + const subRecord = await getSubscriptionByRoomId(rid); + if (!subRecord) { + return; + } + await db.write(async () => { try { await subRecord.update(sub => (sub.lastOpen = ts as Date)); // TODO: reevaluate IMessage @@ -228,11 +232,11 @@ const MessageActions = React.memo( // do nothing } }); - Navigation.navigate('RoomsListView'); } } catch (e) { - logEvent(events.ROOM_MSG_ACTION_UNREAD_F); log(e); + } finally { + Navigation.popToTop(isMasterDetail); } }; diff --git a/app/ee/omnichannel/views/QueueListView.tsx b/app/ee/omnichannel/views/QueueListView.tsx index 0b2e32e3f13..162993744dc 100644 --- a/app/ee/omnichannel/views/QueueListView.tsx +++ b/app/ee/omnichannel/views/QueueListView.tsx @@ -85,8 +85,7 @@ const QueueListView = React.memo(() => { // we're calling v as visitor on our mergeSubscriptionsRooms visitor: item.v }, - isMasterDetail, - popToRoot: true + isMasterDetail }); }; diff --git a/app/lib/methods/getThreadName.ts b/app/lib/methods/getThreadName.ts index a8c47933e3d..d232f79cf5b 100644 --- a/app/lib/methods/getThreadName.ts +++ b/app/lib/methods/getThreadName.ts @@ -16,30 +16,36 @@ const getThreadName = async (rid: string, tmid: string, messageId: string): Prom const db = database.active; const threadCollection = db.get('threads'); const messageRecord = await getMessageById(messageId); - const threadRecord = await getThreadById(tmid); + let threadRecord = await getThreadById(tmid); if (threadRecord) { tmsg = buildThreadName(threadRecord); - await db.write(async () => { - await messageRecord?.update(m => { - m.tmsg = tmsg; + if (tmsg !== messageRecord?.tmsg) { + await db.write(async () => { + await messageRecord?.update(m => { + m.tmsg = tmsg; + }); }); - }); + } } else { let thread = await getSingleMessage(tmid); thread = await Encryption.decryptMessage(thread); tmsg = buildThreadName(thread); - await db.write(async () => { - await db.batch( - threadCollection?.prepareCreate((t: TThreadModel) => { - t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema); - if (t.subscription) t.subscription.id = rid; - Object.assign(t, thread); - }), - messageRecord?.prepareUpdate(m => { - m.tmsg = tmsg; - }) - ); - }); + // check it again to avoid race condition + threadRecord = await getThreadById(tmid); + if (!threadRecord) { + await db.write(async () => { + await db.batch( + threadCollection?.prepareCreate((t: TThreadModel) => { + t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema); + if (t.subscription) t.subscription.id = rid; + Object.assign(t, thread); + }), + messageRecord?.prepareUpdate(m => { + m.tmsg = tmsg; + }) + ); + }); + } } } catch (e) { log(e); diff --git a/app/lib/methods/helpers/goRoom.ts b/app/lib/methods/helpers/goRoom.ts index 7367d3617c6..e3b40f89539 100644 --- a/app/lib/methods/helpers/goRoom.ts +++ b/app/lib/methods/helpers/goRoom.ts @@ -19,16 +19,7 @@ interface IGoRoomItem { export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription | IOmnichannelRoomVisitor; -const navigate = ({ - item, - isMasterDetail, - popToRoot, - ...props -}: { - item: TGoRoomItem; - isMasterDetail: boolean; - popToRoot: boolean; -}) => { +const navigate = ({ item, isMasterDetail, ...props }: { item: TGoRoomItem; isMasterDetail: boolean }) => { const routeParams = { rid: item.rid, name: getRoomTitle(item), @@ -40,10 +31,14 @@ const navigate = ({ ...props }; + const currentRoute = Navigation.getCurrentRoute() as any; + if (currentRoute?.name === 'RoomView' && currentRoute?.params?.rid === item.rid) { + Navigation.setParams(routeParams); + return; + } + + Navigation.popTo('DrawerNavigator'); if (isMasterDetail) { - if (popToRoot) { - Navigation.navigate('DrawerNavigator'); - } return Navigation.dispatch((state: any) => { const routesRoomView = state.routes.filter((r: any) => r.name !== 'RoomView'); return CommonActions.reset({ @@ -60,10 +55,6 @@ const navigate = ({ }); } - if (popToRoot) { - Navigation.popToTop(); - Navigation.back(); - } return Navigation.dispatch((state: any) => { const routesRoomsListView = state.routes.filter((r: any) => r.name === 'RoomsListView'); return CommonActions.reset({ @@ -88,14 +79,12 @@ interface IOmnichannelRoomVisitor extends IOmnichannelRoom { export const goRoom = async ({ item, isMasterDetail = false, - popToRoot = false, ...props }: { item: TGoRoomItem; isMasterDetail: boolean; jumpToMessageId?: string; usedCannedResponse?: string; - popToRoot?: boolean; }): Promise => { if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) { // if user is using the search we need first to join/create room @@ -110,7 +99,6 @@ export const goRoom = async ({ t: SubscriptionType.DIRECT }, isMasterDetail, - popToRoot, ...props }); } @@ -132,7 +120,7 @@ export const goRoom = async ({ } } - return navigate({ item: _item, isMasterDetail, popToRoot, ...props }); + return navigate({ item: _item, isMasterDetail, ...props }); }; export const navigateToRoom = navigate; diff --git a/app/lib/methods/helpers/log/events.ts b/app/lib/methods/helpers/log/events.ts index 3bda8de5900..03067acfc25 100644 --- a/app/lib/methods/helpers/log/events.ts +++ b/app/lib/methods/helpers/log/events.ts @@ -215,7 +215,6 @@ export default { ROOM_MSG_ACTION_PERMALINK_F: 'room_msg_action_permalink_f', ROOM_MSG_ACTION_DISCUSSION: 'room_msg_action_discussion', ROOM_MSG_ACTION_UNREAD: 'room_msg_action_unread', - ROOM_MSG_ACTION_UNREAD_F: 'room_msg_action_unread_f', ROOM_MSG_ACTION_COPY: 'room_msg_action_copy', ROOM_MSG_ACTION_SHARE: 'room_msg_action_share', ROOM_MSG_ACTION_SHARE_F: 'room_msg_action_share_f', diff --git a/app/lib/methods/subscriptions/room.ts b/app/lib/methods/subscriptions/room.ts index d501f41a4f4..32f1d1f8663 100644 --- a/app/lib/methods/subscriptions/room.ts +++ b/app/lib/methods/subscriptions/room.ts @@ -29,38 +29,18 @@ import { readMessages } from '../readMessages'; import { loadMissedMessages } from '../loadMissedMessages'; import { updateLastOpen } from '../updateLastOpen'; -const WINDOW_TIME = 1000; - export default class RoomSubscription { private rid: string; private isAlive: boolean; - private timer: ReturnType | null; - private queue: { [key: string]: IMessage }; - private messagesBatch: {}; - private _messagesBatch: { [key: string]: TMessageModel }; - private threadsBatch: {}; - private _threadsBatch: { [key: string]: TThreadModel }; - private threadMessagesBatch: {}; - private _threadMessagesBatch: { [key: string]: TThreadMessageModel }; private promises?: Promise; private connectedListener?: Promise; private disconnectedListener?: Promise; private notifyRoomListener?: Promise; private messageReceivedListener?: Promise; - private lastOpen?: Date; constructor(rid: string) { this.rid = rid; this.isAlive = true; - this.timer = null; - this.queue = {}; - this.messagesBatch = {}; - this.threadsBatch = {}; - this.threadMessagesBatch = {}; - - this._messagesBatch = {}; - this._threadsBatch = {}; - this._threadMessagesBatch = {}; } subscribe = async () => { @@ -99,9 +79,6 @@ export default class RoomSubscription { this.removeListener(this.disconnectedListener); this.removeListener(this.notifyRoomListener); this.removeListener(this.messageReceivedListener); - if (this.timer) { - clearTimeout(this.timer); - } }; removeListener = async (promise?: Promise): Promise => { @@ -119,9 +96,7 @@ export default class RoomSubscription { try { reduxStore.dispatch(clearUserTyping()); await loadMissedMessages({ rid: this.rid }); - const _lastOpen = new Date(); - this.read(_lastOpen); - this.lastOpen = _lastOpen; + this.read(); } catch (e) { log(e); } @@ -259,8 +234,8 @@ export default class RoomSubscription { } }); - read = debounce((lastOpen: Date) => { - readMessages(this.rid, lastOpen); + read = debounce(() => { + readMessages(this.rid, new Date()); }, 300); updateMessage = (message: IMessage): Promise => @@ -269,6 +244,7 @@ export default class RoomSubscription { return resolve(); } + const batch: TMessageModel[] | TThreadModel[] | TThreadMessageModel[] = []; const db = database.active; const msgCollection = db.get('messages'); const threadsCollection = db.get('threads'); @@ -279,24 +255,26 @@ export default class RoomSubscription { // Create or update message try { - let operation = null; const messageRecord = await getMessageById(message._id); if (messageRecord) { - operation = messageRecord.prepareUpdate( - protectedFunction((m: TMessageModel) => { - Object.assign(m, message); - }) + batch.push( + messageRecord.prepareUpdate( + protectedFunction((m: TMessageModel) => { + Object.assign(m, message); + }) + ) ); } else { - operation = msgCollection.prepareCreate( - protectedFunction((m: TMessageModel) => { - m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema); - if (m.subscription) m.subscription.id = this.rid; - Object.assign(m, message); - }) + batch.push( + msgCollection.prepareCreate( + protectedFunction((m: TMessageModel) => { + m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema); + if (m.subscription) m.subscription.id = this.rid; + Object.assign(m, message); + }) + ) ); } - this._messagesBatch[message._id] = operation; } catch (e) { log(e); } @@ -304,24 +282,26 @@ export default class RoomSubscription { // Create or update thread if (message.tlm) { try { - let operation = null; const threadRecord = await getThreadById(message._id); if (threadRecord) { - operation = threadRecord.prepareUpdate( - protectedFunction((t: TThreadModel) => { - Object.assign(t, message); - }) + batch.push( + threadRecord.prepareUpdate( + protectedFunction((t: TThreadModel) => { + Object.assign(t, message); + }) + ) ); } else { - operation = threadsCollection.prepareCreate( - protectedFunction((t: TThreadModel) => { - t._raw = sanitizedRaw({ id: message._id }, threadsCollection.schema); - if (t.subscription) t.subscription.id = this.rid; - Object.assign(t, message); - }) + batch.push( + threadsCollection.prepareCreate( + protectedFunction((t: TThreadModel) => { + t._raw = sanitizedRaw({ id: message._id }, threadsCollection.schema); + if (t.subscription) t.subscription.id = this.rid; + Object.assign(t, message); + }) + ) ); } - this._threadsBatch[message._id] = operation; } catch (e) { log(e); } @@ -330,89 +310,53 @@ export default class RoomSubscription { // Create or update thread message if (message.tmid) { try { - let operation = null; const threadMessageRecord = await getThreadMessageById(message._id); if (threadMessageRecord) { - operation = threadMessageRecord.prepareUpdate( - protectedFunction((tm: TThreadMessageModel) => { - Object.assign(tm, message); - if (message.tmid) { - tm.rid = message.tmid; - delete tm.tmid; - } - }) + batch.push( + threadMessageRecord.prepareUpdate( + protectedFunction((tm: TThreadMessageModel) => { + Object.assign(tm, message); + if (message.tmid) { + tm.rid = message.tmid; + delete tm.tmid; + } + }) + ) ); } else { - operation = threadMessagesCollection.prepareCreate( - protectedFunction((tm: TThreadMessageModel) => { - tm._raw = sanitizedRaw({ id: message._id }, threadMessagesCollection.schema); - Object.assign(tm, message); - if (tm.subscription) { - tm.subscription.id = this.rid; - } - if (message.tmid) { - tm.rid = message.tmid; - delete tm.tmid; - } - }) + batch.push( + threadMessagesCollection.prepareCreate( + protectedFunction((tm: TThreadMessageModel) => { + tm._raw = sanitizedRaw({ id: message._id }, threadMessagesCollection.schema); + Object.assign(tm, message); + if (tm.subscription) { + tm.subscription.id = this.rid; + } + if (message.tmid) { + tm.rid = message.tmid; + delete tm.tmid; + } + }) + ) ); } - this._threadMessagesBatch[message._id] = operation; } catch (e) { log(e); } } - return resolve(); + await db.write(async () => { + await db.batch(...batch); + }); }); - handleMessageReceived = (ddpMessage: IDDPMessage) => { - if (!this.timer) { - this.timer = setTimeout(async () => { - // copy variables values to local and clean them - const _lastOpen = this.lastOpen; - const _queue = Object.keys(this.queue).map(key => this.queue[key]); - this._messagesBatch = this.messagesBatch; - this._threadsBatch = this.threadsBatch; - this._threadMessagesBatch = this.threadMessagesBatch; - this.queue = {}; - this.messagesBatch = {}; - this.threadsBatch = {}; - this.threadMessagesBatch = {}; - this.timer = null; - - for (let i = 0; i < _queue.length; i += 1) { - try { - // eslint-disable-next-line no-await-in-loop - await this.updateMessage(_queue[i]); - } catch (e) { - log(e); - } - } - - try { - const db = database.active; - await db.write(async () => { - await db.batch( - ...Object.values(this._messagesBatch), - ...Object.values(this._threadsBatch), - ...Object.values(this._threadMessagesBatch) - ); - }); - - this.read(_lastOpen); - } catch (e) { - log(e); - } - - // Clean local variables - this._messagesBatch = {}; - this._threadsBatch = {}; - this._threadMessagesBatch = {}; - }, WINDOW_TIME); + handleMessageReceived = async (ddpMessage: IDDPMessage) => { + try { + const message = buildMessage(EJSON.fromJSONValue(ddpMessage.fields.args[0])) as IMessage; + await this.updateMessage(message); + this.read(); + } catch (e) { + log(e); } - this.lastOpen = new Date(); - const message = buildMessage(EJSON.fromJSONValue(ddpMessage.fields.args[0])) as IMessage; - this.queue[message._id] = message; }; } diff --git a/app/lib/methods/updateLastOpen.ts b/app/lib/methods/updateLastOpen.ts index 2aec8eb4cc5..4005d967c65 100644 --- a/app/lib/methods/updateLastOpen.ts +++ b/app/lib/methods/updateLastOpen.ts @@ -1,11 +1,15 @@ import database from '../database'; +import { getSubscriptionByRoomId } from '../database/services/Subscription'; import log from './helpers/log'; import { TSubscriptionModel } from '../../definitions'; export async function updateLastOpen(rid: string, lastOpen = new Date()): Promise { try { const db = database.active; - const subscription = await db.get('subscriptions').find(rid); + const subscription = await getSubscriptionByRoomId(rid); + if (!subscription) { + return; + } await db.write(async () => { await subscription.update((s: TSubscriptionModel) => { s.lastOpen = lastOpen; diff --git a/app/lib/navigation/appNavigation.ts b/app/lib/navigation/appNavigation.ts index cb1d6160529..6beca585211 100644 --- a/app/lib/navigation/appNavigation.ts +++ b/app/lib/navigation/appNavigation.ts @@ -9,6 +9,10 @@ function navigate(name: string, params?: any) { navigationRef.current?.navigate(name, params); } +function push(name: string, params?: any) { + navigationRef.current?.dispatch(StackActions.push(name, params)); +} + function back() { navigationRef.current?.dispatch(CommonActions.goBack()); } @@ -17,8 +21,32 @@ function replace(name: string, params: any) { navigationRef.current?.dispatch(StackActions.replace(name, params)); } -function popToTop() { - navigationRef.current?.dispatch(StackActions.popToTop()); +// Pops to the first occurrence of the given route name, usually RoomView +function popTo(name: string) { + navigationRef.current?.dispatch(StackActions.popTo(name)); +} + +// Removes RoomView from the stack and leaves only RoomsListView open +function popToTop(isMasterDetail: boolean) { + if (isMasterDetail) { + popTo('DrawerNavigator'); + dispatch( + CommonActions.reset({ + index: 0, + routes: [{ name: 'RoomView' }] + }) + ); + } else { + dispatch(StackActions.popToTop()); + } +} + +function popToRoom(isMasterDetail: boolean) { + if (isMasterDetail) { + popTo('DrawerNavigator'); + } else { + popTo('RoomView'); + } } function dispatch(params: any) { @@ -38,13 +66,26 @@ function resetTo(screen = 'RoomView') { }); } +function getCurrentRoute() { + return navigationRef.current?.getCurrentRoute(); +} + +function setParams(params: any) { + navigationRef.current?.setParams(params); +} + export default { navigationRef, routeNameRef, navigate, + push, back, replace, + popTo, popToTop, + popToRoom, dispatch, - resetTo + resetTo, + getCurrentRoute, + setParams }; diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js index acc26188122..019c39f5e78 100644 --- a/app/sagas/createChannel.js +++ b/app/sagas/createChannel.js @@ -82,7 +82,7 @@ const handleRequest = function* handleRequest({ data }) { const handleSuccess = function* handleSuccess({ data }) { const isMasterDetail = yield select(state => state.app.isMasterDetail); - goRoom({ item: data, isMasterDetail, popToRoot: true }); + goRoom({ item: data, isMasterDetail }); }; const handleFailure = function handleFailure({ err, isTeam }) { diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 43e404e1c86..7195d163228 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -75,9 +75,8 @@ const navigate = function* navigate({ params }) { const isMasterDetail = yield select(state => state.app.isMasterDetail); const jumpToMessageId = params.messageId; - yield waitForNavigation(); - yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId, popToRoot: true }); + yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId }); } } else { yield handleInviteLink({ params }); @@ -220,7 +219,7 @@ const handleNavigateCallRoom = function* handleNavigateCallRoom({ params }) { const room = yield subsCollection.find(params.rid); if (room) { const isMasterDetail = yield select(state => state.app.isMasterDetail); - yield navigateToRoom({ item: room, isMasterDetail, popToRoot: true }); + yield navigateToRoom({ item: room, isMasterDetail }); const uid = params.caller._id; const { rid, callId, event } = params; if (event === 'accept') { diff --git a/app/sagas/messages.js b/app/sagas/messages.js index 211158f64b0..65143965f4e 100644 --- a/app/sagas/messages.js +++ b/app/sagas/messages.js @@ -17,11 +17,11 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) { const isMasterDetail = yield select(state => state.app.isMasterDetail); if (subscriptions.length) { - goRoom({ item: subscriptions[0], isMasterDetail, popToRoot: true, messageId: message.id }); + goRoom({ item: subscriptions[0], isMasterDetail, messageId: message.id }); } else { const result = yield Services.createDirectMessage(username); if (result?.success) { - goRoom({ item: result?.room, isMasterDetail, popToRoot: true, messageId: message.id }); + goRoom({ item: result?.room, isMasterDetail, messageId: message.id }); } } } catch (e) { diff --git a/app/sagas/room.js b/app/sagas/room.js index 082d7e15e67..4c7731de268 100644 --- a/app/sagas/room.js +++ b/app/sagas/room.js @@ -67,11 +67,7 @@ const watchUserTyping = function* watchUserTyping({ rid, status }) { const handleRemovedRoom = function* handleRemovedRoom(roomType, actionType) { const isMasterDetail = yield select(state => state.app.isMasterDetail); - if (isMasterDetail) { - yield Navigation.navigate('DrawerNavigator'); - } else { - yield Navigation.navigate('RoomsListView'); - } + Navigation.popToTop(isMasterDetail); if (actionType === 'leave') { EventEmitter.emit(LISTENER, { diff --git a/app/views/CannedResponseDetail.tsx b/app/views/CannedResponseDetail.tsx index 53c09dcc22b..4d5c926d2e2 100644 --- a/app/views/CannedResponseDetail.tsx +++ b/app/views/CannedResponseDetail.tsx @@ -104,7 +104,7 @@ const CannedResponseDetail = (): JSX.Element => { const { room } = route.params; if (room.rid) { - goRoom({ item: room, isMasterDetail, popToRoot: true, usedCannedResponse: item.text }); + goRoom({ item: room, isMasterDetail, usedCannedResponse: item.text }); } }; diff --git a/app/views/CannedResponsesListView/index.tsx b/app/views/CannedResponsesListView/index.tsx index 5b19a143243..3636efe8be4 100644 --- a/app/views/CannedResponsesListView/index.tsx +++ b/app/views/CannedResponsesListView/index.tsx @@ -102,7 +102,7 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView const navigateToRoom = (item: ICannedResponse) => { if (room?.rid) { - goRoom({ item: room, isMasterDetail, popToRoot: true, usedCannedResponse: item.text }); + goRoom({ item: room, isMasterDetail, usedCannedResponse: item.text }); } }; diff --git a/app/views/CreateDiscussionView/utils/handleSubmitEvent.ts b/app/views/CreateDiscussionView/utils/handleSubmitEvent.ts index 51c7d31293b..b0f6702e138 100644 --- a/app/views/CreateDiscussionView/utils/handleSubmitEvent.ts +++ b/app/views/CreateDiscussionView/utils/handleSubmitEvent.ts @@ -25,7 +25,7 @@ const handleSubmitEvent = ({ loading, failure, isMasterDetail, error, result }: t, prid }; - goRoom({ item, isMasterDetail, popToRoot: true }); + goRoom({ item, isMasterDetail }); } } }; diff --git a/app/views/DirectoryView/index.tsx b/app/views/DirectoryView/index.tsx index d1fdbeafa8f..16741c37a86 100644 --- a/app/views/DirectoryView/index.tsx +++ b/app/views/DirectoryView/index.tsx @@ -173,7 +173,7 @@ class DirectoryView extends React.Component { const { isMasterDetail } = this.props; - goRoom({ item, isMasterDetail, popToRoot: true }); + goRoom({ item, isMasterDetail }); }; onPressItem = async (item: IServerRoom) => { diff --git a/app/views/DiscussionsView/index.tsx b/app/views/DiscussionsView/index.tsx index 4d9c1177f04..331af6a523b 100644 --- a/app/views/DiscussionsView/index.tsx +++ b/app/views/DiscussionsView/index.tsx @@ -20,6 +20,7 @@ import SearchHeader from '../../containers/SearchHeader'; import Item from './Item'; import { Services } from '../../lib/services'; import { useAppSelector } from '../../lib/hooks'; +import { goRoom } from '../../lib/methods/helpers/goRoom'; const API_FETCH_COUNT = 50; @@ -142,11 +143,14 @@ const DiscussionsView = () => { const onDiscussionPress = (item: TThreadModel) => { if (item.drid && item.t) { - navigation.push('RoomView', { - rid: item.drid, - prid: item.rid, - name: item.msg, - t + goRoom({ + item: { + rid: item.drid, + prid: item.rid, + name: item.msg, + t + }, + isMasterDetail }); } }; diff --git a/app/views/MessagesView/index.tsx b/app/views/MessagesView/index.tsx index b90ed687762..7559e987278 100644 --- a/app/views/MessagesView/index.tsx +++ b/app/views/MessagesView/index.tsx @@ -36,6 +36,7 @@ import { Services } from '../../lib/services'; import { TNavigation } from '../../stacks/stackType'; import AudioManager from '../../lib/methods/AudioManager'; import { Encryption } from '../../lib/encryption'; +import Navigation from '../../lib/navigation/appNavigation'; interface IMessagesViewProps { user: { @@ -137,7 +138,7 @@ class MessagesView extends React.Component { - const { navigation, isMasterDetail } = this.props; + const { isMasterDetail } = this.props; let params: IParams = { rid: this.rid, jumpToMessageId: item._id, @@ -145,20 +146,17 @@ class MessagesView extends React.Component, @@ -40,7 +41,7 @@ const RoomInfoView = (): React.ReactElement => { const { params: { rid, t, fromRid, member, room: roomParam, showCloseModal, itsMe } } = useRoute(); - const { addListener, setOptions, navigate, goBack } = useNavigation(); + const { addListener, setOptions, navigate } = useNavigation(); const [room, setRoom] = useState(roomParam || ({ rid, t } as ISubscription)); const [roomFromRid, setRoomFromRid] = useState(); @@ -80,7 +81,7 @@ const RoomInfoView = (): React.ReactElement => { // Prevents from flashing RoomInfoView on the header title before fetching actual room data useLayoutEffect(() => { setHeader(false); - }); + }, []); useEffect(() => { const listener = addListener('focus', () => (isLivechat ? loadVisitor() : null)); @@ -236,15 +237,10 @@ const RoomInfoView = (): React.ReactElement => { if (r?.rid) { if (r.rid === subscribedRoom) { - if (isMasterDetail) { - return navigate('DrawerNavigator'); - } - goBack(); - goBack(); + Navigation.popToRoom(isMasterDetail); return; } - // if it's on master detail layout, we close the modal and replace RoomView - goRoom({ item: params, isMasterDetail, popToRoot: true }); + goRoom({ item: params, isMasterDetail }); } }; diff --git a/app/views/RoomMembersView/helpers.ts b/app/views/RoomMembersView/helpers.ts index ed9dc1013b2..2271a5b4285 100644 --- a/app/views/RoomMembersView/helpers.ts +++ b/app/views/RoomMembersView/helpers.ts @@ -17,7 +17,7 @@ import { emitErrorCreateDirectMessage } from '../../lib/methods/helpers/emitErro export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL; const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => { - goRoom({ item, isMasterDetail, popToRoot: true }); + goRoom({ item, isMasterDetail }); }; export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles?: IGetRoomRoles[]): boolean => { diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx index 92a5ae30945..2746e8f95a2 100644 --- a/app/views/RoomView/index.tsx +++ b/app/views/RoomView/index.tsx @@ -984,8 +984,9 @@ class RoomView extends React.Component { handleRoomRemoved = ({ rid }: { rid: string }) => { const { room } = this.state; + const { isMasterDetail } = this.props; if (rid === this.rid) { - Navigation.navigate('RoomsListView'); + Navigation.popToTop(isMasterDetail); !this.isOmnichannel && showErrorAlert(I18n.t('You_were_removed_from_channel', { channel: getRoomTitle(room) }), I18n.t('Oops')); } diff --git a/app/views/SearchMessagesView/index.tsx b/app/views/SearchMessagesView/index.tsx index bce4cb91840..9a0146d4e8c 100644 --- a/app/views/SearchMessagesView/index.tsx +++ b/app/views/SearchMessagesView/index.tsx @@ -40,6 +40,7 @@ import { } from '../../definitions'; import { Services } from '../../lib/services'; import { TNavigation } from '../../stacks/stackType'; +import Navigation from '../../lib/navigation/appNavigation'; const QUERY_SIZE = 50; @@ -75,6 +76,7 @@ interface ISearchMessagesViewProps extends INavigationOption { }; theme: TSupportedThemes; useRealName: boolean; + isMasterDetail: boolean; } class SearchMessagesView extends React.Component { private offset: number; @@ -227,7 +229,7 @@ class SearchMessagesView extends React.Component { - const { navigation } = this.props; + const { isMasterDetail } = this.props; let params: { rid: string; jumpToMessageId: string; @@ -242,16 +244,17 @@ class SearchMessagesView extends React.Component ({ serverVersion: state.server.version, + isMasterDetail: state.app.isMasterDetail, baseUrl: state.server.server, user: getUserSelector(state), useRealName: state.settings.UI_Use_Real_Name, diff --git a/app/views/TeamChannelsView.tsx b/app/views/TeamChannelsView.tsx index 98859f9c807..87683ba64b8 100644 --- a/app/views/TeamChannelsView.tsx +++ b/app/views/TeamChannelsView.tsx @@ -348,7 +348,7 @@ class TeamChannelsView extends React.Component { const { subscription } = this.state; - const { navigation, isMasterDetail } = this.props; - if (isMasterDetail) { - navigation.pop(); - } - navigation.push('RoomView', { + const { isMasterDetail } = this.props; + Navigation.popToRoom(isMasterDetail); + Navigation.push('RoomView', { rid: item.subscription.id, tmid: item.id, name: makeThreadName(item), diff --git a/e2e/jest.config.js b/e2e/jest.config.js index d8dae0ec2fa..8a4c4d6bd7a 100644 --- a/e2e/jest.config.js +++ b/e2e/jest.config.js @@ -4,7 +4,7 @@ module.exports = { testSequencer: '/e2e/testSequencer.js', testMatch: ['/e2e/tests/**/*.spec.ts'], testTimeout: 120000, - maxWorkers: process.env.CI ? 1 : 1, // ci already uses parallelism + maxWorkers: process.env.CI ? 1 : 3, // ci already uses parallelism globalSetup: 'detox/runners/jest/globalSetup', globalTeardown: 'detox/runners/jest/globalTeardown', reporters: ['detox/runners/jest/reporter'], diff --git a/e2e/tests/onboarding/01-onboarding.spec.ts b/e2e/tests/onboarding/01-onboarding.spec.ts index 3d02a6917bc..13ad8086e82 100644 --- a/e2e/tests/onboarding/01-onboarding.spec.ts +++ b/e2e/tests/onboarding/01-onboarding.spec.ts @@ -4,11 +4,10 @@ import { TTextMatcher, platformTypes } from '../../helpers/app'; import data from '../../data'; describe('Onboarding', () => { - let alertButtonType: string; let textMatcher: TTextMatcher; beforeAll(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); - ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); + ({ textMatcher } = platformTypes[device.getPlatform()]); await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(20000); @@ -24,10 +23,9 @@ describe('Onboarding', () => { it('should enter an invalid server and get error', async () => { await element(by.id('new-server-view-input')).replaceText('invalidtest'); await element(by.id('new-server-view-input')).tapReturnKey(); - await waitFor(element(by[textMatcher]('Invalid workspace URL'))) + await waitFor(element(by[textMatcher]('Invalid URL'))) .toExist() .withTimeout(10000); - await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should enter a valid server without login services and navigate to login', async () => { diff --git a/e2e/tests/room/05-threads.spec.ts b/e2e/tests/room/05-threads.spec.ts index 23997fad91c..5ff10f4d640 100644 --- a/e2e/tests/room/05-threads.spec.ts +++ b/e2e/tests/room/05-threads.spec.ts @@ -10,7 +10,8 @@ import { tapAndWaitFor, navigateToRoom, mockMessage, - tryTapping + tryTapping, + checkRoomTitle } from '../../helpers/app'; import { createRandomRoom, createRandomUser } from '../../helpers/data_setup'; @@ -178,14 +179,10 @@ describe('Threads', () => { .withTimeout(5000); await expect(element(by.id(`room-view-title-${thread}`))).toExist(); await tapBack(); - await waitFor(element(by.id('thread-messages-view'))) - .toExist() - .withTimeout(5000); - await expect(element(by.id('thread-messages-view'))).toExist(); - await tapBack(); }); it('should draft thread message', async () => { + await checkRoomTitle(room); await element(by.id(`message-thread-button-${thread}`)).tap(); await waitFor(element(by.id(`room-view-title-${thread}`))) .toExist() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy