azri 2 дней назад
Родитель
Сommit
cf5676d582

+ 3
- 0
.gitignore Просмотреть файл

@@ -9,6 +9,9 @@ dist/
9 9
 web-build/
10 10
 expo-env.d.ts
11 11
 
12
+/ios
13
+/android
14
+
12 15
 # Native
13 16
 *.orig.*
14 17
 *.jks

+ 19
- 4
app.json Просмотреть файл

@@ -1,7 +1,7 @@
1 1
 {
2 2
   "expo": {
3
-    "name": "lesson01",
4
-    "slug": "lesson01",
3
+    "name": "text4uapp",
4
+    "slug": "text4uapp",
5 5
     "version": "1.0.0",
6 6
     "orientation": "portrait",
7 7
     "icon": "./assets/images/icon.png",
@@ -15,7 +15,9 @@
15 15
       "adaptiveIcon": {
16 16
         "foregroundImage": "./assets/images/adaptive-icon.png",
17 17
         "backgroundColor": "#ffffff"
18
-      }
18
+      },
19
+      "package": "com.mirfalah.text4uapp",
20
+      "googleServicesFile": "./google-services.json"
19 21
     },
20 22
     "web": {
21 23
       "bundler": "metro",
@@ -36,6 +38,19 @@
36 38
     ],
37 39
     "experiments": {
38 40
       "typedRoutes": true
39
-    }
41
+    },
42
+    "extra": {
43
+      "router": {
44
+        "origin": false
45
+      },
46
+      "eas": {
47
+        "projectId": "1111cd70-7f71-4435-9db3-1c131ada169d"
48
+      }
49
+    },
50
+    "owner": "azriperisiben"
51
+  },
52
+  "notification": {
53
+      "vapidPublicKey": "BJE6QNQyegGZqKbsUDU7SkCbdmm2I481P9wjl0wZWpuWXL4r1RUnV2yNYNI_KTZICk8FzQoMjBqZVyPyXkUQXdU"
40 54
   }
41 55
 }
56
+ 

+ 117
- 0
app/(tabs)/home/confirmation.jsx Просмотреть файл

@@ -0,0 +1,117 @@
1
+import React, { useState } from 'react';
2
+import { View, StyleSheet, TextInput, Text, ScrollView, Image } from 'react-native';
3
+import { Appbar, Menu, Avatar, IconButton, useTheme, Button } from 'react-native-paper';
4
+import { useRouter } from 'expo-router';
5
+import Icon from 'react-native-vector-icons/MaterialIcons';
6
+import burgerImage from "@/assets/images/burger.png";
7
+import dpuser from "@/assets/images/userdp.png";
8
+import { Link } from 'expo-router';
9
+
10
+import confirmationNotificationDummyData from "@/dummyData.json";
11
+
12
+const Status = () => {
13
+  const { colors, fonts } = useTheme();
14
+  const [menuVisible, setMenuVisible] = useState(false);
15
+  const [searchInput, setSearchInput] = useState("")
16
+  const router = useRouter();
17
+
18
+  return (
19
+    <View style={[styles.container, { backgroundColor: colors.background }]}>
20
+      <Appbar.Header>
21
+
22
+        <Link href={"/(tabs)/home"}>
23
+          <IconButton
24
+            icon="chevron-left"
25
+            iconColor={colors.secondary}
26
+            size={24}
27
+          />
28
+        </Link>
29
+
30
+        <View style={styles.titleContainer}>
31
+          <Appbar.Content title="Status" titleStyle={[fonts.titleLarge, styles.title]} />
32
+        </View>
33
+
34
+        <Icon
35
+          name="search"
36
+          size={40}
37
+          color="#FFF"
38
+        />
39
+
40
+      </Appbar.Header>
41
+
42
+      <View style={{ flex: 1 }}>
43
+        <View style={{ alignItems: "center", marginBottom: 20 }}>
44
+          <Text style={[fonts.titleLarge, { color: colors.secondary, fontWeight: "bold" }]}>Abang Bob Burger</Text>
45
+          <Text style={[fonts.titleMedium, { color: colors.secondary, fontWeight: "bold" }]}>No ID : BobBurgerBangi</Text>
46
+        </View>
47
+        <Image source={burgerImage} style={styles.itemImage}></Image>
48
+
49
+        <Text style={[fonts.titleLarge, { fontWeight: "bold", marginBottom: 20 }]}>Notification</Text>
50
+
51
+        <View style={{ flex: 1, flexDirection: "row", gap: 10 }}>
52
+          <Avatar.Image size={60} source={burgerImage} style={{ backgroundColor: "rgba(0,0,0,0.2)" }}></Avatar.Image>
53
+          <View style={{ width: "80%" }}>
54
+            <View style={{ backgroundColor: "white", borderWidth: 1, borderColor: colors.secondary, borderRadius: 20, padding: 20, marginBottom: 5 }}>
55
+              <Text style={[fonts.titleMedium, { fontWeight: "bold", marginBottom: 20 }]}>
56
+                Order Ready for Pick Up
57
+              </Text>
58
+              <Text style={[fonts.titleSmall, { fontWeight: "bold" }]}>
59
+                Your order for Burger Ayam Special is now ready for pick up. Please pick up in 15 minutes as we are preparing for closing soon.
60
+              </Text>
61
+            </View>
62
+            <Text style={[fonts.titleSmall, { fontWeight: "bold", marginLeft: "auto", color: "rgba(0,0,0,0.4)" }]}>
63
+              11/2/2024 10:52PM
64
+            </Text>
65
+          </View>
66
+
67
+        </View>
68
+
69
+        <Button
70
+          mode="contained"
71
+          contentStyle={{ paddingHorizontal: 35 }}
72
+          style={{
73
+            alignSelf: 'flex-start'
74
+          }}
75
+          onPress={() => router.push('/(tabs)/home')}
76
+        >
77
+          <Text>Back</Text>
78
+        </Button>
79
+
80
+      </View>
81
+
82
+    </View>
83
+  );
84
+};
85
+
86
+const styles = StyleSheet.create({
87
+  itemImage: {
88
+    marginLeft: "auto",
89
+    marginRight: "auto",
90
+    marginBottom: 20
91
+  },
92
+  container: {
93
+    flex: 1,
94
+    padding: 20,
95
+    width: '100%',
96
+  },
97
+  badge: {
98
+    backgroundColor: "#BB5C3F",
99
+    color: "#FFF",
100
+    borderRadius: 100,
101
+    paddingVertical: 4,
102
+    paddingHorizontal: 6,
103
+    fontSize: 10,
104
+    fontWeight: "400",
105
+    verticalAlign: "middle",
106
+    marginStart: 5
107
+  },
108
+  titleContainer: {
109
+    flex: 1,
110
+    alignItems: 'center',
111
+  },
112
+  title: {
113
+    fontWeight: 'bold',
114
+  }
115
+});
116
+
117
+export default Status;

