Skip to content

Commit

Permalink
wip: notifications flow
Browse files Browse the repository at this point in the history
  • Loading branch information
LuisCusihuaman committed Nov 25, 2023
1 parent 9030b3f commit 6662cad
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 88 deletions.
14 changes: 13 additions & 1 deletion src/core/auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { create } from 'zustand';

import { createSelectors } from '../utils';
import type { TokenType, UserType } from './utils';
import { getToken, getUser, removeToken, setToken, setUser } from './utils';
import {
getToken,
getUser,
registerForPushNotificationsAsync,
removeToken,
setToken,
setUser,
} from './utils';

interface AuthState {
token: TokenType | null;
Expand Down Expand Up @@ -51,6 +58,11 @@ const _useAuth = create<AuthState>((set, get) => ({

const status = isUserComplete(user) ? 'signInComplete' : 'signIn';
set({ status, user, token }); // store token in phone memory ram

// Register for push notifications after successful sign-in
const expoPushToken = await registerForPushNotificationsAsync();
console.log('EXPO PUSH TOKEN', expoPushToken);
// Optionally, send the expoPushToken to your backend for future notifications
},
signOut: async () => {
await removeToken();
Expand Down
44 changes: 43 additions & 1 deletion src/core/auth/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { getItem, removeItem, setItem } from '@/core/storage';
import Constants from 'expo-constants';
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';

import { getItem, removeItem, setItem } from '@/core/storage';
const TOKEN = 'token';
const USER = 'user';

Expand Down Expand Up @@ -30,3 +34,41 @@ export const setToken = (value: TokenType) => setItem<TokenType>(TOKEN, value);
export const getUser = () => getItem<UserType>(USER);
export const removeUser = () => removeItem(USER);
export const setUser = (value: UserType) => setItem<UserType>(USER, value);

export async function registerForPushNotificationsAsync(): Promise<
string | undefined
> {
let token;

if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}

if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
console.error(
'Failed to get push token for push notification because permissions have not been granted'
);
return;
}
const projectId = Constants.expoConfig?.extra?.eas.projectId;
console.log(`EAS PROJECT_ID: ${projectId}`);
token = await Notifications.getExpoPushTokenAsync({ projectId });
} else {
console.error('Must use physical device for Push Notifications');
}

return token ? token.data : undefined;
}
6 changes: 5 additions & 1 deletion src/navigation/navigation-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { useThemeConfig } from './use-theme-config';

export const NavigationContainer = ({
children,
ref,
}: {
children: React.ReactNode;
ref: React.RefObject<any>;
}) => {
const theme = useThemeConfig();
return (
<SafeAreaProvider>
<RNNavigationContainer theme={theme}>{children}</RNNavigationContainer>
<RNNavigationContainer theme={theme} ref={ref}>
{children}
</RNNavigationContainer>
</SafeAreaProvider>
);
};
61 changes: 50 additions & 11 deletions src/navigation/root-navigator.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,69 @@
import type { NavigationContainerRef } from '@react-navigation/native';
import { useNavigationContainerRef } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import * as Notifications from 'expo-notifications';
import * as SplashScreen from 'expo-splash-screen';
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';

import { useAuth } from '@/core';

import { AuthNavigator } from './auth-navigator';
import { NavigationContainer } from './navigation-container';
import { SignInComplete } from './signin-complete';
import { TabNavigator } from './tab-navigator';
import type { RootStackParamList } from './types';

Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});

const Stack = createNativeStackNavigator();
type RootProps = {
navigationRef: NavigationContainerRef<RootStackParamList>;
};

export const Root = () => {
export const Root = ({ navigationRef }: RootProps) => {
const status = useAuth.use.status();
const notificationReceivedListener = useRef<Notifications.Subscription>();
const notificationResponseListener = useRef<Notifications.Subscription>();

const hideSplash = React.useCallback(async () => {
await SplashScreen.hideAsync();
}, []);

useEffect(() => {
if (status !== 'idle') {
hideSplash();
}
}, [hideSplash, status]);

console.log('status:', status);
notificationReceivedListener.current =
Notifications.addNotificationReceivedListener((notif) => {
console.log('Notification received:', notif);
// Handle the notification received
});

notificationResponseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log('Notification response received:', response);
// Handle notification response, possibly navigate
const data = response.notification.request.content.data;
console.log(`Notification data: ${JSON.stringify(data)}`);
if (data.screen && navigationRef.isReady()) {
navigationRef.navigate(data.screen, data.params);
}
});

