Skip to content

Commit

Permalink
Merge branch 'main' into feat/google-sign-in-logic
Browse files Browse the repository at this point in the history
  • Loading branch information
micbaumr authored Apr 8, 2024
2 parents 5f6cf2a + 46af2f2 commit 06cd16e
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 120 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ scripts/doppler_variables.sh
env.json

.vercel
.env*.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { PushNotificationsHelpersScreen } from '@baca/screens'

export default PushNotificationsHelpersScreen
15 changes: 15 additions & 0 deletions patches/expo-router+3.4.8.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
diff --git a/node_modules/expo-router/build/global-state/routing.js b/node_modules/expo-router/build/global-state/routing.js
index 2fba9d1..9cce0c9 100644
--- a/node_modules/expo-router/build/global-state/routing.js
+++ b/node_modules/expo-router/build/global-state/routing.js
@@ -173,6 +173,10 @@ function getNavigateAction(state, parentState, type = 'NAVIGATE') {
else if (type === 'REPLACE' && parentState.type === 'tab') {
type = 'JUMP_TO';
}
+
+ // https://github.com/expo/expo/issues/26211
+ params.initial = false
+
return {
type,
target: parentState.key,
29 changes: 29 additions & 0 deletions src/components/HelpersScreenComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Box, Spacer, Text } from '@baca/design-system'

export const HelperSection = ({
header = '',
children,
}: {
header: string
children: React.ReactNode
}) => {
return (
<Box p={4} borderRadius={16} bg="bg.active" mb={4} gap={2}>
<Text.XlBold>{header}</Text.XlBold>
<Spacer y={2} />
{children}
</Box>
)
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const HelperRenderJson = ({ children }: { children: any }) => {
if (typeof children === 'undefined') {
return null
}
return (
<Box p={2} bg="bg.quaternary" borderRadius={8}>
<Text.SmRegular>{JSON.stringify(children, null, 4)}</Text.SmRegular>
</Box>
)
}
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './wrappers'
export * from './AppLoading'
export * from './CompanyLogo'
export * from './FeaturedIcon'
export * from './HelpersScreenComponents'
export * from './KeyboardAwareScrollView'
export * from './LandingHeader'
export * from './LanguagePicker'
Expand Down
11 changes: 7 additions & 4 deletions src/contexts/NotificationContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { PermissionStatus } from 'expo-modules-core'
import * as Notifications from 'expo-notifications'
import { Dispatch, SetStateAction } from 'react'

export type ReceivedNotification =
| (Notifications.Notification & { context: { [key: string]: string } })
| null
| undefined

export type NotificationContextType = {
permissionStatus?: PermissionStatus
setPermissionStatus: Dispatch<SetStateAction<PermissionStatus | undefined>>
notification?: Notifications.Notification
setNotification: Dispatch<SetStateAction<Notifications.Notification | undefined>>
inAppNotification?: Notifications.Notification
setInAppNotification: Dispatch<SetStateAction<Notifications.Notification | undefined>>
notification: ReceivedNotification
setNotification: Dispatch<SetStateAction<ReceivedNotification>>
}

export const [useNotificationContext, NotificationContextProvider] =
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/usePasswordValidation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const usePasswordValidation = () => {
!showValidationState && setShowValidationState(true)
setIsPasswordError(!!min8Chars || !!min1SpecialChar)
setPasswordErrors([min8Chars, min1SpecialChar])
return !!min8Chars || !!min1SpecialChar ? 'Error' : false
return !!min8Chars || !!min1SpecialChar ? 'Error' : undefined
},
[showValidationState]
)
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@
"do_not_have_an_account": "Don't have an account?",
"forgot_password": "Forgot password",
"sign_in_by_google": "Log in with Google",
"sign_in": "Sign in",
"sign_in": "Log in",
"sign_up": "Sign up",
"welcome_back_enter_details": "Please enter your details",
"welcome_back": "Welcome back"
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/translations/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
},
"sign_up_screen": {
"already_have_an_account": "Masz już konto?",
"get_started": "Rozpoczynamy",
"get_started": "Rozpocznij",
"log_in": "Zaloguj się",
"sign_up": "Zarejestruj się",
"start_free_trail": "Rozpocznij darmowy 30 dniowy okres próbny."
Expand Down
91 changes: 54 additions & 37 deletions src/providers/NotificationProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { ASYNC_STORAGE_KEYS } from '@baca/constants'
import { NotificationContextProvider, NotificationContextType } from '@baca/contexts'
import { useState, useMemo, useEffect, useAppStateActive } from '@baca/hooks'
import {
assignPushToken,
disableAndroidBackgroundNotificationListener,
getNotificationFromStack,
getNotificationStackLength,
} from '@baca/services'
NotificationContextProvider,
NotificationContextType,
ReceivedNotification,
} from '@baca/contexts'
import { useState, useMemo, useEffect, useAppStateActive } from '@baca/hooks'
import { assignPushToken } from '@baca/services'
import { store } from '@baca/store'
import { isSignedInAtom } from '@baca/store/auth'
import AsyncStorage from '@react-native-async-storage/async-storage'
import * as Notifications from 'expo-notifications'
import { router } from 'expo-router'
import { useRootNavigationState, router } from 'expo-router'
import { PropsWithChildren, FC, useCallback } from 'react'
import { Alert, AlertButton } from 'react-native'

