From 0a3f5e59140971504e3277927af470b05ab453ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Wed, 28 Jun 2023 12:49:58 +0200 Subject: [PATCH] Fix problems related to closing react but keeping other state (#7405) --- app/actions/remote/notifications.ts | 7 ++++++ app/init/app.ts | 36 +++++++++++++++++++++++------ app/init/launch.ts | 4 +++- app/screens/navigation.ts | 6 +++++ app/store/ephemeral_store.ts | 10 ++++++++ app/store/navigation_store.ts | 7 ++++++ index.ts | 3 +-- 7 files changed, 63 insertions(+), 10 deletions(-) diff --git a/app/actions/remote/notifications.ts b/app/actions/remote/notifications.ts index f90bd1a87b..0c91656b45 100644 --- a/app/actions/remote/notifications.ts +++ b/app/actions/remote/notifications.ts @@ -180,6 +180,13 @@ export const backgroundNotification = async (serverUrl: string, notification: No }; export const openNotification = async (serverUrl: string, notification: NotificationWithData) => { + // Wait for initial launch to kick in if needed + await new Promise((r) => setTimeout(r, 500)); + + if (EphemeralStore.getProcessingNotification() === notification.identifier) { + return {}; + } + EphemeralStore.setNotificationTapped(true); const channelId = notification.payload!.channel_id!; diff --git a/app/init/app.ts b/app/init/app.ts index 0b8e8a7fba..e04fc54c64 100644 --- a/app/init/app.ts +++ b/app/init/app.ts @@ -12,8 +12,17 @@ import SessionManager from '@managers/session_manager'; import WebsocketManager from '@managers/websocket_manager'; import {registerScreens} from '@screens/index'; import {registerNavigationListeners} from '@screens/navigation'; +import EphemeralStore from '@store/ephemeral_store'; +import NavigationStore from '@store/navigation_store'; + +// Controls whether the main initialization (database, etc...) is done, either on app launch +// or on the Share Extension, for example. +let baseAppInitialized = false; + +// Controls whether the app initialization (websockets, screen listeners, etc...) is done, mainly +// on app launch +let mainAppInitialized = false; -let alreadyInitialized = false; let serverCredentials: ServerCredential[]; // Fallback Polyfill for Promise.allSettle @@ -31,8 +40,8 @@ Promise.allSettled = Promise.allSettled || ((promises: Array>) => )); export async function initialize() { - if (!alreadyInitialized) { - alreadyInitialized = true; + if (!baseAppInitialized) { + baseAppInitialized = true; serverCredentials = await getAllServerCredentials(); const serverUrls = serverCredentials.map((credential) => credential.serverUrl); @@ -46,12 +55,25 @@ export async function initialize() { } export async function start() { + if (baseAppInitialized) { + // Clean relevant information on ephemeral stores + NavigationStore.reset(); + EphemeralStore.setCurrentThreadId(''); + EphemeralStore.setProcessingNotification(''); + } + await initialize(); - PushNotifications.init(serverCredentials.length > 0); + if (!mainAppInitialized) { + mainAppInitialized = true; + + PushNotifications.init(serverCredentials.length > 0); + + registerNavigationListeners(); + registerScreens(); + + await WebsocketManager.init(serverCredentials); + } - registerNavigationListeners(); - registerScreens(); - await WebsocketManager.init(serverCredentials); initialLaunch(); } diff --git a/app/init/launch.ts b/app/init/launch.ts index f5f6ebd4b0..081c6aeeae 100644 --- a/app/init/launch.ts +++ b/app/init/launch.ts @@ -42,7 +42,9 @@ export const initialLaunch = async () => { tapped = delivered.find((d) => (d as unknown as NotificationData).ack_id === notification?.payload.ack_id) == null; } if (initialNotificationTypes.includes(notification?.payload?.type) && tapped) { - return launchAppFromNotification(convertToNotificationData(notification!), true); + const notificationData = convertToNotificationData(notification!); + EphemeralStore.setProcessingNotification(notificationData.identifier); + return launchAppFromNotification(notificationData, true); } const coldStart = notification ? (tapped || AppState.currentState === 'active') : true; diff --git a/app/screens/navigation.ts b/app/screens/navigation.ts index cd85c33703..b1f02203ab 100644 --- a/app/screens/navigation.ts +++ b/app/screens/navigation.ts @@ -14,6 +14,7 @@ import {NOT_READY} from '@constants/screens'; import {getDefaultThemeByAppearance} from '@context/theme'; import EphemeralStore from '@store/ephemeral_store'; import NavigationStore from '@store/navigation_store'; +import {logError} from '@utils/log'; import {appearanceControlledScreens, mergeNavigationOptions} from '@utils/navigation'; import {changeOpacity, setNavigatorStyles} from '@utils/theme'; @@ -449,6 +450,11 @@ export function goToScreen(name: AvailableScreens, title: string, passProps = {} const theme = getThemeFromState(); const isDark = tinyColor(theme.sidebarBg).isDark(); const componentId = NavigationStore.getVisibleScreen(); + if (!componentId) { + logError('Trying to go to screen without any screen on the navigation store'); + return ''; + } + const defaultOptions: Options = { layout: { componentBackgroundColor: theme.centerChannelBg, diff --git a/app/store/ephemeral_store.ts b/app/store/ephemeral_store.ts index bc2674f0bf..6015f0184d 100644 --- a/app/store/ephemeral_store.ts +++ b/app/store/ephemeral_store.ts @@ -38,6 +38,16 @@ class EphemeralStore { private notificationTapped = false; private enablingCRT = false; + // There are some corner cases where the react context is not loaded (and therefore + // launch will be called) but the notification callbacks are registered. This is used + // so the notification is processed only once (preferably on launch). + private processingNotification = ''; + + setProcessingNotification = (v: string) => { + this.processingNotification = v; + }; + getProcessingNotification = () => this.processingNotification; + addLoadingMessagesForChannel = (serverUrl: string, channelId: string) => { if (!this.loadingMessagesForChannel[serverUrl]) { this.loadingMessagesForChannel[serverUrl] = new Set(); diff --git a/app/store/navigation_store.ts b/app/store/navigation_store.ts index 2f19fce4e3..0c0f815cad 100644 --- a/app/store/navigation_store.ts +++ b/app/store/navigation_store.ts @@ -9,6 +9,13 @@ class NavigationStore { private visibleTab = 'Home'; private tosOpen = false; + reset = () => { + this.screensInStack = []; + this.modalsInStack = []; + this.visibleTab = 'Home'; + this.tosOpen = false; + }; + addModalToStack = (modalId: AvailableScreens) => { this.removeModalFromStack(modalId); this.addScreenToStack(modalId); diff --git a/index.ts b/index.ts index 430b7434ec..457eb096ee 100644 --- a/index.ts +++ b/index.ts @@ -8,7 +8,7 @@ import {RUNNING_E2E} from 'react-native-dotenv'; import 'react-native-gesture-handler'; import {Navigation} from 'react-native-navigation'; -import {initialize, start} from './app/init/app'; +import {start} from './app/init/app'; import setFontFamily from './app/utils/font_family'; import {logInfo} from './app/utils/log'; @@ -59,6 +59,5 @@ if (Platform.OS === 'android') { } Navigation.events().registerAppLaunchedListener(async () => { - await initialize(); start(); });