return () => {
notificationReceivedListener.current?.remove();
notificationResponseListener.current?.remove();
};
}, [hideSplash, status, navigationRef]);
console.log('App status:', status);

return (
<Stack.Navigator
screenOptions={{
Expand All @@ -34,11 +75,7 @@ export const Root = () => {
>
<Stack.Group>
{status === 'signOut' ? (
<Stack.Screen
name="Auth"
component={AuthNavigator}
//options={{ headerTitle: (props) => <LogoTitle {...props} /> }}
/>
<Stack.Screen name="Auth" component={AuthNavigator} />
) : status === 'signInComplete' ? (
<Stack.Screen name="App" component={TabNavigator} />
) : (
Expand All @@ -50,9 +87,11 @@ export const Root = () => {
};

export const RootNavigator = () => {
const navigationRef = useNavigationContainerRef();

return (
<NavigationContainer>
<Root />
<NavigationContainer ref={navigationRef}>
<Root navigationRef={navigationRef} />
</NavigationContainer>
);
};
98 changes: 24 additions & 74 deletions src/screens/feed/notification.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import Constants from 'expo-constants';
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import { useEffect, useRef, useState } from 'react';
import type * as Notifications from 'expo-notifications';
import { useEffect, useState } from 'react';
import React from 'react';
import { Platform } from 'react-native';

import { registerForPushNotificationsAsync } from '@/core/auth/utils';
import { Button, Text, View } from '@/ui';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});

// Can use this function below or use Expo's Push Notification Tool from: https://expo.dev/notifications
async function sendPushNotification(expoPushToken: string) {
Expand All @@ -33,79 +24,38 @@ async function sendPushNotification(expoPushToken: string) {
});
}

async function registerForPushNotificationsAsync(): Promise<
string | undefined
> {
let token;

if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}

if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
console.error(
'Failed to get push token for push notification because permissions have not been granted'
);
return;
}
const projectId = Constants.expoConfig?.extra?.eas.projectId;
console.log(`EAS PROJECT_ID: ${projectId}`);
token = await Notifications.getExpoPushTokenAsync({ projectId });
console.log(`EXPO PUSH TOKEN: ${token.data}`);
} else {
console.error('Must use physical device for Push Notifications');
}

return token ? token.data : undefined;
}

export const NotificationTest = () => {
const [expoPushToken, setExpoPushToken] = useState<string | undefined>('');
const [notification, setNotification] = useState<
Notifications.Notification | boolean
>(false);
const notificationReceivedListener = useRef<Notifications.Subscription>();
const notificationResponseListener = useRef<Notifications.Subscription>();
const [notification] = useState<Notifications.Notification | boolean>(false);
// const notificationReceivedListener = useRef<Notifications.Subscription>();
// const notificationResponseListener = useRef<Notifications.Subscription>();

useEffect(() => {
registerForPushNotificationsAsync().then((token) =>
setExpoPushToken(token)
);

notificationReceivedListener.current =
Notifications.addNotificationReceivedListener((notif) => {
setNotification(notif);
});
// notificationReceivedListener.current =
// Notifications.addNotificationReceivedListener((notif) => {
// setNotification(notif);
// });

notificationResponseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log(response);
});
// notificationResponseListener.current =
// Notifications.addNotificationResponseReceivedListener((response) => {
// console.log(response);
// });
return () => {
// Check if the current value is defined before passing it
if (notificationReceivedListener.current) {
Notifications.removeNotificationSubscription(
notificationReceivedListener.current
);
}
if (notificationResponseListener.current) {
Notifications.removeNotificationSubscription(
notificationResponseListener.current
);
}
// if (notificationReceivedListener.current) {
// Notifications.removeNotificationSubscription(
// notificationReceivedListener.current
// );
// }
// if (notificationResponseListener.current) {
// Notifications.removeNotificationSubscription(
// notificationResponseListener.current
// );
// }
};
}, []);

Expand Down

0 comments on commit 6662cad

Please sign in to comment.