+ 54
- 29
app/(tabs)/home/index.jsx Просмотреть файл

@@ -1,10 +1,11 @@
1
-import React, { useState } from 'react';
1
+import React, { useEffect, useState } from 'react';
2 2
 import { View, StyleSheet, TextInput, Text, ScrollView, Pressable } from 'react-native';
3 3
 import { Appbar, Menu, Avatar, useTheme, Button } from 'react-native-paper';
4
-
5 4
 import Icon from 'react-native-vector-icons/MaterialIcons';
6 5
 import dpuser from "@/assets/images/userdp.png";
7
-import { Link } from 'expo-router';
6
+import { Link, router } from 'expo-router';
7
+
8
+import confirmationNotificationDummyData from "@/dummyData.json";
8 9
 
9 10
 const Home = () => {
10 11
   const { colors, fonts } = useTheme();
@@ -12,10 +13,18 @@ const Home = () => {
12 13
   const [searchInput, setSearchInput] = useState("")
13 14
   const [showRequest, setshowRequest] = useState(false)
14 15
   const [activeTab, setActiveTab] = useState('General');
16
+  const [confirmationNotification, setConfirmationNotification] = useState([])
17
+
18
+  useEffect(() => {
19
+    setConfirmationNotification(confirmationNotificationDummyData)
20
+  }, [])
15 21
 
16 22
   const renderNotification = () => {
17 23
     return (
18
-      <Link href={"/home/status"} asChild>
24
+      <Link href={{
25
+        pathname: '/home/status',
26
+        params: { id: '42', message: 'ReactNativeRocks' },
27
+      }} asChild>
19 28
         <View style={{ marginBottom: 30, borderBottomWidth: 1, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
20 29
           <View style={{ flexDirection: "row", marginBottom: 10 }}>
21 30
             <Avatar.Image size={60} source={dpuser}></Avatar.Image>
@@ -34,14 +43,19 @@ const Home = () => {
34 43
   const renderNotificationAction = () => {
35 44
     return (
36 45
       <View style={{ marginBottom: 30, borderBottomWidth: 1, paddingBottom: 15, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
37
-        <View style={{ flexDirection: "row", marginBottom: 10 }}>
38
-          <Avatar.Image size={60} source={dpuser}></Avatar.Image>
39
-          <View style={{ flex: 1, justifyContent: "center", paddingStart: 15, position: "relative", justifyContent: "space-evenly" }}>
40
-            <Text style={{ fontWeight: "700", position: "absolute", top: 5, right: 0, color: "rgba(0,0,0,0.3)" }}>7:55AM</Text>
41
-            <Text style={{ fontWeight: "700" }}>Invest Pahang</Text>
42
-            <Text style={{ fontWeight: "700" }}>ID No : Ariffudin@PSK</Text>
46
+        <Link href={{
47
+          pathname: '/home/status',
48
+          params: { id: '42', message: 'ReactNativeRocks' },
49
+        }} asChild>
50
+          <View style={{ flexDirection: "row", marginBottom: 10 }}>
51
+            <Avatar.Image size={60} source={dpuser}></Avatar.Image>
52
+            <View style={{ flex: 1, justifyContent: "center", paddingStart: 15, position: "relative", justifyContent: "space-evenly" }}>
53
+              <Text style={{ fontWeight: "700", position: "absolute", top: 5, right: 0, color: "rgba(0,0,0,0.3)" }}>7:55AM</Text>
54
+              <Text style={{ fontWeight: "700" }}>Invest Pahang</Text>
55
+              <Text style={{ fontWeight: "700" }}>ID No : Ariffudin@PSK</Text>
56
+            </View>
43 57
           </View>
44
-        </View>
58
+        </Link>
45 59
         <Text style={{ fontWeight: "700", marginBottom: 10 }}>Muhammad Ariffudin is working from home today</Text>
46 60
         <View style={{ flexDirection: "row", justifyContent: "space-evenly", gap: 10 }}>
47 61
           <Button mode='outlined' style={{ paddingHorizontal: 35, borderColor: colors.primary }} onPress={() => { console.log("click") }}>Reject</Button>
@@ -53,23 +67,28 @@ const Home = () => {
53 67
 
54 68
   const renderNotificationResult = (result) => {
55 69
     return (
56
-      <View style={{ marginBottom: 30, borderBottomWidth: 1, paddingBottom: 15, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
57
-        <View style={{ flexDirection: "row", marginBottom: 10 }}>
58
-          <Avatar.Image size={60} source={dpuser}></Avatar.Image>
59
-          <View style={{ flex: 1, justifyContent: "center", paddingStart: 15, position: "relative", justifyContent: "space-evenly" }}>
60
-            <Text style={{ fontWeight: "700", position: "absolute", top: 5, right: 0, color: "rgba(0,0,0,0.3)" }}>7:55AM</Text>
61
-            <Text style={{ fontWeight: "700" }}>Invest Pahang</Text>
62
-            <Text style={{ fontWeight: "700" }}>ID No : Ariffudin@PSK</Text>
70
+      <Link href={{
71
+        pathname: '/home/status',
72
+        params: { id: '42', message: 'ReactNativeRocks' },
73
+      }} asChild>
74
+        <View style={{ marginBottom: 30, borderBottomWidth: 1, paddingBottom: 15, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
75
+          <View style={{ flexDirection: "row", marginBottom: 10 }}>
76
+            <Avatar.Image size={60} source={dpuser}></Avatar.Image>
77
+            <View style={{ flex: 1, justifyContent: "center", paddingStart: 15, position: "relative", justifyContent: "space-evenly" }}>
78
+              <Text style={{ fontWeight: "700", position: "absolute", top: 5, right: 0, color: "rgba(0,0,0,0.3)" }}>7:55AM</Text>
79
+              <Text style={{ fontWeight: "700" }}>Invest Pahang</Text>
80
+              <Text style={{ fontWeight: "700" }}>ID No : Ariffudin@PSK</Text>
81
+            </View>
82
+          </View>
83
+          <Text style={{ fontWeight: "700", marginBottom: 10 }}>Muhammad Ariffudin is working from home today</Text>
84
+          <View style={{ flexDirection: "row" }}>
85
+            {(result) ?
86
+              <Text style={[fonts.titleSmall, { color: colors.primary }]}><Icon name="done" size={20} style={{ verticalAlign: "middle", marginEnd: 10 }} />Request Accepted</Text> :
87
+              <Text style={[fonts.titleSmall, { color: colors.error }]}><Icon name="close" size={20} style={{ verticalAlign: "middle", marginEnd: 10 }} />Request Decline</Text>
88
+            }
63 89
           </View>
64 90
         </View>
65
-        <Text style={{ fontWeight: "700", marginBottom: 10 }}>Muhammad Ariffudin is working from home today</Text>
66
-        <View style={{ flexDirection: "row" }}>
67
-          {(result) ?
68
-            <Text style={[fonts.titleSmall, { color: colors.primary }]}><Icon name="done" size={20} style={{ verticalAlign: "middle", marginEnd: 10 }} />Request Accepted</Text> :
69
-            <Text style={[fonts.titleSmall, { color: colors.error }]}><Icon name="close" size={20} style={{ verticalAlign: "middle", marginEnd: 10 }} />Request Decline</Text>
70
-          }
71
-        </View>
72
-      </View>
91
+      </Link>
73 92
     )
74 93
   }
75 94
 
@@ -131,9 +150,15 @@ const Home = () => {
131 150
 
132 151
       {(activeTab == 'Request') && <View style={{ flex: 1 }}>
133 152
         <ScrollView>
134
-          {[1, 2].map(() => renderNotificationAction())}
135
-          {[1].map(() => renderNotificationResult(true))}
136
-          {[1].map(() => renderNotificationResult(false))}
153
+          {confirmationNotification.map((confirmation) => {
154
+
155
+            if (!confirmation) return
156
+
157
+            if (confirmation.accepted == true) return renderNotificationResult(true)
158
+            else if (confirmation.accepted == false) return renderNotificationResult(false)
159
+            else return renderNotificationAction()
160
+
161
+          })}
137 162
         </ScrollView>
138 163
       </View>}
139 164
 

+ 16
- 10
app/(tabs)/home/status.jsx Просмотреть файл

@@ -1,25 +1,29 @@
1 1
 import React, { useState } from 'react';
2 2
 import { View, StyleSheet, TextInput, Text, ScrollView, Image } from 'react-native';
3
-import { Appbar, Menu, Avatar, IconButton, useTheme } from 'react-native-paper';
3
+import { Appbar, Menu, Avatar, IconButton, useTheme, Button } from 'react-native-paper';
4
+import { useRouter } from 'expo-router';
4 5
 import Icon from 'react-native-vector-icons/MaterialIcons';
5 6
 import burgerImage from "@/assets/images/burger.png";
6 7
 import dpuser from "@/assets/images/userdp.png";
8
+import { Link } from 'expo-router';
7 9
 
8 10
 const Status = () => {
9 11
   const { colors, fonts } = useTheme();
10 12
   const [menuVisible, setMenuVisible] = useState(false);
11 13
   const [searchInput, setSearchInput] = useState("")
14
+  const router = useRouter();
12 15
 
13 16
   return (
14 17
     <View style={[styles.container, { backgroundColor: colors.background }]}>
15 18
       <Appbar.Header>
16 19
 
17
-        <IconButton
18
-          icon="chevron-left" // Or use "arrow-left"
19
-          iconColor={colors.secondary}
20
-          size={24}
21
-          onPress={() => navigation.goBack()} // Navigates back
22
-        />
20
+        <Link href={"/(tabs)/home"}>
21
+          <IconButton
22
+            icon="chevron-left"
23
+            iconColor={colors.secondary}
24
+            size={24}
25
+          />
26
+        </Link>
23 27
 
24 28
         <View style={styles.titleContainer}>
25 29
           <Appbar.Content title="Status" titleStyle={[fonts.titleLarge, styles.title]} />
@@ -44,8 +48,8 @@ const Status = () => {
44 48
 
45 49
         <View style={{ flex: 1, flexDirection: "row", gap: 10 }}>
46 50
           <Avatar.Image size={60} source={burgerImage} style={{ backgroundColor: "rgba(0,0,0,0.2)" }}></Avatar.Image>
47
-          <View style={{width:"80%"}}>
48
-            <View style={{ backgroundColor: "white", borderWidth: 1, borderColor: colors.secondary, borderRadius: 20, padding: 20, marginBottom:5 }}>
51
+          <View style={{ width: "80%" }}>
52
+            <View style={{ backgroundColor: "white", borderWidth: 1, borderColor: colors.secondary, borderRadius: 20, padding: 20, marginBottom: 5 }}>
49 53
               <Text style={[fonts.titleMedium, { fontWeight: "bold", marginBottom: 20 }]}>
50 54
                 Order Ready for Pick Up
51 55
               </Text>
@@ -53,13 +57,15 @@ const Status = () => {
53 57
                 Your order for Burger Ayam Special is now ready for pick up. Please pick up in 15 minutes as we are preparing for closing soon.
54 58
               </Text>
55 59
             </View>
56
-            <Text style={[fonts.titleSmall, { fontWeight: "bold", marginLeft:"auto", color:"rgba(0,0,0,0.4)" }]}>
60
+            <Text style={[fonts.titleSmall, { fontWeight: "bold", marginLeft: "auto", color: "rgba(0,0,0,0.4)" }]}>
57 61
               11/2/2024 10:52PM
58 62
             </Text>
59 63
           </View>
60 64
 
61 65
         </View>
62 66
 
67
+        <Button mode='contained' style={{ paddingHorizontal: 35, borderColor: colors.primary }} onPress={() => router.push('/(tabs)/home')}>Back</Button>
68
+
63 69
       </View>
64 70
 
65 71
     </View>

+ 1
- 1
app/(tabs)/notify/index.jsx Просмотреть файл

@@ -30,7 +30,7 @@ const Notification = () => {
30 30
   const [inputContent, setInputContent] = useState("");
31 31
   const [pickedUser, setPickedUser] = useState(null);
32 32
   const [userNameSearch, setUserNameSearch] = useState('');
33
-  const [visible, setVisible] = useState(false);
33
+  const [visible, setVisible] = useState(true);
34 34
   const [nameSuggestion, setNameSuggestion] = useState([])
35 35
 
36 36
 

+ 2
- 0
app/index.jsx Просмотреть файл

@@ -4,6 +4,7 @@ import { Link } from 'expo-router';
4 4
 import { Button, useTheme } from 'react-native-paper';
5 5
 import text4uLogo from '@/assets/images/text4ulogo.png'
6 6
 import text4uTitle from '@/assets/images/text4utitle.png'
7
+import PushNotification from '@/components/PushNotification';
7 8
 
8 9
 const Welcome = () => {
9 10
     const { colors, button, fonts } = useTheme();
@@ -38,6 +39,7 @@ const Welcome = () => {
38 39
                         Login
39 40
                     </Text>
40 41
                 </Text>
42
+                <PushNotification></PushNotification>
41 43
             </View>
42 44
         </View>
43 45
     );

+ 32
- 4
components/BootstrapModal.jsx Просмотреть файл

@@ -23,12 +23,16 @@ const BootstrapModal = ({ visible, setVisible }) => {
23 23
 
24 24
       <View style={styles.centeredView}>
25 25
         <View style={styles.modalView}>
26
-          <Text style={styles.modalText}>This is a Bootstrap-like Modal!</Text>
26
+          <Text style={styles.modalText}>Save as Preset Text?</Text>
27
+          <View style={styles.titleInputContainer}>
28
+            <Text style={styles.modalSubText}>Set Title</Text>
29
+
30
+          </View>
27 31
           <Pressable
28 32
             style={[styles.button, styles.buttonClose]}
29 33
             onPress={() => setVisible(false)}
30 34
           >
31
-            <Text style={styles.textStyle}>Close</Text>
35
+            <Text style={styles.textStyle}>Save</Text>
32 36
           </Pressable>
33 37
         </View>
34 38
       </View>
@@ -58,6 +62,9 @@ const styles = StyleSheet.create({
58 62
     shadowRadius: 4,
59 63
     elevation: 5,
60 64
   },
65
+  titleInputContainer: {
66
+    alignItems:"flex-start"
67
+  },
61 68
   openButton: {
62 69
     backgroundColor: '#007bff',
63 70
     borderRadius: 6,
@@ -70,17 +77,38 @@ const styles = StyleSheet.create({
70 77
     borderRadius: 6,
71 78
   },
72 79
   buttonClose: {
73
-    backgroundColor: '#dc3545',
80
+    backgroundColor: '#DD9C49',
81
+    borderRadius: 30,
82
+    paddingVertical: 10,
83
+    paddingHorizontal: 35
74 84
   },
75 85
   textStyle: {
76 86
     color: 'white',
77 87
     fontWeight: '600',
78 88
     textAlign: 'center',
89
+
79 90
   },
80 91
   modalText: {
81 92
     fontSize: 18,
82
-    textAlign: 'center',
93
+    color: '#396868',
94
+    fontWeight: 'bolder'
95
+  },
96
+  modalSubText: {
97
+    fontSize: 12,
98
+    color: '#396868',
99
+    alignSelf:"flex-start",
100
+    fontWeight: 'bolder'
83 101
   },
102
+  textArea: {
103
+    height: 220,
104
+    borderColor: "#ccc",
105
+    borderWidth: 1,
106
+    padding: 10,
107
+    borderRadius: 8,
108
+    textAlignVertical: "top", // Makes text start from the top-left
109
+    backgroundColor: "#fff",
110
+    marginBottom: 20
111
+  }
84 112
 });
85 113
 
86 114
 export default BootstrapModal;

+ 131
- 0
components/PushNotification.tsx Просмотреть файл

@@ -0,0 +1,131 @@
1
+import { useState, useEffect, useRef } from 'react';
2
+import { Text, View, Button, Platform } from 'react-native';
3
+import * as Device from 'expo-device';
4
+import * as Notifications from 'expo-notifications';
5
+import Constants from 'expo-constants';
6
+
7
+
8
+Notifications.setNotificationHandler({
9
+  handleNotification: async () => ({
10
+    shouldShowAlert: true,
11
+    shouldPlaySound: true,
12
+    shouldSetBadge: true,
13
+  }),
14
+});
15
+
16
+
17
+
18
+// async function sendPushNotification(expoPushToken: string) {
19
+//   const message = {
20
+//     to: expoPushToken,
21
+//     sound: 'default',
22
+//     title: 'Original Title',
23
+//     body: 'And here is the body!',
24
+//     data: { someData: 'goes here' },
25
+//   };
26
+
27
+//   await fetch('https://exp.host/--/api/v2/push/send', {
28
+//     method: 'POST',
29
+//     headers: {
30
+//       Accept: 'application/json',
31
+//       'Accept-encoding': 'gzip, deflate',
32
+//       'Content-Type': 'application/json',
33
+//     },
34
+//     body: JSON.stringify(message),
35
+//   });
36
+// }
37
+
38
+
39
+function handleRegistrationError(errorMessage: string) {
40
+  alert(errorMessage);
41
+  throw new Error(errorMessage);
42
+}
43
+
44
+async function registerForPushNotificationsAsync() {
45
+  if (Platform.OS === 'android') {
46
+    Notifications.setNotificationChannelAsync('default', {
47
+      name: 'default',
48
+      importance: Notifications.AndroidImportance.MAX,
49
+      vibrationPattern: [0, 250, 250, 250],
50
+      lightColor: '#FF231F7C',
51
+    });
52
+  }
53
+
54
+  if (Device.isDevice) {
55
+    const { status: existingStatus } = await Notifications.getPermissionsAsync();
56
+    let finalStatus = existingStatus;
57
+    if (existingStatus !== 'granted') {
58
+      const { status } = await Notifications.requestPermissionsAsync();
59
+      finalStatus = status;
60
+    }
61
+    if (finalStatus !== 'granted') {
62
+      handleRegistrationError('Permission not granted to get push token for push notification!');
63
+      return;
64
+    }
65
+    const projectId = "1111cd70-7f71-4435-9db3-1c131ada169d";
66
+      //Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId;
67
+    if (!projectId) {
68
+      handleRegistrationError('Project ID not found');
69
+    }
70
+    try {
71
+      const pushTokenString = (
72
+        await Notifications.getExpoPushTokenAsync({
73
+          projectId,
74
+        })
75
+      ).data;
76
+      console.log(pushTokenString);
77
+      return pushTokenString;
78
+    } catch (e: unknown) {
79
+      handleRegistrationError(`${e}`);
80
+    }
81
+  } else {
82
+    handleRegistrationError('Must use physical device for push notifications');
83
+  }
84
+}
85
+
86
+export default function PushNotification() {
87
+  const [expoPushToken, setExpoPushToken] = useState('');
88
+  const [notification, setNotification] = useState<Notifications.Notification | undefined>(
89
+    undefined
90
+  );
91
+  const notificationListener = useRef<Notifications.EventSubscription>();
92
+  const responseListener = useRef<Notifications.EventSubscription>();
93
+
94
+  useEffect(() => {
95
+    registerForPushNotificationsAsync()
96
+      .then(token => setExpoPushToken(token ?? ''))
97
+      .catch((error: any) => setExpoPushToken(`${error}`));
98
+
99
+    notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
100
+      setNotification(notification);
101
+    });
102
+
103
+    responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
104
+      console.log(response);
105
+    });
106
+
107
+    return () => {
108
+      notificationListener.current &&
109
+        Notifications.removeNotificationSubscription(notificationListener.current);
110
+      responseListener.current &&
111
+        Notifications.removeNotificationSubscription(responseListener.current);
112
+    };
113
+  }, []);
114
+
115
+  return (
116
+    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'space-around' }}>
117
+      <Text>Your Expo push token: {expoPushToken}</Text>
118
+      <View style={{ alignItems: 'center', justifyContent: 'center' }}>
119
+        <Text>Title: {notification && notification.request.content.title} </Text>
120
+        <Text>Body: {notification && notification.request.content.body}</Text>
121
+        <Text>Data: {notification && JSON.stringify(notification.request.content.data)}</Text>
122
+      </View>
123
+      {/* <Button
124
+        title="Press to Send Notification"
125
+        onPress={async () => {
126
+          await sendPushNotification(expoPushToken);
127
+        }}
128
+      /> */}
129
+    </View>
130
+  );
131
+}

+ 39
- 0
dummyData.json Просмотреть файл

@@ -0,0 +1,39 @@
1
+[
2
+    {
3
+      "id": 1,
4
+      "uuid": "1IS301SDDOEZ",
5
+      "name": "Invest Pahang",
6
+      "accepted": null,
7
+      "enquiry": "Request to work from home",
8
+      "user": {
9
+        "id": 1,
10
+        "name": "AzmanHadi@PSK",
11
+        "user_img": "https://t4.ftcdn.net/jpg/03/64/21/11/360_F_364211147_1qgLVxv1Tcq0Ohz3FawUfrtONzz8nq3e.jpg"
12
+      }
13
+    },
14
+    {
15
+      "id": 2,
16
+      "uuid": "256DDSREF44",
17
+      "name": "Invest Pahang",
18
+      "accepted": false,
19
+      "enquiry": "Notify for early Annual Leave",
20
+      "user": {
21
+        "id": 2,
22
+        "name": "NorMelati@PSK",
23
+        "user_img": "https://www.shutterstock.com/image-photo/head-shot-portrait-millennial-beautiful-260nw-1893951241.jpg"
24
+      }
25
+    },
26
+    {
27
+      "id": 3,
28
+      "uuid": "78DFEEDDDS",
29
+      "name": "Invest Pahang",
30
+      "accepted": true,
31
+      "enquiry": "Notify for Emergency Leave",
32
+      "user": {
33
+        "id": 3,
34
+        "name": "RazuanKhan@PSK",
35
+        "user_img": "https://plus.unsplash.com/premium_photo-1689568126014-06fea9d5d341?fm=jpg&q=60&w=3000&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8cHJvZmlsZXxlbnwwfHwwfHx8MA%3D%3D"
36
+      }
37
+    }
38
+  ]
39
+  

+ 21
- 0
eas.json Просмотреть файл

@@ -0,0 +1,21 @@
1
+{
2
+  "cli": {
3
+    "version": ">= 16.3.1",
4
+    "appVersionSource": "remote"
5
+  },
6
+  "build": {
7
+    "development": {
8
+      "developmentClient": true,
9
+      "distribution": "internal"
10
+    },
11
+    "preview": {
12
+      "distribution": "internal"
13
+    },
14
+    "production": {
15
+      "autoIncrement": true
16
+    }
17
+  },
18
+  "submit": {
19
+    "production": {}
20
+  }
21
+}

+ 29
- 0
google-services.json Просмотреть файл

@@ -0,0 +1,29 @@
1
+{
2
+  "project_info": {
3
+    "project_number": "643066857095",
4
+    "project_id": "react-native-text4u",
5
+    "storage_bucket": "react-native-text4u.firebasestorage.app"
6
+  },
7
+  "client": [
8
+    {
9
+      "client_info": {
10
+        "mobilesdk_app_id": "1:643066857095:android:7815fe575c60c4143e9a22",
11
+        "android_client_info": {
12
+          "package_name": "com.mirfalah.text4uapp"
13
+        }
14
+      },
15
+      "oauth_client": [],
16
+      "api_key": [
17
+        {
18
+          "current_key": "AIzaSyAee1l7G0PZp4V7hf7OLT2W5J_DyMiHa5E"
19
+        }
20
+      ],
21
+      "services": {
22
+        "appinvite_service": {
23
+          "other_platform_oauth_client": []
24
+        }
25
+      }
26
+    }
27
+  ],
28
+  "configuration_version": "1"
29
+}

+ 1520
- 200
package-lock.json
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


+ 10
- 6
package.json Просмотреть файл

@@ -16,25 +16,26 @@
16 16
   },
17 17
   "dependencies": {
18 18
     "@expo/vector-icons": "^14.0.2",
19
+    "@react-native-firebase/app": "^21.14.0",
19 20
     "@react-navigation/bottom-tabs": "^7.2.0",
20 21
     "@react-navigation/native": "^7.0.14",
21 22
     "@rneui/base": "github:react-native-elements/react-native-elements#base",
22 23
     "@rneui/themed": "github:react-native-elements/react-native-elements#themed",
23
-    "expo": "~52.0.40",
24
+    "expo": "~52.0.46",
24 25
     "expo-blur": "~14.0.3",
25 26
     "expo-constants": "~17.0.8",
26 27
     "expo-font": "~13.0.4",
27 28
     "expo-haptics": "~14.0.1",
28 29
     "expo-linking": "~7.0.5",
29
-    "expo-router": "~4.0.19",
30
-    "expo-splash-screen": "~0.29.22",
30
+    "expo-router": "~4.0.20",
31
+    "expo-splash-screen": "~0.29.24",
31 32
     "expo-status-bar": "~2.0.1",
32 33
     "expo-symbols": "~0.2.2",
33
-    "expo-system-ui": "~4.0.8",
34
+    "expo-system-ui": "~4.0.9",
34 35
     "expo-web-browser": "~14.0.2",
35 36
     "react": "18.3.1",
36 37
     "react-dom": "18.3.1",
37
-    "react-native": "0.76.7",
38
+    "react-native": "0.76.9",
38 39
     "react-native-gesture-handler": "~2.20.2",
39 40
     "react-native-paper": "^5.13.1",
40 41
     "react-native-reanimated": "~3.16.1",
@@ -42,7 +43,10 @@
42 43
     "react-native-screens": "~4.4.0",
43 44
     "react-native-vector-icons": "^10.2.0",
44 45
     "react-native-web": "~0.19.13",
45
-    "react-native-webview": "13.12.5"
46
+    "react-native-webview": "13.12.5",
47
+    "expo-dev-client": "~5.0.20",
48
+    "expo-notifications": "~0.29.14",
49
+    "expo-device": "~7.0.3"
46 50
   },
47 51
   "devDependencies": {
48 52
     "@babel/core": "^7.25.2",

+ 13
- 0
react-native-text4u-firebase-adminsdk-fbsvc-1d084e9f00.json Просмотреть файл

@@ -0,0 +1,13 @@
1
+{
2
+  "type": "service_account",
3
+  "project_id": "react-native-text4u",
4
+  "private_key_id": "1d084e9f0048d1f6851487102674e4b9fd5ceecd",
5
+  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbu7flaw+/2AKo\nbU0H6MQjv7GkQz22ATZCvzNklgkl3Yv+EMbUd/2uQPWM/BAJpFQyoKrhjeEoYxVu\ns4ekCL43P/kPltrxrzizm7VJBnuWdngG0dHGI/VhuHK6Tuch6DuR3ISwtMJDUhcq\nzMyRPhNI32YoRxS0pIMCJueNHNTr0PjrR5y1wplfkd3lOo7aLHCyq4pf4Mv2I1DT\nrZ9nCT8E6eXKn/OP/qP0e6J0QzXhDeO5YFONPwjVzzowF8nYFFrFuZnPvUWJ4Gmi\nvU+4bj06zY9rN4+q7TwmwLv1nJD9yzylygM+vNDm6W5VhlnwI1Yoxb01Wu1t+Sxv\n/kgZi+qLAgMBAAECggEANI+DvouG0kHui7fwIrY91K7zHrSPFGmOTbLIfq9gwQXo\nJMS2xqkE1pYYChBcvVRYEopFgkDneEEQz78KDaPhxzXxMoDAvodcBMqNwerNNXgg\nhHS2b0BdpjChkB3nRnth95XMtvMbCaugJBLhDDg+O/8iNH/hDiLkEIre5bKQblIf\nlgiM23vhIhpfFKULDZL22kOGJ4hQ9e8RSYCwBUZ6WwZfrmNyELzoTuvtAl89xBh1\n82x4COlaXSFZ7FQqZRq08eCwrrPnTfNOcvaHcYhMdUYiRDU0j9pRbyt+A5SKGnzh\nIuLS7diIGfyf1OxhXyndcMspAgASwsJrnnh/2wj7YQKBgQDKchpqWCogsf3V+lAO\nZ3thgtExXSYsPJnh9gZOebxCjGkLCbwGc+sAe4lfa22kEDpSiaPBG7ISFGur5eaS\nb1UVp/MEhJZZVefQsu+YR5+zAdMgzXUuid1Qr+rN64bhwEvaVZTihRvZ5CLV+uUm\nBaUrvjWstUMhJGJG4LHQptjAEQKBgQDE7i+JMQonPsnS2g03B0xrqJvy3x9nC49M\nZH/vpmHta0QeaptCycC8NSIKBMrIAAt0zxdCO6WrsanXt6rcH7FA63r6Fbt7vhLd\nfx8oCoJie7SowkTgUAG6Hp8Od93y71EprI4V/Q1aj/SmM5PO/IuPpPIG7VBH3MhM\n+/CxyQHc2wKBgFZgHLkm6MKObtkvNZ0PterC3/KJ5dGWAaoRSFuPQS27rs3Q13KB\nXtET5Gbb9Sm86sVwiHfKE033fIEZVClCazEUOXHCCEwRhRjcX7G/TXoY2zXxs3+p\nvs3AYRR+20vSugpr3VxQorNyzRIzPDXezrw9KbWImR244l2PXQ7DQVOhAoGAahGs\nkuVSBa9g/2upAIGy3K3VSWx6OoNUbJ4Y9piUWE+VQhjDhaarY/PuYJ0SJ8U2ZnUA\nnxEHe8HWrDTmF2gOjdwHwdmgwNawzJKi0mtOrQLaXKc+d98//uJtY1qWtjnGan7h\njOBep++vozFWpH8MIJOENMzvl7VIQYbVrryIf08CgYEAgvnjmXxA1bcgbTP9SI+e\nYTwnn8UN8gwsD7tG1Wy8FTl+rjFul606IsJOuN2Pq+87aVpFVpV5+Cy4tbaTt3CH\nWLrU07ClRqYKfDbtakCV8L1ui19coC2rPeY3GbawAS3S2gULUlv7mj+mVAYzALR/\nVNpHXVqMX+0MkzKGlp6774w=\n-----END PRIVATE KEY-----\n",
6
+  "client_email": "firebase-adminsdk-fbsvc@react-native-text4u.iam.gserviceaccount.com",
7
+  "client_id": "105205134476001492906",
8
+  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+  "token_uri": "https://oauth2.googleapis.com/token",
10
+  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40react-native-text4u.iam.gserviceaccount.com",
12
+  "universe_domain": "googleapis.com"
13
+}

Загрузка…
Отмена
Сохранить