Browse Source

text4u notification

master
azri 3 days ago
parent
commit
cf5676d582

+ 3
- 0
.gitignore View File

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

+ 19
- 4
app.json View File

1
 {
1
 {
2
   "expo": {
2
   "expo": {
3
-    "name": "lesson01",
4
-    "slug": "lesson01",
3
+    "name": "text4uapp",
4
+    "slug": "text4uapp",
5
     "version": "1.0.0",
5
     "version": "1.0.0",
6
     "orientation": "portrait",
6
     "orientation": "portrait",
7
     "icon": "./assets/images/icon.png",
7
     "icon": "./assets/images/icon.png",
15
       "adaptiveIcon": {
15
       "adaptiveIcon": {
16
         "foregroundImage": "./assets/images/adaptive-icon.png",
16
         "foregroundImage": "./assets/images/adaptive-icon.png",
17
         "backgroundColor": "#ffffff"
17
         "backgroundColor": "#ffffff"
18
-      }
18
+      },
19
+      "package": "com.mirfalah.text4uapp",
20
+      "googleServicesFile": "./google-services.json"
19
     },
21
     },
20
     "web": {
22
     "web": {
21
       "bundler": "metro",
23
       "bundler": "metro",
36
     ],
38
     ],
37
     "experiments": {
39
     "experiments": {
38
       "typedRoutes": true
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 View File

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 View File

1
-import React, { useState } from 'react';
1
+import React, { useEffect, useState } from 'react';
2
 import { View, StyleSheet, TextInput, Text, ScrollView, Pressable } from 'react-native';
2
 import { View, StyleSheet, TextInput, Text, ScrollView, Pressable } from 'react-native';
3
 import { Appbar, Menu, Avatar, useTheme, Button } from 'react-native-paper';
3
 import { Appbar, Menu, Avatar, useTheme, Button } from 'react-native-paper';
4
-
5
 import Icon from 'react-native-vector-icons/MaterialIcons';
4
 import Icon from 'react-native-vector-icons/MaterialIcons';
6
 import dpuser from "@/assets/images/userdp.png";
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
 const Home = () => {
10
 const Home = () => {
10
   const { colors, fonts } = useTheme();
11
   const { colors, fonts } = useTheme();
12
   const [searchInput, setSearchInput] = useState("")
13
   const [searchInput, setSearchInput] = useState("")
13
   const [showRequest, setshowRequest] = useState(false)
14
   const [showRequest, setshowRequest] = useState(false)
14
   const [activeTab, setActiveTab] = useState('General');
15
   const [activeTab, setActiveTab] = useState('General');
16
+  const [confirmationNotification, setConfirmationNotification] = useState([])
17
+
18
+  useEffect(() => {
19
+    setConfirmationNotification(confirmationNotificationDummyData)
20
+  }, [])
15
 
21
 
16
   const renderNotification = () => {
22
   const renderNotification = () => {
17
     return (
23
     return (
18
-      <Link href={"/home/status"} asChild>
24
+      <Link href={{
25
+        pathname: '/home/status',
26
+        params: { id: '42', message: 'ReactNativeRocks' },
27
+      }} asChild>
19
         <View style={{ marginBottom: 30, borderBottomWidth: 1, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
28
         <View style={{ marginBottom: 30, borderBottomWidth: 1, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
20
           <View style={{ flexDirection: "row", marginBottom: 10 }}>
29
           <View style={{ flexDirection: "row", marginBottom: 10 }}>
21
             <Avatar.Image size={60} source={dpuser}></Avatar.Image>
30
             <Avatar.Image size={60} source={dpuser}></Avatar.Image>
34
   const renderNotificationAction = () => {
43
   const renderNotificationAction = () => {
35
     return (
44
     return (
36
       <View style={{ marginBottom: 30, borderBottomWidth: 1, paddingBottom: 15, borderBottomColor: "rgba(57, 104, 104, 0.3)" }}>
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
           </View>
57
           </View>
44
-        </View>
58
+        </Link>
45
         <Text style={{ fontWeight: "700", marginBottom: 10 }}>Muhammad Ariffudin is working from home today</Text>
59
         <Text style={{ fontWeight: "700", marginBottom: 10 }}>Muhammad Ariffudin is working from home today</Text>
46
         <View style={{ flexDirection: "row", justifyContent: "space-evenly", gap: 10 }}>
60
         <View style={{ flexDirection: "row", justifyContent: "space-evenly", gap: 10 }}>
47
           <Button mode='outlined' style={{ paddingHorizontal: 35, borderColor: colors.primary }} onPress={() => { console.log("click") }}>Reject</Button>
61
           <Button mode='outlined' style={{ paddingHorizontal: 35, borderColor: colors.primary }} onPress={() => { console.log("click") }}>Reject</Button>
53
 
67
 
54
   const renderNotificationResult = (result) => {
68
   const renderNotificationResult = (result) => {
55
     return (
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
           </View>
89
           </View>
64
         </View>
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
 
150
 
132
       {(activeTab == 'Request') && <View style={{ flex: 1 }}>
151
       {(activeTab == 'Request') && <View style={{ flex: 1 }}>
133
         <ScrollView>
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
         </ScrollView>
162
         </ScrollView>
138
       </View>}
163
       </View>}
139
 
164
 

+ 16
- 10
app/(tabs)/home/status.jsx View File

1
 import React, { useState } from 'react';
1
 import React, { useState } from 'react';
2
 import { View, StyleSheet, TextInput, Text, ScrollView, Image } from 'react-native';
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
 import Icon from 'react-native-vector-icons/MaterialIcons';
5
 import Icon from 'react-native-vector-icons/MaterialIcons';
5
 import burgerImage from "@/assets/images/burger.png";
6
 import burgerImage from "@/assets/images/burger.png";
6
 import dpuser from "@/assets/images/userdp.png";
7
 import dpuser from "@/assets/images/userdp.png";
8
+import { Link } from 'expo-router';
7
 
9
 
8
 const Status = () => {
10
 const Status = () => {
9
   const { colors, fonts } = useTheme();
11
   const { colors, fonts } = useTheme();
10
   const [menuVisible, setMenuVisible] = useState(false);
12
   const [menuVisible, setMenuVisible] = useState(false);
11
   const [searchInput, setSearchInput] = useState("")
13
   const [searchInput, setSearchInput] = useState("")
14
+  const router = useRouter();
12
 
15
 
13
   return (
16
   return (
14
     <View style={[styles.container, { backgroundColor: colors.background }]}>
17
     <View style={[styles.container, { backgroundColor: colors.background }]}>
15
       <Appbar.Header>
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
         <View style={styles.titleContainer}>
28
         <View style={styles.titleContainer}>
25
           <Appbar.Content title="Status" titleStyle={[fonts.titleLarge, styles.title]} />
29
           <Appbar.Content title="Status" titleStyle={[fonts.titleLarge, styles.title]} />
44
 
48
 
45
         <View style={{ flex: 1, flexDirection: "row", gap: 10 }}>
49
         <View style={{ flex: 1, flexDirection: "row", gap: 10 }}>
46
           <Avatar.Image size={60} source={burgerImage} style={{ backgroundColor: "rgba(0,0,0,0.2)" }}></Avatar.Image>
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
               <Text style={[fonts.titleMedium, { fontWeight: "bold", marginBottom: 20 }]}>
53
               <Text style={[fonts.titleMedium, { fontWeight: "bold", marginBottom: 20 }]}>
50
                 Order Ready for Pick Up
54
                 Order Ready for Pick Up
51
               </Text>
55
               </Text>
53
                 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.
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
               </Text>
58
               </Text>
55
             </View>
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
               11/2/2024 10:52PM
61
               11/2/2024 10:52PM
58
             </Text>
62
             </Text>
59
           </View>
63
           </View>
60
 
64
 
61
         </View>
65
         </View>
62
 
66
 
67
+        <Button mode='contained' style={{ paddingHorizontal: 35, borderColor: colors.primary }} onPress={() => router.push('/(tabs)/home')}>Back</Button>
68
+
63
       </View>
69
       </View>
64
 
70
 
65
     </View>
71
     </View>

+ 1
- 1
app/(tabs)/notify/index.jsx View File

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

+ 2
- 0
app/index.jsx View File

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

+ 32
- 4
components/BootstrapModal.jsx View File

23
 
23
 
24
       <View style={styles.centeredView}>
24
       <View style={styles.centeredView}>
25
         <View style={styles.modalView}>
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
           <Pressable
31
           <Pressable
28
             style={[styles.button, styles.buttonClose]}
32
             style={[styles.button, styles.buttonClose]}
29
             onPress={() => setVisible(false)}
33
             onPress={() => setVisible(false)}
30
           >
34
           >
31
-            <Text style={styles.textStyle}>Close</Text>
35
+            <Text style={styles.textStyle}>Save</Text>
32
           </Pressable>
36
           </Pressable>
33
         </View>
37
         </View>
34
       </View>
38
       </View>
58
     shadowRadius: 4,
62
     shadowRadius: 4,
59
     elevation: 5,
63
     elevation: 5,
60
   },
64
   },
65
+  titleInputContainer: {
66
+    alignItems:"flex-start"
67
+  },
61
   openButton: {
68
   openButton: {
62
     backgroundColor: '#007bff',
69
     backgroundColor: '#007bff',
63
     borderRadius: 6,
70
     borderRadius: 6,
70
     borderRadius: 6,
77
     borderRadius: 6,
71
   },
78
   },
72
   buttonClose: {
79
   buttonClose: {
73
-    backgroundColor: '#dc3545',
80
+    backgroundColor: '#DD9C49',
81
+    borderRadius: 30,
82
+    paddingVertical: 10,
83
+    paddingHorizontal: 35
74
   },
84
   },
75
   textStyle: {
85
   textStyle: {
76
     color: 'white',
86
     color: 'white',
77
     fontWeight: '600',
87
     fontWeight: '600',
78
     textAlign: 'center',
88
     textAlign: 'center',
89
+
79
   },
90
   },
80
   modalText: {
91
   modalText: {
81
     fontSize: 18,
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
 export default BootstrapModal;
114
 export default BootstrapModal;

+ 131
- 0
components/PushNotification.tsx View File

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 View File

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 View File

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 View File

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
File diff suppressed because it is too large
View File


+ 10
- 6
package.json View File

16
   },
16
   },
17
   "dependencies": {
17
   "dependencies": {
18
     "@expo/vector-icons": "^14.0.2",
18
     "@expo/vector-icons": "^14.0.2",
19
+    "@react-native-firebase/app": "^21.14.0",
19
     "@react-navigation/bottom-tabs": "^7.2.0",
20
     "@react-navigation/bottom-tabs": "^7.2.0",
20
     "@react-navigation/native": "^7.0.14",
21
     "@react-navigation/native": "^7.0.14",
21
     "@rneui/base": "github:react-native-elements/react-native-elements#base",
22
     "@rneui/base": "github:react-native-elements/react-native-elements#base",
22
     "@rneui/themed": "github:react-native-elements/react-native-elements#themed",
23
     "@rneui/themed": "github:react-native-elements/react-native-elements#themed",
23
-    "expo": "~52.0.40",
24
+    "expo": "~52.0.46",
24
     "expo-blur": "~14.0.3",
25
     "expo-blur": "~14.0.3",
25
     "expo-constants": "~17.0.8",
26
     "expo-constants": "~17.0.8",
26
     "expo-font": "~13.0.4",
27
     "expo-font": "~13.0.4",
27
     "expo-haptics": "~14.0.1",
28
     "expo-haptics": "~14.0.1",
28
     "expo-linking": "~7.0.5",
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
     "expo-status-bar": "~2.0.1",
32
     "expo-status-bar": "~2.0.1",
32
     "expo-symbols": "~0.2.2",
33
     "expo-symbols": "~0.2.2",
33
-    "expo-system-ui": "~4.0.8",
34
+    "expo-system-ui": "~4.0.9",
34
     "expo-web-browser": "~14.0.2",
35
     "expo-web-browser": "~14.0.2",
35
     "react": "18.3.1",
36
     "react": "18.3.1",
36
     "react-dom": "18.3.1",
37
     "react-dom": "18.3.1",
37
-    "react-native": "0.76.7",
38
+    "react-native": "0.76.9",
38
     "react-native-gesture-handler": "~2.20.2",
39
     "react-native-gesture-handler": "~2.20.2",
39
     "react-native-paper": "^5.13.1",
40
     "react-native-paper": "^5.13.1",
40
     "react-native-reanimated": "~3.16.1",
41
     "react-native-reanimated": "~3.16.1",
42
     "react-native-screens": "~4.4.0",
43
     "react-native-screens": "~4.4.0",
43
     "react-native-vector-icons": "^10.2.0",
44
     "react-native-vector-icons": "^10.2.0",
44
     "react-native-web": "~0.19.13",
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
   "devDependencies": {
51
   "devDependencies": {
48
     "@babel/core": "^7.25.2",
52
     "@babel/core": "^7.25.2",

+ 13
- 0
react-native-text4u-firebase-adminsdk-fbsvc-1d084e9f00.json View File

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
+}

Loading…
Cancel
Save