eletrotupi / tcc/ commit / fe2deb4

frontend: basic notifications testing screen

Pedro Lucas Porcellis porcellis@eletrotupi.com 1 month ago fe2deb48e4d91c97820de01dec6dd464c725329f
Parents: 6360c22
3 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 + });