diff --git a/.env b/.env
new file mode 100644
index 0000000..06385cd
--- /dev/null
+++ b/.env
@@ -0,0 +1,3 @@
+
+EXPO_PUBLIC_API_URL=https://longdistance.gibbyb.com/api
+EXPO_PUBLIC_API_KEY=@xMr*20K*oOxfbD
diff --git a/app.json b/app.json
index 3b1085e..aa0dac5 100644
--- a/app.json
+++ b/app.json
@@ -13,7 +13,11 @@
"backgroundColor": "#ffffff"
},
"ios": {
- "supportsTablet": true
+ "supportsTablet": true,
+ "usesAppleSignIn": true,
+ "config": {
+ "usesNonExemptEncryption": false
+ }
},
"android": {
"adaptiveIcon": {
@@ -27,10 +31,26 @@
"favicon": "./assets/images/favicon.png"
},
"plugins": [
- "expo-router"
+ "expo-router",
+ "expo-apple-authentication",
+ [
+ "expo-secure-store",
+ {
+ "faceIDPermission": "Allow $(PRODUCT_NAME) to access your Face ID biometric data."
+ }
+ ]
],
"experiments": {
"typedRoutes": true
- }
+ },
+ "extra": {
+ "eas": {
+ "projectId": "52fdbdef-44f6-435f-a303-dd8eeba08432"
+ },
+ "router": {
+ "origin": false
+ }
+ },
+ "owner": "gibbyb"
}
}
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
index 25c71b6..9c27e61 100644
--- a/app/(tabs)/index.tsx
+++ b/app/(tabs)/index.tsx
@@ -19,7 +19,7 @@ const Index = () => {
onPress={() => alert('You pressed a button.')}
>
setIsSignedIn(true)} />
+ );
+ }
return (
-
-
-
-
+
+
+
+
+
+
);
}
diff --git a/assets/fonts/SpaceMono-Regular.ttf b/assets/fonts/SpaceMono-Regular.ttf
old mode 100644
new mode 100755
diff --git a/components/auth/SignInScreen.tsx b/components/auth/SignInScreen.tsx
new file mode 100644
index 0000000..e6fe116
--- /dev/null
+++ b/components/auth/SignInScreen.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import * as AppleAuthentication from 'expo-apple-authentication';
+import { StyleSheet, Alert } from 'react-native';
+import { ThemedView } from '@/components/ThemedView';
+import { useColorScheme } from '@/hooks/useColorScheme';
+import * as Notifications from 'expo-notifications';
+import Constants from 'expo-constants';
+import { saveUserData } from '@/components/services/securestorage/UserData';
+
+type UserData = {
+ appleId: string;
+ appleEmail: string;
+ fullName: string;
+ pushToken: string;
+};
+
+export default function SignInScreen({ onSignIn }: { onSignIn: () => void }) {
+ const scheme = useColorScheme() ?? 'light';
+
+ const handleAppleSignIn = async () => {
+ try {
+ const credential = await AppleAuthentication.signInAsync({
+ requestedScopes: [
+ AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
+ AppleAuthentication.AppleAuthenticationScope.EMAIL,
+ ],
+ });
+
+ const projectId = Constants.expoConfig?.extra?.eas?.projectId;
+ if (!projectId) {
+ throw new Error('Project ID not found');
+ }
+
+ const pushToken = await Notifications.getExpoPushTokenAsync({
+ projectId: projectId
+ });
+
+ console.log(credential.user, credential.email, credential.fullName, credential.fullName?.givenName, credential.fullName?.familyName, pushToken);
+
+
+ const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/users/createUser`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-api-key': process.env.EXPO_PUBLIC_API_KEY ?? '',
+ },
+ body: JSON.stringify({
+ appleId: credential.user,
+ appleEmail: credential.email,
+ fullName: `${credential.fullName?.givenName} ${credential.fullName?.familyName}`,
+ pushToken: pushToken.data,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to create user');
+ }
+
+ const userData = await response.json();
+ await saveUserData(userData);
+ onSignIn();
+
+ } catch (error) {
+ if (error.code === 'ERR_REQUEST_CANCELLED') {
+ // Handle when user cancels sign in
+ console.error('User canceled sign in', error);
+ Alert.alert('An error occurred', 'User canceled sign in');
+ } else {
+ console.error('An unknown error occurred', error);
+ Alert.alert('Unknown error', 'An unknown error occurred');
+ }
+ }
+ };
+
+
+ return (
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ button: {
+ width: 200,
+ height: 44,
+ },
+});
diff --git a/components/services/notifications/PushNotificationManager.tsx b/components/services/notifications/PushNotificationManager.tsx
new file mode 100644
index 0000000..0e7cc89
--- /dev/null
+++ b/components/services/notifications/PushNotificationManager.tsx
@@ -0,0 +1,76 @@
+import React, { useState, useEffect, useRef } from 'react';
+import { Platform } from 'react-native';
+import * as Device from 'expo-device';
+import * as Notifications from 'expo-notifications';
+import Constants from 'expo-constants';
+
+Notifications.setNotificationHandler({
+ handleNotification: async () => ({
+ shouldShowAlert: true,
+ shouldPlaySound: false,
+ shouldSetBadge: false,
+ }),
+});
+
+async function registerForPushNotificationsAsync() {
+ let token;
+
+ if (Platform.OS === 'android') {
+ await Notifications.setNotificationChannelAsync('default', {
+ name: 'default',
+ importance: Notifications.AndroidImportance.MAX,
+ vibrationPattern: [0, 250, 250, 250],
+ lightColor: '#FF231F7C',
+ });
+ }
+
+ if (Device.isDevice) {
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
+ let finalStatus = existingStatus;
+ if (existingStatus !== 'granted') {
+ const { status } = await Notifications.requestPermissionsAsync();
+ finalStatus = status;
+ }
+ if (finalStatus !== 'granted') {
+ alert('Failed to get push token for push notification!');
+ return;
+ }
+ const projectId = Constants.expoConfig?.extra?.eas?.projectId;
+ if (!projectId) {
+ alert('Project ID not found');
+ return;
+ }
+ token = (await Notifications.getExpoPushTokenAsync({ projectId })).data;
+ } else {
+ alert('Must use physical device for Push Notifications');
+ }
+
+ return token;
+}
+
+export function PushNotificationManager({ children }: { children: React.ReactNode }) {
+ const [expoPushToken, setExpoPushToken] = useState('');
+ const [notification, setNotification] = useState(undefined);
+ const notificationListener = useRef();
+ const responseListener = useRef();
+
+ useEffect(() => {
+ registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
+
+ notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
+ setNotification(notification);
+ });
+
+ responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
+ console.log(response);
+ // Handle notification response here
+ });
+
+ return () => {
+ Notifications.removeNotificationSubscription(notificationListener.current!);
+ Notifications.removeNotificationSubscription(responseListener.current!);
+ };
+ }, []);
+
+ return <>{children}>;
+}
diff --git a/components/services/securestorage/UserData.tsx b/components/services/securestorage/UserData.tsx
new file mode 100644
index 0000000..6268a12
--- /dev/null
+++ b/components/services/securestorage/UserData.tsx
@@ -0,0 +1,26 @@
+import * as SecureStore from 'expo-secure-store';
+
+type UserData = {
+ appleId: string;
+ appleEmail: string;
+ fullName: string;
+ pushToken: string;
+};
+
+export const saveUserData = async (userData: any) => {
+ try {
+ await SecureStore.setItemAsync('userData', JSON.stringify(userData));
+ } catch (error) {
+ console.error('Error saving user data:', error);
+ }
+};
+
+export const getUserData = async () => {
+ try {
+ const userData = await SecureStore.getItemAsync('userData');
+ return userData ? JSON.parse(userData) : null;
+ } catch (error) {
+ console.error('Error getting user data:', error);
+ return null;
+ }
+};
diff --git a/example/assets/fonts/SpaceMono-Regular.ttf b/example/assets/fonts/SpaceMono-Regular.ttf
old mode 100644
new mode 100755
diff --git a/example/scripts/reset-project.js b/example/scripts/reset-project.js
old mode 100644
new mode 100755
diff --git a/package-lock.json b/package-lock.json
index 5164e26..395ed4c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,12 +11,16 @@
"@expo/vector-icons": "^14.0.2",
"@react-navigation/native": "^6.0.2",
"expo": "~51.0.28",
+ "expo-apple-authentication": "~6.4.2",
"expo-constants": "~16.0.2",
+ "expo-device": "~6.0.2",
"expo-font": "~12.0.9",
"expo-image": "~1.13.0",
"expo-image-picker": "~15.0.7",
"expo-linking": "~6.3.1",
+ "expo-notifications": "~0.28.18",
"expo-router": "~3.5.23",
+ "expo-secure-store": "~13.0.2",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
@@ -3567,6 +3571,12 @@
"@hapi/hoek": "^9.0.0"
}
},
+ "node_modules/@ide/backoff": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz",
+ "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==",
+ "license": "MIT"
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -7663,6 +7673,19 @@
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"license": "MIT"
},
+ "node_modules/assert": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
+ "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
"node_modules/ast-types": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz",
@@ -8169,6 +8192,12 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/badgin": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz",
+ "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==",
+ "license": "MIT"
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -9566,9 +9595,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.33",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz",
- "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==",
+ "version": "1.5.34",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.34.tgz",
+ "integrity": "sha512-/TZAiChbAflBNjCg+VvstbcwAtIL/VdMFO3NgRFIzBjpvPzWOTIbbO8kNb6RwU4bt9TP7K+3KqBKw/lOU+Y+GA==",
"license": "ISC"
},
"node_modules/emittery": {
@@ -10056,6 +10085,24 @@
"expo": "bin/cli"
}
},
+ "node_modules/expo-apple-authentication": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/expo-apple-authentication/-/expo-apple-authentication-6.4.2.tgz",
+ "integrity": "sha512-X4u1n3Ql1hOpztXHbKNq4I1l4+Ff82gC6RmEeW43Eht7VE6E8PrQBpYKw+JJv8osrCJt7R5O1PZwed6WLN5oig==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-application": {
+ "version": "5.9.1",
+ "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-5.9.1.tgz",
+ "integrity": "sha512-uAfLBNZNahnDZLRU41ZFmNSKtetHUT9Ua557/q189ua0AWV7pQjoVAx49E4953feuvqc9swtU3ScZ/hN1XO/FQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-asset": {
"version": "10.0.10",
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-10.0.10.tgz",
@@ -10083,6 +10130,44 @@
"expo": "*"
}
},
+ "node_modules/expo-device": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-6.0.2.tgz",
+ "integrity": "sha512-sCt91CuTmAuMXX4SlFOn4lIos2UIr8vb0jDstDDZXys6kErcj0uynC7bQAMreU5uRUTKMAl4MAMpKt9ufCXPBw==",
+ "license": "MIT",
+ "dependencies": {
+ "ua-parser-js": "^0.7.33"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-device/node_modules/ua-parser-js": {
+ "version": "0.7.39",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz",
+ "integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ua-parser-js"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/faisalman"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/faisalman"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "ua-parser-js": "script/cli.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/expo-file-system": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz",
@@ -10286,6 +10371,61 @@
"invariant": "^2.2.4"
}
},
+ "node_modules/expo-notifications": {
+ "version": "0.28.18",
+ "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.28.18.tgz",
+ "integrity": "sha512-oRvr8rYhbbKNhVgcO+fj5g5g6vS0umGcElpeMSWa0KudUfOOgV6nNLvv5M89393z2Ahd7wPK4bnK8lygc0nCPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/image-utils": "^0.5.0",
+ "@ide/backoff": "^1.0.0",
+ "abort-controller": "^3.0.0",
+ "assert": "^2.0.0",
+ "badgin": "^1.1.5",
+ "expo-application": "~5.9.0",
+ "expo-constants": "~16.0.0",
+ "fs-extra": "^9.1.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-notifications/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "license": "MIT",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/expo-notifications/node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/expo-notifications/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
"node_modules/expo-router": {
"version": "3.5.23",
"resolved": "https://registry.npmjs.org/expo-router/-/expo-router-3.5.23.tgz",
@@ -10412,6 +10552,15 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/expo-secure-store": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-13.0.2.tgz",
+ "integrity": "sha512-3QYgoneo8p8yeeBPBiAfokNNc2xq6+n8+Ob4fAlErEcf4H7Y72LH+K/dx0nQyWau2ZKZUXBxyyfuHFyVKrEVLg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-splash-screen": {
"version": "0.27.6",
"resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.6.tgz",
@@ -11908,6 +12057,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-negative-zero": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
@@ -16177,6 +16342,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -19295,9 +19476,9 @@
}
},
"node_modules/undici": {
- "version": "6.19.8",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",
- "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==",
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.0.tgz",
+ "integrity": "sha512-AITZfPuxubm31Sx0vr8bteSalEbs9wQb/BOBi9FPlD9Qpd6HxZ4Q0+hI742jBhkPb4RT2v5MQzaW5VhRVyj+9A==",
"license": "MIT",
"engines": {
"node": ">=18.17"
diff --git a/package.json b/package.json
index 709545c..aaa76d7 100644
--- a/package.json
+++ b/package.json
@@ -18,10 +18,16 @@
"@expo/vector-icons": "^14.0.2",
"@react-navigation/native": "^6.0.2",
"expo": "~51.0.28",
+ "expo-apple-authentication": "~6.4.2",
"expo-constants": "~16.0.2",
+ "expo-device": "~6.0.2",
"expo-font": "~12.0.9",
+ "expo-image": "~1.13.0",
+ "expo-image-picker": "~15.0.7",
"expo-linking": "~6.3.1",
+ "expo-notifications": "~0.28.18",
"expo-router": "~3.5.23",
+ "expo-secure-store": "~13.0.2",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
@@ -33,9 +39,7 @@
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1",
- "react-native-web": "~0.19.10",
- "expo-image": "~1.13.0",
- "expo-image-picker": "~15.0.7"
+ "react-native-web": "~0.19.10"
},
"devDependencies": {
"@babel/core": "^7.20.0",