frontend: patch notifications receiving and draft notification handling
Parents:
d139cd18 file(s) changed
- frontend/app/(tabs)/actions.tsx +0 -1
- frontend/app/(tabs)/history.tsx +0 -1
- frontend/app/(tabs)/insights.tsx +0 -1
- frontend/app/(tabs)/new/index.tsx +0 -5
- frontend/app/(tabs)/settings.tsx +1 -4
- frontend/app/notifications.tsx +27 -16
- frontend/components/ui/ScreenLayout.tsx +3 -1
- frontend/hooks/useNotificationObserver.ts +9 -0
frontend/app/(tabs)/actions.tsx
1 1
import { View, Text, StyleSheet } from "react-native";
2 2
import { ThemedText } from "@/components/misc/themed-text";
3 3
import { ThemedView } from "@/components/misc/themed-view";
4 4
import { ScreenLayout } from "@/components/ui/ScreenLayout";
5 5
import { useAuth } from "@/context/AuthContext";
▸ 24 unchanged lines
30 30
31 31
return (
32 32
<ScreenLayout
33 33
userName={user.firstName}
34 34
userAvatar={user.avatarURL}
35 -
onNotificationPress={() => console.log("Notifications")}
36 35
showNotificationBadge={true}
37 36
>
38 37
<Section>
39 38
<SectionHeader
40 39
title="Ações de Cuidado"
▸ 132 unchanged lines
173 172
fontSize: 12,
174 173
lineHeight: 16,
175 174
alignItems: "center",
176 175
},
177 176
});
frontend/app/(tabs)/history.tsx
1 1
import { useState } from "react";
2 2
import { ScrollView, View, Text } from "react-native";
3 3
import { ScreenLayout } from "@/components/ui/ScreenLayout";
4 4
import { Section, SectionHeader } from "@/components/ui/Sections";
5 5
import { FilterPills } from "@/components/ui/FilterPills";
▸ 31 unchanged lines
37 37
38 38
return (
39 39
<ScreenLayout
40 40
userName={user.firstName}
41 41
userAvatar={user.avatarURL}
42 -
onNotificationPress={() => console.log("Notifications")}
43 42
showNotificationBadge={true}
44 43
>
45 44
<ScrollView>
46 45
<FilterPills active={activeFilter} onChange={setActiveFilter} />
47 46
▸ 9 unchanged lines
57 56
))}
58 57
</ScrollView>
59 58
</ScreenLayout>
60 59
);
61 60
}
frontend/app/(tabs)/insights.tsx
1 1
import { View, Text, ScrollView, StyleSheet } from "react-native";
2 2
3 3
import { ThemedText } from "@/components/misc/themed-text";
4 4
import { ThemedView } from "@/components/misc/themed-view";
5 5
import { ScreenLayout } from "@/components/ui/ScreenLayout";
▸ 12 unchanged lines
18 18
19 19
return (
20 20
<ScreenLayout
21 21
userName={user.firstName}
22 22
userAvatar={user.avatarURL}
23 -
onNotificationPress={() => console.log("Notifications")}
24 23
showNotificationBadge={true}
25 24
>
26 25
<WeeklyResume
27 26
avgMood="Neutro"
28 27
stats={[
▸ 111 unchanged lines
140 139
insightMetric: {
141 140
...Typography.headlineLg,
142 141
fontSize: 22,
143 142
},
144 143
});
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";
▸ 33 unchanged lines
39 39
const initQuickRegister = () => {
40 40
// TODO: Set down on camelCase vs kebab case
41 41
router.navigate(`/new/entry?initialMood=${mood}`);
42 42
};
43 43
44 -
const onNotificationPress = () => {
45 -
router.push("/notifications");
46 -
};
47 -
48 44
return (
49 45
<ScreenLayout
50 46
userName={user.firstName}
51 47
userAvatar={user.avatarURL}
52 -
onNotificationPress={onNotificationPress}
53 48
showNotificationBadge={true}
54 49
>
55 50
<Section>
56 51
<SectionHeader
57 52
title="Como você está hoje?"
▸ 102 unchanged lines
160 155
fontSize: 11,
161 156
fontWeight: 600,
162 157
lineHeight: 16.5,
163 158
},
164 159
});
frontend/app/(tabs)/settings.tsx
1 1
import {
2 2
TouchableOpacity,
3 3
StyleSheet,
4 4
Text,
5 5
SectionList,
▸ 18 unchanged lines
24 24
const [allowNotifications, setAllowNotifications] = useState(false);
25 25
const [allowWeeklyInsights, setAllowWeeklyInsights] = useState(false);
26 26
27 27
const textColor = useThemeColor({}, "text");
28 28
29 -
const onNotificationPress = () => {};
30 -
31 29
const handleLogout = async () => {
32 30
Alert.alert("Sair", "Tem certeza que quer sair?", [
33 31
{ text: "Cancelar", style: "cancel" },
34 32
{
35 33
text: "Sair",
▸ 28 unchanged lines
64 62
65 63
return (
66 64
<ScreenLayout
67 65
userName={user.firstName}
68 66
userAvatar={user.avatarURL}
69 -
onNotificationPress={onNotificationPress}
70 67
showNotificationBadge={true}
71 68
>
72 69
<Section>
73 70
<SectionHeader title="Perfil" />
74 71
▸ 138 unchanged lines
213 210
color: Colors.light.text,
214 211
fontSize: 14,
215 212
fontWeight: "500" as const,
216 213
}}
217 214
>
218 -
Criptografia (EE2E)
215 +
Privacidade
219 216
</Text>
220 217
</View>
221 218
222 219
<Ionicons name="chevron-forward" size={24} color={textColor} />
223 220
</TouchableOpacity>
▸ 82 unchanged lines
306 303
height: StyleSheet.hairlineWidth,
307 304
backgroundColor: "#D1D5DB",
308 305
marginVertical: 16,
309 306
},
310 307
});
frontend/app/notifications.tsx
1 1
import {
2 2
View,
3 3
Text,
4 4
StyleSheet,
5 5
ScrollView,
▸ 18 unchanged lines
24 24
title?: string;
25 25
body?: string;
26 26
notificationId?: string;
27 27
};
28 28
29 -
Notifications.setNotificationHandler({
30 -
handleNotification: async () => ({
31 -
shouldPlaySound: true,
32 -
shouldSetBadge: false,
33 -
shouldShowBanner: true,
34 -
shouldShowList: true,
35 -
}),
36 -
});
29 +
type ReceivedNotification = {
30 +
title?: string;
31 +
body?: string;
32 +
};
37 33
38 34
export default function NotificationsPage() {
39 35
const { user } = useAuth();
40 36
const params = useLocalSearchParams<NotificationParams>();
41 37
const [expoPushToken, setExpoPushToken] = useState(user?.pushToken);
38 +
const [liveNotification, setLiveNotification] = useState<ReceivedNotification | null>(null);
42 39
43 40
useEffect(() => {
44 41
if (user) {
45 42
setExpoPushToken(user.pushToken);
46 43
}
47 44
}, [user]);
48 45
46 +
useEffect(() => {
47 +
const sub = Notifications.addNotificationReceivedListener((n) => {
48 +
setLiveNotification({
49 +
title: n.request.content.title ?? undefined,
50 +
body: n.request.content.body ?? undefined,
51 +
});
52 +
});
53 +
return () => sub.remove();
54 +
}, []);
55 +
56 +
const displayed = liveNotification ?? (params.title || params.body ? { title: params.title, body: params.body } : null);
57 +
49 58
async function sendPushNotification() {
50 59
if (expoPushToken) {
51 60
const message = {
52 61
to: expoPushToken,
53 62
sound: "default",
▸ 23 unchanged lines
77 86
showsVerticalScrollIndicator={false}
78 87
>
79 88
<Section>
80 89
<SectionHeader title="Recebidas" />
81 90
82 -
<Card style={{ padding: Spacing.cardGap }}>
83 -
<Text>Title: {params.title}</Text>
84 -
<Text>Body: {params.body}</Text>
85 -
<Text>
86 -
Data:{" "}
87 -
{params.notificationId && JSON.stringify(params.notificationId)}
88 -
</Text>
89 -
</Card>
91 +
{displayed ? (
92 +
<Card style={{ padding: Spacing.cardGap }}>
93 +
<Text>Title: {displayed.title}</Text>
94 +
<Text>Body: {displayed.body}</Text>
95 +
</Card>
96 +
) : (
97 +
<Card style={{ padding: Spacing.cardGap }}>
98 +
<Text>Nenhuma notificação recebida.</Text>
99 +
</Card>
100 +
)}
90 101
</Section>
91 102
92 103
<Button title="Quer ver uma coisa?" onPress={sendPushNotification} />
93 104
</ScrollView>
94 105
</KeyboardAvoidingView>
▸ 18 unchanged lines
113 124
header: {
114 125
alignItems: "left",
115 126
marginBottom: 40,
116 127
},
117 128
});
frontend/components/ui/ScreenLayout.tsx
1 1
import React from "react";
2 2
3 3
import {
4 4
View,
5 5
ScrollView,
▸ 2 unchanged lines
8 8
Pressable,
9 9
StatusBar,
10 10
} from "react-native";
11 11
12 12
import { SafeAreaView } from "react-native-safe-area-context";
13 +
import { useRouter } from "expo-router";
13 14
import { IconSymbol } from "@/components/ui/icon-symbol";
14 15
import { useThemeColor } from "@/hooks/use-theme-color";
15 16
import { Typography, Spacing, BorderRadius } from "@/constants/theme";
16 17
import { ThemedText } from "@/components/misc/themed-text";
17 18
▸ 12 unchanged lines
30 31
userAvatar,
31 32
onNotificationPress,
32 33
showNotificationBadge = false,
33 34
scrollEnabled = true,
34 35
}) => {
36 +
const router = useRouter();
35 37
const textColor = useThemeColor({}, "text");
36 38
const backgroundColor = useThemeColor({}, "background");
37 39
const surfaceColor = useThemeColor({}, "surface");
38 40
const dividerColor = useThemeColor({}, "divider");
39 41
const accentBlue = useThemeColor({}, "accentBlue");
▸ 76 unchanged lines
116 118
</View>
117 119
</View>
118 120
119 121
<Pressable
120 122
style={styles.notificationButton}
121 -
onPress={onNotificationPress}
123 +
onPress={onNotificationPress ?? (() => router.push("/notifications"))}
122 124
hitSlop={8}
123 125
>
124 126
<View
125 127
style={{
126 128
fontSize: 24,
▸ 18 unchanged lines
145 147
</View>
146 148
);
147 149
};
148 150
149 151
export default ScreenLayout;
frontend/hooks/useNotificationObserver.ts
1 1
import { useEffect, useRef } from "react";
2 2
import * as Notifications from "expo-notifications";
3 3
import { useRouter, useRootNavigationState } from "expo-router";
4 4
5 +
Notifications.setNotificationHandler({
6 +
handleNotification: async () => ({
7 +
shouldPlaySound: true,
8 +
shouldSetBadge: false,
9 +
shouldShowBanner: true,
10 +
shouldShowList: true,
11 +
}),
12 +
});
13 +
5 14
export function useNotificationObserver() {
6 15
const router = useRouter();
7 16
const rootNavState = useRootNavigationState();
8 17
const routerReady = !!rootNavState?.key;
9 18
const handled = useRef(false);
▸ 58 unchanged lines
68 77
isMounted = false;
69 78
subscription.remove();
70 79
};
71 80
}, [routerReady, router]);
72 81
}