diff --git a/app.config.ts b/app.config.ts index cb6a15f..2eb2b35 100644 --- a/app.config.ts +++ b/app.config.ts @@ -39,8 +39,9 @@ export default ({config}: ConfigContext): ExpoConfig => ({ [ 'expo-build-properties', { - ios: {newArchEnabled: true}, - android: {newArchEnabled: true}, + // https://github.com/software-mansion/react-native-screens/issues/2219 + // ios: {newArchEnabled: true}, + // android: {newArchEnabled: true}, }, ], // @ts-ignore @@ -59,6 +60,14 @@ export default ({config}: ConfigContext): ExpoConfig => ({ ], }, ], + [ + 'expo-notifications', + { + icon: './assets/notification-icon.png', + color: '#ffffff', + defaultChannel: 'default', + }, + ], ], experiments: { typedRoutes: true, @@ -94,6 +103,7 @@ export default ({config}: ConfigContext): ExpoConfig => ({ entitlements: { 'com.apple.developer.applesignin': ['Default'], }, + googleServicesFile: process.env.GOOGLE_SERVICES_IOS, infoPlist: { LSApplicationQueriesSchemes: ['mailto'], CFBundleAllowMixedLocalizations: true, @@ -107,7 +117,7 @@ export default ({config}: ConfigContext): ExpoConfig => ({ }, }, android: { - googleServicesFile: process.env.GOOGLE_SERVICES_JSON, + googleServicesFile: process.env.GOOGLE_SERVICES_ANDROID, userInterfaceStyle: 'automatic', permissions: [ 'RECEIVE_BOOT_COMPLETED', @@ -117,6 +127,7 @@ export default ({config}: ConfigContext): ExpoConfig => ({ 'WRITE_EXTERNAL_STORAGE', 'NOTIFICATIONS', 'USER_FACING_NOTIFICATIONS', + 'SCHEDULE_EXACT_ALARM', ], adaptiveIcon: { foregroundImage: './assets/adaptive_icon.png', diff --git a/app/(app)/(tabs)/_layout.tsx b/app/(app)/(tabs)/_layout.tsx index 2f92323..de5e173 100644 --- a/app/(app)/(tabs)/_layout.tsx +++ b/app/(app)/(tabs)/_layout.tsx @@ -1,10 +1,12 @@ import {Pressable, View} from 'react-native'; import {Icon, useDooboo} from 'dooboo-ui'; import {Link, Redirect, Tabs, useRouter} from 'expo-router'; -import {useRecoilValue} from 'recoil'; +import {useRecoilState} from 'recoil'; import {authRecoilState} from '../../../src/recoil/atoms'; import {t} from '../../../src/STRINGS'; +import {useEffect, useRef} from 'react'; +import * as Notifications from 'expo-notifications'; function SettingsMenu(): JSX.Element { const {theme} = useDooboo(); @@ -12,7 +14,7 @@ function SettingsMenu(): JSX.Element { return ( - push('settings')}> + push('/settings')}> {({pressed}) => ( (); + + useEffect(() => { + if (!authId) return; + notificationResponseListener.current = + Notifications.addNotificationResponseReceivedListener((response) => { + console.log(JSON.stringify(response.notification.request)); + }); + + return () => { + notificationResponseListener.current && + Notifications.removeNotificationSubscription( + notificationResponseListener.current, + ); + }; + }, [authId, setAuth]); if (!authId) { return ; diff --git a/app/(app)/(tabs)/profile.tsx b/app/(app)/(tabs)/profile.tsx index be25a61..ffedf42 100644 --- a/app/(app)/(tabs)/profile.tsx +++ b/app/(app)/(tabs)/profile.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/native'; import {Stack} from 'expo-router'; -import {Icon, Typography} from 'dooboo-ui'; +import {Icon, Typography, useDooboo} from 'dooboo-ui'; import {t} from '../../../src/STRINGS'; import {useRecoilValue} from 'recoil'; import {authRecoilState} from '../../../src/recoil/atoms'; @@ -81,7 +81,7 @@ const TagContainer = styled.View` `; const Tag = styled.View` - background-color: ${({theme}) => theme.role.accent}; + background-color: ${({theme}) => theme.role.link}; border-radius: 20px; padding: 6px 12px; margin-right: 8px; @@ -89,12 +89,13 @@ const Tag = styled.View` `; const TagText = styled.Text` - color: ${({theme}) => theme.text.basic}; + color: ${({theme}) => theme.text.contrast}; font-size: 14px; `; export default function Profile(): JSX.Element { const {user, tags} = useRecoilValue(authRecoilState); + const {theme} = useDooboo(); return ( @@ -109,7 +110,7 @@ export default function Profile(): JSX.Element { source={user?.avatar_url ? {uri: user?.avatar_url} : IC_ICON} /> {user?.display_name || ''} - {user?.introduction || ''} + {user?.introduction ? {user?.introduction} : null} @@ -122,7 +123,7 @@ export default function Profile(): JSX.Element { gap: 4px; `} > - + {user?.github_id || ''} @@ -131,26 +132,36 @@ export default function Profile(): JSX.Element { {user?.affiliation || ''} - - - {t('onboarding.desiredConnection')} - {user?.desired_connection || ''} - - - {t('onboarding.futureExpectations')} - {user?.future_expectations || ''} - - - - {t('onboarding.userTags')}: - - {tags?.map((tag, index) => ( - - {tag} - - ))} - - + + {user?.desired_connection || user?.future_expectations ? ( + + {user?.desired_connection ? ( + + {t('onboarding.desiredConnection')} + {user?.desired_connection || ''} + + ) : null} + {user?.future_expectations ? ( + + {t('onboarding.futureExpectations')} + {user?.future_expectations || ''} + + ) : null} + + ) : null} + + {tags?.length ? ( + + {t('onboarding.userTags')} + + {tags.map((tag, index) => ( + + {tag} + + ))} + + + ) : null} diff --git a/app/(app)/onboarding.tsx b/app/(app)/onboarding.tsx index 0193a30..63a0bcc 100644 --- a/app/(app)/onboarding.tsx +++ b/app/(app)/onboarding.tsx @@ -6,7 +6,6 @@ import * as yup from 'yup'; import {Controller, SubmitHandler, useForm} from 'react-hook-form'; import { ActivityIndicator, - Alert, KeyboardAvoidingView, Platform, Pressable, @@ -30,6 +29,7 @@ import {useRecoilState} from 'recoil'; import {authRecoilState} from '../../src/recoil/atoms'; import {ImageInsertArgs} from '../../src/types'; import FallbackComponent from '../../src/components/uis/FallbackComponent'; +import {showAlert} from '../../src/utils/alert'; const Container = styled.SafeAreaView` flex: 1; @@ -55,11 +55,11 @@ const Content = styled.View` `; const schema = yup.object().shape({ - display_name: yup.string().required(t('common.requiredField')), + display_name: yup.string().required(t('common.requiredField')).min(2).max(20), + github_id: yup.string().required(t('common.requiredField')), + affiliation: yup.string().required(t('common.requiredField')), avatar_url: yup.string(), meetup_id: yup.string(), - affiliation: yup.string(), - github_id: yup.string(), other_sns_urls: yup.array().of(yup.string()), tags: yup.array().of(yup.string()), desired_connection: yup.string(), @@ -115,7 +115,7 @@ export default function Onboarding(): JSX.Element { let image: ImageInsertArgs | undefined = {}; - if (profileImg) { + if (profileImg && !profileImg.startsWith('http')) { const destPath = `users/${authId}`; image = await uploadFileToSupabase({ @@ -148,7 +148,7 @@ export default function Onboarding(): JSX.Element { return; } - Alert.alert((error as Error)?.message || ''); + showAlert((error as Error)?.message || ''); } }; @@ -295,7 +295,7 @@ export default function Onboarding(): JSX.Element { displayNameError ? displayNameError : errors.display_name - ? errors.display_name.message + ? t('error.displayNameInvalid') : '' } /> @@ -304,9 +304,10 @@ export default function Onboarding(): JSX.Element { /> ( )} rules={{validate: (value) => !!value}} /> ( )} rules={{validate: (value) => !!value}} /> ( )} rules={{validate: (value) => !!value}} diff --git a/app/(app)/picture.tsx b/app/(app)/picture.tsx index 38d344a..5f34f11 100644 --- a/app/(app)/picture.tsx +++ b/app/(app)/picture.tsx @@ -17,7 +17,14 @@ export default function Picture(): JSX.Element { const [loading, setLoading] = useState(true); if (!imageUrl || typeof imageUrl !== 'string') { - return ; + return ( + <> + + + + ); } return ( diff --git a/app/(app)/post/[id]/update.tsx b/app/(app)/post/[id]/update.tsx index 8edb120..30bf4cc 100644 --- a/app/(app)/post/[id]/update.tsx +++ b/app/(app)/post/[id]/update.tsx @@ -9,6 +9,7 @@ import { KeyboardAvoidingView, Platform, Pressable, + View, } from 'react-native'; import ErrorFallback from '../../../../src/components/uis/FallbackComponent'; import {useRecoilValue} from 'recoil'; @@ -156,7 +157,7 @@ export default function PostUpdate(): JSX.Element { return ( + diff --git a/app/(app)/post/write.tsx b/app/(app)/post/write.tsx index 76883ea..fdcc273 100644 --- a/app/(app)/post/write.tsx +++ b/app/(app)/post/write.tsx @@ -10,6 +10,7 @@ import { KeyboardAvoidingView, Platform, Pressable, + View, } from 'react-native'; import {useRecoilValue} from 'recoil'; import {authRecoilState} from '../../../src/recoil/atoms'; @@ -131,7 +132,7 @@ export default function PostWrite(): JSX.Element { /> + diff --git a/app/(app)/settings/login-info.tsx b/app/(app)/settings/login-info.tsx index 7d88072..4cd4380 100644 --- a/app/(app)/settings/login-info.tsx +++ b/app/(app)/settings/login-info.tsx @@ -14,6 +14,7 @@ import type {User} from '../../../src/types'; import {showConfirm} from '../../../src/utils/alert'; import {AsyncStorageKey} from '../../../src/utils/constants'; import CustomLoadingIndicator from '../../../src/components/uis/CustomLoadingIndicator'; +import { fetchDeletePushToken } from '../../../src/apis/pushTokenQueries'; const Content = styled.View` flex: 1; @@ -93,10 +94,16 @@ export default function LoginInfo(): JSX.Element { const {back, replace} = useRouter(); const {theme, alertDialog} = useDooboo(); const {bottom} = useSafeAreaInsets(); - const auth = useRecoilValue(authRecoilState); + const {authId, user, pushToken} = useRecoilValue(authRecoilState); const handleSignOut = async (): Promise => { - // RNOnesignal?.logout(); + if (pushToken && authId) { + await fetchDeletePushToken({ + authId, + expoPushToken: pushToken, + }) + } + await AsyncStorage.removeItem(AsyncStorageKey.Token); supabase.auth.signOut(); back(); @@ -104,7 +111,7 @@ export default function LoginInfo(): JSX.Element { }; const handleWithdrawUser = async (): Promise => { - if (!auth) { + if (!user || !authId) { return; } @@ -120,13 +127,13 @@ export default function LoginInfo(): JSX.Element { await supabase .from('users') .update({deleted_at: new Date().toISOString()}) - .eq('id', auth); + .eq('id', authId); supabase.auth.signOut(); replace('/'); }; - if (!auth.user) { + if (!user) { return ( <> @@ -148,8 +155,8 @@ export default function LoginInfo(): JSX.Element { {t('loginInfo.loginMethod')}