frontend: basic notifications testing screen
Parents:
6360c223 file(s) changed
- frontend/app/(tabs)/_layout.tsx +23 -0
- frontend/app/(tabs)/new/index.tsx +5 -1
- frontend/app/notifications.tsx +126 -0
frontend/app/(tabs)/_layout.tsx
1 1
import { Tabs, Redirect, router } from 'expo-router';
2 2
import React, { useEffect } from 'react';
3 3
import { View, ActivityIndicator } from 'react-native';
4 4
5 5
import { HapticTab } from '@/components/misc/haptic-tab';
6 6
import { IconSymbol } from '@/components/ui/icon-symbol';
7 7
import { Colors } from '@/constants/theme';
8 8
import { useColorScheme } from '@/hooks/use-color-scheme';
9 9
import { useAuth } from '@/context/AuthContext';
10 +
import * as Notifications from "expo-notifications";
10 11
11 12
export default function TabLayout() {
12 13
const colorScheme = useColorScheme();
13 14
const { isAuthenticated, isLoading } = useAuth();
14 15
15 16
useEffect(() => {
16 17
if (!isLoading && !isAuthenticated) {
17 18
router.replace('/auth/login');
18 19
}
19 20
}, [isAuthenticated, isLoading]);
21 +
22 +
// TODO: Store the notification in a hook ctx, then capture if was
23 +
useEffect(() => {
24 +
const subscription = Notifications.addNotificationResponseReceivedListener(response => {
25 +
const screen = response.notification.request.content.data?.screen;
26 +
27 +
if (screen === 'notifications') {
28 +
router.push('/notifications');
29 +
}
30 +
});
31 +
32 +
// Handle notification tap when app was already open
33 +
Notifications.setNotificationHandler({
34 +
handleNotification: async () => ({
35 +
shouldShowAlert: true,
36 +
shouldPlaySound: false,
37 +
shouldSetBadge: false,
38 +
}),
39 +
});
40 +
41 +
return () => subscription.remove();
42 +
}, []);
20 43
21 44
if (isLoading) {
22 45
return (
23 46
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
24 47
<ActivityIndicator size="large" />
▸ 54 unchanged lines
79 102
}}
80 103
/>
81 104
</Tabs>
82 105
);
83 106
}
frontend/app/(tabs)/new/index.tsx
1 1
import { useEffect, useState } from "react";
2 2
import { StyleSheet, View, Text, ActivityIndicator } from "react-native";
3 3
4 4
import { ThemedText } from "@/components/misc/themed-text";
5 5
import { ThemedView } from "@/components/misc/themed-view";
▸ 31 unchanged lines
37 37
const initQuickRegister = () => {
38 38
// TODO: Set down on camelCase vs kebab case
39 39
router.navigate(`/new/entry?initialMood=${mood}`);
40 40
};
41 41
42 +
const onNotificationPress = () => {
43 +
router.push('/notifications');
44 +
}
45 +
42 46
return (
43 47
<ScreenLayout
44 48
userName={user.firstName}
45 49
userAvatar={user.avatarURL}
46 -
onNotificationPress={() => console.log("Notifications")}
50 +
onNotificationPress={onNotificationPress}
47 51
showNotificationBadge={true}
48 52
>
49 53
<Section>
50 54
<SectionHeader
51 55
title="Como você está hoje?"
▸ 133 unchanged lines
185 189
fontSize: 11,
186 190
fontWeight: 600,
187 191
lineHeight: 16.5,
188 192
},
189 193
});
frontend/app/notifications.tsx
@@ -0,0 +1,126 @@
1 + import {
2 + View,
3 + Text,
4 + StyleSheet,
5 + ScrollView,
6 + KeyboardAvoidingView,
7 + Platform,
8 + Alert,
9 + } from 'react-native';
10 +
11 + import {
12 + useState, useEffect
13 + } from 'react';
14 + import * as Notifications from 'expo-notifications';
15 + import { useAuth } from "@/context/AuthContext";
16 +
17 + import { StatusBar } from 'expo-status-bar';
18 + import { SafeAreaView } from 'react-native-safe-area-context';
19 + import { useThemeColor } from '@/hooks/use-theme-color';
20 + import { Button } from '@/components/ui/Button';
21 +
22 + export default function NotificationsPage() {
23 + const { user } = useAuth();
24 + const backgroundColor = useThemeColor({}, 'background');
25 + const [expoPushToken, setExpoPushToken] = useState(user.pushToken);
26 + const [notification, setNotification] = useState<Notifications.Notification | undefined>(
27 + undefined
28 + );
29 +
30 + useEffect(() => {
31 + const notificationListener = Notifications
32 + .addNotificationReceivedListener(notification => {
33 + setNotification(notification);
34 + });
35 +
36 + const responseListener = Notifications
37 + .addNotificationResponseReceivedListener(response => {
38 + console.log(response);
39 + });
40 +
41 + return () => {
42 + notificationListener.remove();
43 + responseListener.remove();
44 + };
45 + }, []);
46 +
47 + useEffect(() => {
48 + setExpoPushToken(user.pushToken)
49 + }, [user]);
50 +
51 + async function sendPushNotification(expoPushToken: string) {
52 + if (expoPushToken) {
53 + const message = {
54 + to: expoPushToken,
55 + sound: 'default',
56 + title: 'Oi meu querido',
57 + body: 'Vamo escutar um The Doors',
58 + data: { screen: 'notifications' },
59 + };
60 +
61 + const response = await fetch('https://exp.host/--/api/v2/push/send', {
62 + method: 'POST',
63 + headers: {
64 + Accept: 'application/json',
65 + 'Accept-encoding': 'gzip, deflate',
66 + 'Content-Type': 'application/json',
67 + },
68 + body: JSON.stringify(message),
69 + });
70 + }
71 + }
72 +
73 + return (
74 + <SafeAreaView style={[styles.container, { backgroundColor }]}>
75 + <StatusBar style={'dark'} />
76 + <KeyboardAvoidingView
77 + behavior='height'
78 + style={styles.keyboardView}
79 + >
80 + <ScrollView
81 + contentContainerStyle={styles.scrollContainer}
82 + showsVerticalScrollIndicator={false}
83 + >
84 + <View>
85 + <Text>
86 + Uma notificação!!!
87 + </Text>
88 + </View>
89 +
90 + <View style={{ alignItems: 'center', justifyContent: 'center' }}>
91 + <Text>Title: {notification && notification.request.content.title} </Text>
92 + <Text>Body: {notification && notification.request.content.body}</Text>
93 + <Text>Data: {notification && JSON.stringify(notification.request.content.data)}</Text>
94 + </View>
95 +
96 + <Button
97 + title="Quer ver uma coisa?"
98 + onPress={async () => {
99 + await sendPushNotification(expoPushToken);
100 + }}
101 + />
102 +
103 + </ScrollView>
104 + </KeyboardAvoidingView>
105 + </SafeAreaView>
106 + );
107 + }
108 +
109 + const styles = StyleSheet.create({
110 + container: {
111 + flex: 1,
112 + },
113 + keyboardView: {
114 + flex: 1,
115 + },
116 + scrollContainer: {
117 + flexGrow: 1,
118 + paddingHorizontal: 24,
119 + justifyContent: 'start',
120 + minHeight: '100%',
121 + },
122 + header: {
123 + alignItems: 'left',
124 + marginBottom: 40,
125 + },
126 + });