From 006cfd79de95e441deca9508ac6979ff72a5dd5b Mon Sep 17 00:00:00 2001 From: williamlines Date: Tue, 15 Aug 2023 11:15:00 +0100 Subject: [PATCH 1/5] fix: allows OTP to be approved from a cold start --- src/screens/HomeScreen.tsx | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 8ac7ac11..3c908265 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import * as Notifications from 'expo-notifications' import { NotificationResponse } from 'expo-notifications' import { ScrollView, StyleSheet, View } from 'react-native' @@ -47,6 +47,8 @@ export const HomeScreen: React.FC = ({ navigation }) => { const isFocused = useIsFocused() const notificationListener = useRef() const responseListener = useRef() + const lastNotificationResponse = Notifications.useLastNotificationResponse() + const [initialLoadingComplete, setInitialLoadingComplete] = useState(false) const api = useMemo(() => apiFactory({ idToken: user.idToken }), [user]) @@ -111,6 +113,28 @@ export const HomeScreen: React.FC = ({ navigation }) => { ) useEffect(() => { + // runs callbacks when notification is pressed from a cold start + if ( + !initialLoadingComplete && + !secretsLoading && + lastNotificationResponse + ) { + const request = lastNotificationResponse.notification + .request as Notifications.NotificationRequest & { + content: { data: NotificationData } + } + const opticNotification: OpticNotification = { + date: lastNotificationResponse.notification.date, + request: request, + } + + onNotificationResponse(lastNotificationResponse) + onNotification(opticNotification) + + // prevents the cold start callbacks from being called after a notification is triggered + setInitialLoadingComplete(true) + } + notificationListener.current = Notifications.addNotificationReceivedListener(onNotification) @@ -123,7 +147,13 @@ export const HomeScreen: React.FC = ({ navigation }) => { Notifications.removeNotificationSubscription(responseListener.current) Notifications.removeNotificationSubscription(notificationListener.current) } - }, [onNotification, onNotificationResponse]) + }, [ + initialLoadingComplete, + lastNotificationResponse, + onNotification, + onNotificationResponse, + secretsLoading, + ]) if (secretsLoading) { return null From 6caca29ad39b6a89696022eb062ac787a21ef216 Mon Sep 17 00:00:00 2001 From: williamlines Date: Tue, 15 Aug 2023 11:32:51 +0100 Subject: [PATCH 2/5] chore: updated tests --- src/screens/HomeScreen.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/screens/HomeScreen.test.tsx b/src/screens/HomeScreen.test.tsx index c6371a88..63082c8c 100644 --- a/src/screens/HomeScreen.test.tsx +++ b/src/screens/HomeScreen.test.tsx @@ -17,6 +17,7 @@ jest.mock('expo-notifications', () => ({ addNotificationReceivedListener: jest.fn(), addNotificationResponseReceivedListener: jest.fn(), removeNotificationSubscription: jest.fn(), + useLastNotificationResponse: jest.fn(), })) jest.mock('../lib/api') From 2e1e8ddc2657d3307241a72838e789d30a1c2dd1 Mon Sep 17 00:00:00 2001 From: williamlines Date: Wed, 16 Aug 2023 10:20:26 +0100 Subject: [PATCH 3/5] chore: moved solution to independent useEffect --- src/screens/HomeScreen.tsx | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 3c908265..21581eb7 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -112,6 +112,21 @@ export const HomeScreen: React.FC = ({ navigation }) => { [navigation, secrets] ) + useEffect(() => { + notificationListener.current = + Notifications.addNotificationReceivedListener(onNotification) + + responseListener.current = + Notifications.addNotificationResponseReceivedListener( + onNotificationResponse + ) + + return () => { + Notifications.removeNotificationSubscription(responseListener.current) + Notifications.removeNotificationSubscription(notificationListener.current) + } + }, [onNotification, onNotificationResponse]) + useEffect(() => { // runs callbacks when notification is pressed from a cold start if ( @@ -131,22 +146,9 @@ export const HomeScreen: React.FC = ({ navigation }) => { onNotificationResponse(lastNotificationResponse) onNotification(opticNotification) - // prevents the cold start callbacks from being called after a notification is triggered + // prevents the cold start callbacks from being called again after this conditional has been triggered setInitialLoadingComplete(true) } - - notificationListener.current = - Notifications.addNotificationReceivedListener(onNotification) - - responseListener.current = - Notifications.addNotificationResponseReceivedListener( - onNotificationResponse - ) - - return () => { - Notifications.removeNotificationSubscription(responseListener.current) - Notifications.removeNotificationSubscription(notificationListener.current) - } }, [ initialLoadingComplete, lastNotificationResponse, From 519d0d67de4d0b9a131685cc1d39419f3e0efd1e Mon Sep 17 00:00:00 2001 From: williamlines Date: Wed, 16 Aug 2023 11:46:24 +0100 Subject: [PATCH 4/5] chore: moved initialLoadingComplete to react context --- src/App.tsx | 11 +++++++---- src/context/InitalLoadingContext.tsx | 28 ++++++++++++++++++++++++++++ src/screens/HomeScreen.tsx | 9 ++++++--- 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 src/context/InitalLoadingContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 850844c5..3b0f5ee4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import theme from './lib/theme' import Main from './Main' import { PrefsProvider } from './context/PrefsContext' import { PendingNotificationsProvider } from './context/PendingNotificationsContext' +import { InitialLoadingProvider } from './context/InitalLoadingContext' export default function App() { return ( @@ -18,10 +19,12 @@ export default function App() { - -
- - + + +
+ + + diff --git a/src/context/InitalLoadingContext.tsx b/src/context/InitalLoadingContext.tsx new file mode 100644 index 00000000..49ccb3db --- /dev/null +++ b/src/context/InitalLoadingContext.tsx @@ -0,0 +1,28 @@ +import React, { createContext, useContext, useState } from 'react' + +const initialContext = { + initialLoadingComplete: false, + markInitialLoadingComplete: () => { + // set in context provider construction + }, +} + +const InitialLoadingContext = createContext(initialContext) + +export const useInitialLoading = () => useContext(InitialLoadingContext) + +export const InitialLoadingProvider = ({ children }) => { + const [initialLoadingComplete, setInitialLoadingComplete] = useState(false) + + const markInitialLoadingComplete = () => { + setInitialLoadingComplete(true) + } + + return ( + + {children} + + ) +} diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 21581eb7..a50140ed 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' import * as Notifications from 'expo-notifications' import { NotificationResponse } from 'expo-notifications' import { ScrollView, StyleSheet, View } from 'react-native' @@ -10,6 +10,7 @@ import Toast from 'react-native-root-toast' import { usePendingNotifications } from '../context/PendingNotificationsContext' import { useSecrets } from '../context/SecretsContext' import { useAuth } from '../context/AuthContext' +import { useInitialLoading } from '../context/InitalLoadingContext' import apiFactory from '../lib/api' import { NoSecrets } from '../components/NoSecrets' import { Actions } from '../components/Actions' @@ -48,7 +49,8 @@ export const HomeScreen: React.FC = ({ navigation }) => { const notificationListener = useRef() const responseListener = useRef() const lastNotificationResponse = Notifications.useLastNotificationResponse() - const [initialLoadingComplete, setInitialLoadingComplete] = useState(false) + const { initialLoadingComplete, markInitialLoadingComplete } = + useInitialLoading() const api = useMemo(() => apiFactory({ idToken: user.idToken }), [user]) @@ -147,11 +149,12 @@ export const HomeScreen: React.FC = ({ navigation }) => { onNotification(opticNotification) // prevents the cold start callbacks from being called again after this conditional has been triggered - setInitialLoadingComplete(true) + markInitialLoadingComplete() } }, [ initialLoadingComplete, lastNotificationResponse, + markInitialLoadingComplete, onNotification, onNotificationResponse, secretsLoading, From 9a2f85643efefb78ec0871ac49ec929d826e8f24 Mon Sep 17 00:00:00 2001 From: williamlines Date: Wed, 16 Aug 2023 12:03:09 +0100 Subject: [PATCH 5/5] chore: memoise return value for initialLoadingComplete context --- src/context/InitalLoadingContext.tsx | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/context/InitalLoadingContext.tsx b/src/context/InitalLoadingContext.tsx index 49ccb3db..2dcfb438 100644 --- a/src/context/InitalLoadingContext.tsx +++ b/src/context/InitalLoadingContext.tsx @@ -1,4 +1,10 @@ -import React, { createContext, useContext, useState } from 'react' +import React, { + createContext, + useContext, + useState, + useMemo, + useCallback, +} from 'react' const initialContext = { initialLoadingComplete: false, @@ -14,14 +20,20 @@ export const useInitialLoading = () => useContext(InitialLoadingContext) export const InitialLoadingProvider = ({ children }) => { const [initialLoadingComplete, setInitialLoadingComplete] = useState(false) - const markInitialLoadingComplete = () => { + const markInitialLoadingComplete = useCallback(() => { setInitialLoadingComplete(true) - } + }, [setInitialLoadingComplete]) + + const value = useMemo( + () => ({ + initialLoadingComplete, + markInitialLoadingComplete, + }), + [initialLoadingComplete, markInitialLoadingComplete] + ) return ( - + {children} )