Skip to content

Commit

Permalink
feat: add push notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
reneaaron committed Oct 31, 2024
1 parent 1273971 commit 1c6e544
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 9 deletions.
5 changes: 5 additions & 0 deletions app/(app)/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Notifications } from "../../pages/Notifications";

export default function Page() {
return <Notifications />;
}
8 changes: 5 additions & 3 deletions components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ArchiveRestore,
ArrowDown,
ArrowLeftRight,
Bell,
Bitcoin,
BookUser,
Camera,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -136,5 +137,6 @@ export {
WalletIcon,
X,
XCircle,
ZapIcon,
ZapIcon
};

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
135 changes: 135 additions & 0 deletions pages/Notifications.tsx
Original file line number Diff line number Diff line change
@@ -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);

Check failure on line 76 in pages/Notifications.tsx

View workflow job for this annotation

GitHub Actions / linting

Unexpected console statement
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<ExpoNotifications.Notification | undefined>(
undefined
);
const notificationListener = useRef<ExpoNotifications.Subscription>();
const responseListener = useRef<ExpoNotifications.Subscription>();

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);

Check failure on line 104 in pages/Notifications.tsx

View workflow job for this annotation

GitHub Actions / linting

Unexpected console statement
});

return () => {
notificationListener.current &&
ExpoNotifications.removeNotificationSubscription(notificationListener.current);
responseListener.current &&
ExpoNotifications.removeNotificationSubscription(responseListener.current);
};
}, []);

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'space-around' }}>
<Screen
title="Notifications"
/>
<Text>Your Expo push token: {expoPushToken}</Text>
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
<Text>Title: {notification && notification.request.content.title} </Text>
<Text>Body: {notification && notification.request.content.body}</Text>
<Text>Data: {notification && JSON.stringify(notification.request.content.data)}</Text>
</View>
<Button
onPress={async () => {
await sendPushNotification(expoPushToken);
}}
>
<Text>Send push</Text>
</Button>
</View>
);
}
12 changes: 11 additions & 1 deletion pages/settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link, router } from "expo-router";
import { Alert, TouchableOpacity, View } from "react-native";
import {
Bell,
Bitcoin,
Egg,
Fingerprint,
Expand Down Expand Up @@ -104,7 +105,16 @@ export function Settings() {
}}
>
<Egg className="text-foreground" />
<Text className="font-medium2 text-xl">Open Onboarding</Text>
<Text className="font-medium2 text-xl">Onboarding</Text>
</TouchableOpacity>
<TouchableOpacity
className="flex flex-row gap-4"
onPress={() => {
router.push("/notifications");
}}
>
<Bell className="text-foreground" />
<Text className="font-medium2 text-xl">Notifications</Text>
</TouchableOpacity>
<TouchableOpacity
className="flex flex-row gap-4"
Expand Down
Loading

0 comments on commit 1c6e544

Please sign in to comment.