From 1c6e54485d7eac3d3d04158da3ca86328965a9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 22 Oct 2024 16:01:10 +0200 Subject: [PATCH] feat: add push notifications --- app/(app)/notifications.js | 5 ++ components/Icons.tsx | 8 ++- package.json | 2 + pages/Notifications.tsx | 135 ++++++++++++++++++++++++++++++++++++ pages/settings/Settings.tsx | 12 +++- yarn.lock | 103 +++++++++++++++++++++++++-- 6 files changed, 256 insertions(+), 9 deletions(-) create mode 100644 app/(app)/notifications.js create mode 100644 pages/Notifications.tsx diff --git a/app/(app)/notifications.js b/app/(app)/notifications.js new file mode 100644 index 0000000..09a5702 --- /dev/null +++ b/app/(app)/notifications.js @@ -0,0 +1,5 @@ +import { Notifications } from "../../pages/Notifications"; + +export default function Page() { + return ; +} diff --git a/components/Icons.tsx b/components/Icons.tsx index c0cf94f..7cb0e6f 100644 --- a/components/Icons.tsx +++ b/components/Icons.tsx @@ -3,6 +3,7 @@ import { ArchiveRestore, ArrowDown, ArrowLeftRight, + Bell, Bitcoin, BookUser, Camera, @@ -95,13 +96,13 @@ interopIcon(CircleCheck); interopIcon(TriangleAlert); interopIcon(LogOut); interopIcon(ArchiveRestore); +interopIcon(Bell); export { AlertCircle, ArchiveRestore, ArrowDown, - ArrowLeftRight, - Bitcoin, + ArrowLeftRight, Bell, Bitcoin, BookUser, Camera, CameraOff, @@ -136,5 +137,6 @@ export { WalletIcon, X, XCircle, - ZapIcon, + ZapIcon }; + diff --git a/package.json b/package.json index 7e69fa6..0d79d45 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,12 @@ "expo-camera": "~15.0.16", "expo-clipboard": "~6.0.3", "expo-constants": "~16.0.2", + "expo-device": "~6.0.2", "expo-font": "~12.0.10", "expo-linear-gradient": "~13.0.2", "expo-linking": "~6.3.1", "expo-local-authentication": "~14.0.1", + "expo-notifications": "~0.28.19", "expo-router": "^3.5.23", "expo-secure-store": "^13.0.2", "expo-status-bar": "~1.12.1", diff --git a/pages/Notifications.tsx b/pages/Notifications.tsx new file mode 100644 index 0000000..33c60b6 --- /dev/null +++ b/pages/Notifications.tsx @@ -0,0 +1,135 @@ +import Constants from "expo-constants"; +import React, { useEffect, useRef, useState } from "react"; +import { View, Platform } from "react-native"; +import { Button } from "~/components/ui/button"; +import { Text } from "~/components/ui/text"; +import Screen from "~/components/Screen"; + +import * as Device from "expo-device"; +import * as ExpoNotifications from "expo-notifications"; + +ExpoNotifications.setNotificationHandler({ + handleNotification: async () => ({ + shouldShowAlert: true, + shouldPlaySound: false, + shouldSetBadge: false, + }), +}); + +async function sendPushNotification(expoPushToken: string) { + const message = { + to: expoPushToken, + sound: 'default', + title: 'Original Title', + body: 'And here is the body!', + data: { someData: 'goes here' }, + }; + + await fetch('https://exp.host/--/api/v2/push/send', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Accept-encoding': 'gzip, deflate', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(message), + }); +} + +function handleRegistrationError(errorMessage: string) { + alert(errorMessage); + throw new Error(errorMessage); +} + +async function registerForPushNotificationsAsync() { + if (Platform.OS === 'android') { + ExpoNotifications.setNotificationChannelAsync('default', { + name: 'default', + importance: ExpoNotifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: '#FF231F7C', + }); + } + + if (Device.isDevice) { + const { status: existingStatus } = await ExpoNotifications.getPermissionsAsync(); + let finalStatus = existingStatus; + if (existingStatus !== 'granted') { + const { status } = await ExpoNotifications.requestPermissionsAsync(); + finalStatus = status; + } + if (finalStatus !== 'granted') { + handleRegistrationError('Permission not granted to get push token for push notification!'); + return; + } + const projectId = + Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId; + if (!projectId) { + handleRegistrationError('Project ID not found'); + } + try { + const pushTokenString = ( + await ExpoNotifications.getExpoPushTokenAsync({ + projectId, + }) + ).data; + console.log(pushTokenString); + return pushTokenString; + } catch (e: unknown) { + handleRegistrationError(`${e}`); + } + } else { + handleRegistrationError('Must use physical device for push notifications'); + } +} + +export function Notifications() { + const [expoPushToken, setExpoPushToken] = useState(''); + const [notification, setNotification] = useState( + undefined + ); + const notificationListener = useRef(); + const responseListener = useRef(); + + useEffect(() => { + registerForPushNotificationsAsync() + .then(token => setExpoPushToken(token ?? '')) + .catch((error: any) => setExpoPushToken(`${error}`)); + + notificationListener.current = ExpoNotifications.addNotificationReceivedListener(notification => { + setNotification(notification); + }); + + responseListener.current = ExpoNotifications.addNotificationResponseReceivedListener(response => { + console.log(response); + }); + + return () => { + notificationListener.current && + ExpoNotifications.removeNotificationSubscription(notificationListener.current); + responseListener.current && + ExpoNotifications.removeNotificationSubscription(responseListener.current); + }; + }, []); + + return ( + + + Your Expo push token: {expoPushToken} + + Title: {notification && notification.request.content.title} + Body: {notification && notification.request.content.body} + Data: {notification && JSON.stringify(notification.request.content.data)} + + + + ); +} \ No newline at end of file diff --git a/pages/settings/Settings.tsx b/pages/settings/Settings.tsx index 151543e..33eaea6 100644 --- a/pages/settings/Settings.tsx +++ b/pages/settings/Settings.tsx @@ -1,6 +1,7 @@ import { Link, router } from "expo-router"; import { Alert, TouchableOpacity, View } from "react-native"; import { + Bell, Bitcoin, Egg, Fingerprint, @@ -104,7 +105,16 @@ export function Settings() { }} > - Open Onboarding + Onboarding + + { + router.push("/notifications"); + }} + > + + Notifications