diff --git a/app/actions/remote/entry/app.ts b/app/actions/remote/entry/app.ts index 65ac211e15..a04bb97c97 100644 --- a/app/actions/remote/entry/app.ts +++ b/app/actions/remote/entry/app.ts @@ -35,7 +35,7 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false) let result; if (config?.FeatureFlagGraphQL === 'true') { const {currentTeamId, currentChannelId} = await getCommonSystemValues(database); - result = await graphQLCommon(serverUrl, true, currentTeamId, currentChannelId, isUpgrade); + result = await graphQLCommon(serverUrl, true, currentTeamId, currentChannelId, '', isUpgrade); if (result.error) { logDebug('Error using GraphQL, trying REST', result.error); result = restAppEntry(serverUrl, since, isUpgrade); diff --git a/app/actions/remote/entry/common.ts b/app/actions/remote/entry/common.ts index ee07b6f1a7..483b9a22e4 100644 --- a/app/actions/remote/entry/common.ts +++ b/app/actions/remote/entry/common.ts @@ -3,16 +3,16 @@ import {Model} from '@nozbe/watermelondb'; -import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, MyChannelsRequest} from '@actions/remote/channel'; +import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, MyChannelsRequest, switchToChannelById} from '@actions/remote/channel'; import {fetchPostsForUnreadChannels} from '@actions/remote/post'; import {MyPreferencesRequest, fetchMyPreferences} from '@actions/remote/preference'; import {fetchRoles} from '@actions/remote/role'; import {fetchConfigAndLicense} from '@actions/remote/systems'; import {fetchAllTeams, fetchMyTeams, fetchTeamsChannelsAndUnreadPosts, MyTeamsRequest} from '@actions/remote/team'; -import {fetchNewThreads} from '@actions/remote/thread'; +import {fetchAndSwitchToThread, fetchNewThreads} from '@actions/remote/thread'; import {fetchMe, MyUserRequest, updateAllUsersSince} from '@actions/remote/user'; import {gqlAllChannels} from '@client/graphQL/entry'; -import {Preferences} from '@constants'; +import {Preferences, Screens} from '@constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; import DatabaseManager from '@database/manager'; @@ -22,14 +22,18 @@ import {DEFAULT_LOCALE} from '@i18n'; import NetworkManager from '@managers/network_manager'; import {getDeviceToken} from '@queries/app/global'; import {queryAllServers} from '@queries/app/servers'; -import {prepareMyChannelsForTeam, queryAllChannelsForTeam, queryChannelsById} from '@queries/servers/channel'; +import {getMyChannel, prepareMyChannelsForTeam, queryAllChannelsForTeam, queryChannelsById} from '@queries/servers/channel'; import {prepareModels, truncateCrtRelatedTables} from '@queries/servers/entry'; import {getHasCRTChanged} from '@queries/servers/preference'; -import {getConfig, getCurrentUserId, getPushVerificationStatus, getWebSocketLastDisconnected} from '@queries/servers/system'; -import {deleteMyTeams, getAvailableTeamIds, getNthLastChannelFromTeam, queryMyTeams, queryMyTeamsByIds, queryTeamsById} from '@queries/servers/team'; +import {getConfig, getCurrentUserId, getPushVerificationStatus, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; +import {deleteMyTeams, getAvailableTeamIds, getMyTeamById, getNthLastChannelFromTeam, queryMyTeams, queryMyTeamsByIds, queryTeamsById} from '@queries/servers/team'; +import {getIsCRTEnabled} from '@queries/servers/thread'; +import NavigationStore from '@store/navigation_store'; import {isDMorGM} from '@utils/channel'; import {getMemberChannelsFromGQLQuery, gqlToClientChannelMembership} from '@utils/graphql'; +import {isTablet} from '@utils/helpers'; import {logDebug} from '@utils/log'; +import {emitNotificationError} from '@utils/notification'; import {processIsCRTEnabled} from '@utils/thread'; import {fetchGroupsForMember} from '../groups'; @@ -482,3 +486,61 @@ export async function verifyPushProxy(serverUrl: string) { // Do nothing } } + +export async function handleNotificationNavigation(serverUrl: string, selectedChannelId: string, selectedTeamId: string, notificationChannelId: string, notificationTeamId: string, rootId = '') { + try { + const {operator, database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const myChannel = await getMyChannel(database, selectedChannelId); + const myTeam = await getMyTeamById(database, selectedTeamId); + const isCRTEnabled = await getIsCRTEnabled(database); + const isThreadNotification = isCRTEnabled && Boolean(rootId); + + let switchedToScreen = false; + let switchedToChannel = false; + if (myChannel && myTeam) { + if (isThreadNotification) { + await fetchAndSwitchToThread(serverUrl, rootId, true); + } else { + switchedToChannel = true; + await switchToChannelById(serverUrl, selectedChannelId, selectedTeamId); + } + switchedToScreen = true; + } + + if (!switchedToScreen) { + const isTabletDevice = await isTablet(); + if (isTabletDevice || (notificationChannelId === selectedChannelId)) { + // Make switch again to get the missing data and make sure the team is the correct one + switchedToScreen = true; + if (isThreadNotification) { + await fetchAndSwitchToThread(serverUrl, rootId, true); + } else { + switchedToChannel = true; + await switchToChannelById(serverUrl, notificationChannelId, notificationTeamId); + } + } else if (notificationTeamId !== selectedTeamId || notificationChannelId !== selectedChannelId) { + // If in the end the selected team or channel is different than the one from the notification + // we switch again + await setCurrentTeamAndChannelId(operator, selectedTeamId, selectedChannelId); + } + } + + if (notificationTeamId !== selectedTeamId) { + emitNotificationError('Team'); + } else if (notificationChannelId !== selectedChannelId) { + emitNotificationError('Channel'); + } + + // Waiting for the screen to display fixes a race condition when fetching and storing data + if (switchedToChannel) { + await NavigationStore.waitUntilScreenHasLoaded(Screens.CHANNEL); + } else if (switchedToScreen && isThreadNotification) { + await NavigationStore.waitUntilScreenHasLoaded(Screens.THREAD); + } + + return {}; + } catch (error) { + return {error}; + } +} diff --git a/app/actions/remote/entry/gql_common.ts b/app/actions/remote/entry/gql_common.ts index e83a341e74..797b3df06d 100644 --- a/app/actions/remote/entry/gql_common.ts +++ b/app/actions/remote/entry/gql_common.ts @@ -24,7 +24,7 @@ import {filterAndTransformRoles, getMemberChannelsFromGQLQuery, getMemberTeamsFr import {isTablet} from '@utils/helpers'; import {processIsCRTEnabled} from '@utils/thread'; -import {teamsToRemove, FETCH_UNREADS_TIMEOUT} from './common'; +import {teamsToRemove, FETCH_UNREADS_TIMEOUT, handleNotificationNavigation} from './common'; import type ClientError from '@client/rest/error'; import type ChannelModel from '@typings/database/models/servers/channel'; @@ -158,7 +158,7 @@ const getChannelData = async (serverUrl: string, initialTeamId: string, userId: return {chData, roles}; }; -export const graphQLCommon = async (serverUrl: string, syncDatabase: boolean, currentTeamId: string, currentChannelId: string, isUpgrade = false) => { +export const graphQLCommon = async (serverUrl: string, syncDatabase: boolean, currentTeamId: string, currentChannelId: string, rootId?: string, isUpgrade = false, isNotificationEntry = false) => { const dt = Date.now(); const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; @@ -303,6 +303,10 @@ export const graphQLCommon = async (serverUrl: string, syncDatabase: boolean, cu await operator.batchRecords(models.flat()); } + if (isNotificationEntry) { + await handleNotificationNavigation(serverUrl, initialChannelId, initialTeamId, currentChannelId, currentTeamId, rootId); + } + const isCRTEnabled = Boolean(prefData.preferences && processIsCRTEnabled(prefData.preferences, config)); deferredAppEntryGraphQLActions(serverUrl, 0, meData, teamData, chData, isTabletDevice, initialTeamId, initialChannelId, isCRTEnabled, syncDatabase); diff --git a/app/actions/remote/entry/notification.ts b/app/actions/remote/entry/notification.ts index 44a4d2bbc0..ef7535943e 100644 --- a/app/actions/remote/entry/notification.ts +++ b/app/actions/remote/entry/notification.ts @@ -1,25 +1,18 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {switchToChannelById} from '@actions/remote/channel'; -import {fetchAndSwitchToThread} from '@actions/remote/thread'; import {Preferences, Screens} from '@constants'; import {getDefaultThemeByAppearance} from '@context/theme'; import DatabaseManager from '@database/manager'; -import {getMyChannel} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {getCommonSystemValues, getConfig, getCurrentTeamId, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; -import {getMyTeamById} from '@queries/servers/team'; -import {getIsCRTEnabled} from '@queries/servers/thread'; +import {getCommonSystemValues, getConfig, getCurrentTeamId, getWebSocketLastDisconnected} from '@queries/servers/system'; import {getCurrentUser} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; import NavigationStore from '@store/navigation_store'; -import {isTablet} from '@utils/helpers'; import {logDebug} from '@utils/log'; -import {emitNotificationError} from '@utils/notification'; import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme'; -import {deferredAppEntryActions, entry, syncOtherServers} from './common'; +import {deferredAppEntryActions, entry, handleNotificationNavigation, syncOtherServers} from './common'; import {graphQLCommon} from './gql_common'; export async function pushNotificationEntry(serverUrl: string, notification: NotificationWithData) { @@ -64,7 +57,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not const config = await getConfig(database); let result; if (config?.FeatureFlagGraphQL === 'true') { - result = await graphQLCommon(serverUrl, true, teamId, channelId); + result = await graphQLCommon(serverUrl, true, teamId, channelId, rootId, false, true); if (result.error) { logDebug('Error using GraphQL, trying REST', result.error); result = restNotificationEntry(serverUrl, teamId, channelId, rootId, isDirectChannel); @@ -106,61 +99,15 @@ const restNotificationEntry = async (serverUrl: string, teamId: string, channelI } } - const myChannel = await getMyChannel(database, channelId); - const myTeam = await getMyTeamById(database, teamId); - const isCRTEnabled = await getIsCRTEnabled(database); - const isThreadNotification = isCRTEnabled && Boolean(rootId); - await operator.batchRecords(models); - let switchedToScreen = false; - let switchedToChannel = false; - if (myChannel && myTeam) { - if (isThreadNotification) { - await fetchAndSwitchToThread(serverUrl, rootId, true); - } else { - switchedToChannel = true; - await switchToChannelById(serverUrl, channelId, teamId); - } - switchedToScreen = true; - } - - if (!switchedToScreen) { - const isTabletDevice = await isTablet(); - if (isTabletDevice || (selectedChannelId === channelId)) { - // Make switch again to get the missing data and make sure the team is the correct one - switchedToScreen = true; - if (isThreadNotification) { - await fetchAndSwitchToThread(serverUrl, rootId, true); - } else { - switchedToChannel = true; - await switchToChannelById(serverUrl, selectedChannelId, selectedTeamId); - } - } else if (selectedTeamId !== teamId || selectedChannelId !== channelId) { - // If in the end the selected team or channel is different than the one from the notification - // we switch again - await setCurrentTeamAndChannelId(operator, selectedTeamId, selectedChannelId); - } - } - - if (selectedTeamId !== teamId) { - emitNotificationError('Team'); - } else if (selectedChannelId !== channelId) { - emitNotificationError('Channel'); - } + await handleNotificationNavigation(serverUrl, selectedChannelId, selectedTeamId, channelId, teamId, rootId); const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(operator.database))!; const {config, license} = await getCommonSystemValues(operator.database); const lastDisconnectedAt = await getWebSocketLastDisconnected(database); - // Waiting for the screen to display fixes a race condition when fetching and storing data - if (switchedToChannel) { - await NavigationStore.waitUntilScreenHasLoaded(Screens.CHANNEL); - } else if (switchedToScreen && isThreadNotification) { - await NavigationStore.waitUntilScreenHasLoaded(Screens.THREAD); - } - await deferredAppEntryActions(serverUrl, lastDisconnectedAt, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, selectedTeamId, selectedChannelId); return {userId: currentUserId};