Expand All @@ -28,17 +27,37 @@ const deeplinkWhenNotificationReceived = async (
// Alternatively we can prevent navigating to this routes when user is not logged in

if (deeplinkPath) {
router.push(deeplinkPath)
router.navigate(deeplinkPath)
}
}

export const NotificationProvider: FC<PropsWithChildren> = ({ children }) => {
// -------------------------------------------------------------
// ----------------------- HOOKS -------------------------------
// -------------------------------------------------------------
const [permissionStatus, setPermissionStatus] =
useState<NotificationContextType['permissionStatus']>()
const [notification, setNotification] = useState<NotificationContextType['notification']>()
const [inAppNotification, setInAppNotification] =
useState<NotificationContextType['inAppNotification']>()
const [notification, setNotification] = useState<ReceivedNotification>(undefined)
const backgroundNotification = Notifications.useLastNotificationResponse()
const rootNavigationState = useRootNavigationState()

// -------------------------------------------------------------
// ------ Navigating to screen after opening notification ------
// -------------------------------------------------------------

// When initializing push notifications logic navigation is not ready yet
// We need to wait for navigation to set up and that's why there is `rootNavigationState.key` listener
// Ideally this should be added as hook to layout file as described in this tutorial:
// - https://docs.expo.dev/versions/latest/sdk/notifications/#handle-push-notifications-with-navigation
useEffect(() => {
if (notification && rootNavigationState.key) {
deeplinkWhenNotificationReceived(notification)
}
}, [rootNavigationState.key, notification])

// -------------------------------------------------------------
// --------------- Sending push token to backend ---------------
// -------------------------------------------------------------
const tryToRegisterPushToken = useCallback(async () => {
const wasPushTokenSendStringified = await AsyncStorage.getItem(
ASYNC_STORAGE_KEYS.WAS_PUSH_TOKEN_SEND
Expand Down Expand Up @@ -69,29 +88,23 @@ export const NotificationProvider: FC<PropsWithChildren> = ({ children }) => {
// To update immediately permission status
useAppStateActive(tryToRegisterPushToken, true)

// ----------------------------------------------
// fix notifications on android when app is killed
// ----------------------------------------------
// -------------------------------------------------------------
// Listener for notifications when app is killed and in background
// -------------------------------------------------------------
useEffect(() => {
while (getNotificationStackLength() > 0) {
const androidBackgroundNotification = getNotificationFromStack()
if (androidBackgroundNotification) {
setNotification(androidBackgroundNotification)
deeplinkWhenNotificationReceived(androidBackgroundNotification)
}
if (backgroundNotification) {
setNotification({
...backgroundNotification?.notification,
context: {
source: 'useLastNotificationResponse',
},
})
} else {
setNotification(undefined)
}
disableAndroidBackgroundNotificationListener()

// -------------------------------------------------------------
// Listener for notifications when app is killed and in background
// -------------------------------------------------------------
const notificationResponseReceived = Notifications.addNotificationResponseReceivedListener(
({ notification }) => {
setNotification(notification)
deeplinkWhenNotificationReceived(notification)
}
)
}, [backgroundNotification])

useEffect(() => {
// --------------------------------------------------
// listener for notifications when app is in background
// --------------------------------------------------
Expand All @@ -105,7 +118,14 @@ export const NotificationProvider: FC<PropsWithChildren> = ({ children }) => {
{
text: 'Ok',
style: 'default',
onPress: () => deeplinkWhenNotificationReceived(notification),
onPress: () => {
setNotification({
...notification,
context: {
source: 'addNotificationReceivedListener',
},
})
},
},
]

Expand All @@ -119,7 +139,6 @@ export const NotificationProvider: FC<PropsWithChildren> = ({ children }) => {
})

return () => {
Notifications.removeNotificationSubscription(notificationResponseReceived)
Notifications.removeNotificationSubscription(notificationReceived)
}
}, [])
Expand All @@ -130,10 +149,8 @@ export const NotificationProvider: FC<PropsWithChildren> = ({ children }) => {
setPermissionStatus,
notification,
setNotification,
inAppNotification,
setInAppNotification,
}),
[inAppNotification, notification, permissionStatus]
[notification, permissionStatus]
)
return <NotificationContextProvider value={value}>{children}</NotificationContextProvider>
}
54 changes: 1 addition & 53 deletions src/screens/ApplicationInfoScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import { ENV, isExpoGo } from '@baca/constants'
import { Box, Button, Text } from '@baca/design-system'
import {
useCallback,
usePreventGoBack,
useSafeAreaInsets,
useScreenOptions,
useTranslation,
} from '@baca/hooks'
import { usePreventGoBack, useSafeAreaInsets, useScreenOptions, useTranslation } from '@baca/hooks'
// TODO: there are tons of more interesting methods there!
import * as Application from 'expo-application'
import * as Clipboard from 'expo-clipboard'
import * as Notifications from 'expo-notifications'
import { useRouter } from 'expo-router'
import { ScrollView, StyleSheet } from 'react-native'

Expand All @@ -25,51 +16,8 @@ export const ApplicationInfoScreen = (): JSX.Element => {

usePreventGoBack()

const checkNotificationPermissionStatus = useCallback(async () => {
const permissions = await Notifications.getPermissionsAsync()

alert('Permission status' + JSON.stringify(permissions, null, 2))
}, [])

const handleCopyPushToken = useCallback(async () => {
try {
if (!isExpoGo && !ENV.EAS_PROJECT_ID) {
throw new Error(
'You must set `projectId` in eas build then value will be available from Constants?.expoConfig?.extra?.eas?.projectId'
)
}
const token = (
await Notifications.getExpoPushTokenAsync(
!isExpoGo
? {
projectId: ENV.EAS_PROJECT_ID,
}
: {}
)
).data

console.log(token)
await Clipboard.setStringAsync(token)
alert('Copied push token to clipboard.')
} catch (error) {
console.log('error', error)
alert(
JSON.stringify({
message: 'There was an error when copying push token',
error,
})
)
}
}, [])

return (
<ScrollView contentContainerStyle={styles.container}>
<Button my={2} onPress={checkNotificationPermissionStatus}>
Check notification perfmission status
</Button>
<Button my={2} onPress={handleCopyPushToken}>
{t('settings_screen.copy_push_token')}
</Button>
<Text bold>{t('application_info_screen.navigation_info')}</Text>
<Text>{Application.applicationId}</Text>
<Text>{Application.applicationName}</Text>
Expand Down
8 changes: 8 additions & 0 deletions src/screens/ExamplesScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export const ExamplesScreen = () => {
const goToTypography = useCallback(() => push('/example/typography'), [push])
const goToCityListScreen_EXAMPLE = useCallback(() => push('/example/data-from-be'), [push])
const goToTestForm = useCallback(() => push('/example/test-form'), [push])
const goToPushNotificationsHelpers = useCallback(
() => push('/example/push-notifications-helpers'),
[push]
)
const goToUserSession = useCallback(() => push('/example/user-session'), [push])

const goToHomeStackDetails = useCallback(() => push('/home/details'), [push])
Expand Down Expand Up @@ -43,6 +47,10 @@ export const ExamplesScreen = () => {
<Button mb={2} onPress={goToTestForm}>
{t('examples_screen.go_to_screen_test_form')}
</Button>
{/* TODO: Add translations */}
<Button mb={2} onPress={goToPushNotificationsHelpers}>
Go to push notifications helpers
</Button>
<Button mb={2} onPress={goToUserSession}>
{t('examples_screen.go_to_user_session')}
</Button>
Expand Down
Loading

0 comments on commit 06cd16e

Please sign in to comment.