From 965665eb702f73771b34a98a62c6a62892619d38 Mon Sep 17 00:00:00 2001 From: hyochan Date: Thu, 19 Sep 2024 00:11:46 +0900 Subject: [PATCH] refactor: unify new supabase client in useSupabase --- app/(home)/(tabs)/index.tsx | 8 +- app/(home)/onboarding.tsx | 15 +- app/(home)/post/[id]/index.tsx | 11 +- app/(home)/post/[id]/replies.tsx | 24 +- app/(home)/post/[id]/update.tsx | 50 ++- app/(home)/post/write.tsx | 26 +- app/(home)/settings/block-users.tsx | 29 +- app/(home)/settings/login-info.tsx | 8 +- app/(home)/settings/profile-update.tsx | 587 +++++++++++++++++++++++++ app/[displayName].tsx | 22 +- app/_layout.tsx | 18 +- assets/langs/en.json | 6 +- assets/langs/ko.json | 6 +- src/apis/blockQueries.ts | 40 +- src/apis/followQueries.ts | 20 +- src/apis/likeQueries.ts | 4 +- src/apis/notifications.ts | 8 +- src/apis/postQueries.ts | 29 +- src/apis/profileQueries.ts | 22 +- src/apis/pushTokenQueries.ts | 14 +- src/apis/replyQueries.ts | 17 +- src/apis/reportQueries.ts | 10 +- src/hooks/useSupabase.tsx | 61 +++ src/providers/AppLogicProvider.tsx | 19 +- src/supabase/index.ts | 58 +-- 25 files changed, 960 insertions(+), 152 deletions(-) create mode 100644 app/(home)/settings/profile-update.tsx create mode 100644 src/hooks/useSupabase.tsx diff --git a/app/(home)/(tabs)/index.tsx b/app/(home)/(tabs)/index.tsx index 26f42b9..559e42c 100644 --- a/app/(home)/(tabs)/index.tsx +++ b/app/(home)/(tabs)/index.tsx @@ -17,6 +17,7 @@ import { } from '../../../src/recoil/atoms'; import ListEmptyItem from '../../../src/components/uis/ListEmptyItem'; import ErrorBoundary from 'react-native-error-boundary'; +import useSupabase from '../../../src/hooks/useSupabase'; const Container = styled.View` flex: 1; @@ -25,6 +26,7 @@ const Container = styled.View` `; export default function Posts(): JSX.Element { + const {supabase} = useSupabase(); const {push} = useRouter(); const {authId, blockedUserIds} = useRecoilValue(authRecoilState); const [cursor, setCursor] = useState(undefined); @@ -34,13 +36,13 @@ export default function Posts(): JSX.Element { const fetcher = useCallback( (cursor: string | undefined) => - fetchPostPagination({cursor, blockedUserIds}), - [blockedUserIds], + fetchPostPagination({cursor, blockedUserIds, supabase: supabase!}), + [blockedUserIds, supabase], ); const {error, isValidating, mutate} = useSWR( ['posts', cursor], - () => fetchPostPagination({cursor, blockedUserIds}), + () => fetchPostPagination({cursor, blockedUserIds, supabase: supabase!}), { revalidateOnFocus: false, revalidateIfStale: false, diff --git a/app/(home)/onboarding.tsx b/app/(home)/onboarding.tsx index dde7689..516a3a9 100644 --- a/app/(home)/onboarding.tsx +++ b/app/(home)/onboarding.tsx @@ -31,6 +31,7 @@ import FallbackComponent from '../../src/components/uis/FallbackComponent'; import {showAlert} from '../../src/utils/alert'; import {RectButton} from 'react-native-gesture-handler'; import ErrorBoundary from 'react-native-error-boundary'; +import useSupabase, { SupabaseClient } from '../../src/hooks/useSupabase'; const Container = styled.SafeAreaView` flex: 1; @@ -76,10 +77,13 @@ type FormData = yup.InferType< } >; -const fetcher = async (authId: string | null) => { +const fetcher = async (authId: string | null, supabase: SupabaseClient) => { if (!authId) return; - const {profile, userTags} = await fetchUserProfile(authId); + const {profile, userTags} = await fetchUserProfile({ + authId, + supabase, + }); return {profile, userTags}; }; @@ -90,9 +94,10 @@ export default function Onboarding(): JSX.Element { const [tag, setTag] = useState(''); const [tags, setTags] = useState([]); const [profileImg, setProfileImg] = useState(); + const {supabase} = useSupabase(); const {data, error} = useSwr(authId && `/profile/${authId}`, () => - fetcher(authId), + fetcher(authId, supabase!), ); const handleAddTag = () => { @@ -112,7 +117,7 @@ export default function Onboarding(): JSX.Element { }); const handleFinishOnboarding: SubmitHandler = async (data) => { - if (!authId) return; + if (!authId || !supabase) return; let image: ImageInsertArgs | undefined = {}; @@ -124,6 +129,7 @@ export default function Onboarding(): JSX.Element { fileType: 'Image', bucket: 'images', destPath, + supabase, }); } @@ -137,6 +143,7 @@ export default function Onboarding(): JSX.Element { args: formDataWithTags, authId, tags: tags || [], + supabase, }); if (updatedUser) { diff --git a/app/(home)/post/[id]/index.tsx b/app/(home)/post/[id]/index.tsx index 4a92b3f..1cbe069 100644 --- a/app/(home)/post/[id]/index.tsx +++ b/app/(home)/post/[id]/index.tsx @@ -31,6 +31,7 @@ import { import {RectButton} from 'react-native-gesture-handler'; import ErrorBoundary from 'react-native-error-boundary'; import FallbackComponent from '../../../../src/components/uis/FallbackComponent'; +import useSupabase from '../../../../src/hooks/useSupabase'; const Container = styled.View` background-color: ${({theme}) => theme.bg.basic}; @@ -44,6 +45,7 @@ const Content = styled.View` `; export default function PostDetails(): JSX.Element { + const {supabase} = useSupabase(); const {id} = useLocalSearchParams<{id: string}>(); const {theme, snackbar} = useDooboo(); const {bottom} = useSafeAreaInsets(); @@ -80,9 +82,9 @@ export default function PostDetails(): JSX.Element { }, [post, authId]); const handleDeletePost = useCallback(async () => { - if (!post) return; + if (!post || !supabase) return; - const result = await fetchDeletePost({id: post.id}); + const result = await fetchDeletePost({id: post.id, supabase}); if (result) { snackbar.open({text: t('common.deleteSuccess')}); @@ -95,7 +97,7 @@ export default function PostDetails(): JSX.Element { color: 'danger', text: t('common.unhandledError'), }); - }, [back, post, snackbar, setPosts]); + }, [post, supabase, snackbar, setPosts, back]); const handlePressMore = useCallback(() => { if (authId === post?.user_id) { @@ -140,7 +142,7 @@ export default function PostDetails(): JSX.Element { }, [post?.user.display_name, push]); const handleToggleLike = async () => { - if (!authId || !post) return; + if (!authId || !post || !supabase) return; const userLike = post.likes?.find((like) => like.user_id === authId); @@ -155,6 +157,7 @@ export default function PostDetails(): JSX.Element { await toggleLike({ userId: authId, postId: post.id, + supabase, }); setPosts((prevPosts) => diff --git a/app/(home)/post/[id]/replies.tsx b/app/(home)/post/[id]/replies.tsx index f8a351b..3a28254 100644 --- a/app/(home)/post/[id]/replies.tsx +++ b/app/(home)/post/[id]/replies.tsx @@ -11,7 +11,6 @@ import ReplyInput from '../../../../src/components/uis/ReplyInput'; import {ImagePickerAsset} from 'expo-image-picker'; import { getPublicUrlFromPath, - supabase, uploadFileToSupabase, } from '../../../../src/supabase'; import { @@ -25,6 +24,7 @@ import {ReplyWithJoins} from '../../../../src/types'; import FallbackComponent from '../../../../src/components/uis/FallbackComponent'; import {toggleLike} from '../../../../src/apis/likeQueries'; import ErrorBoundary from 'react-native-error-boundary'; +import useSupabase from '../../../../src/hooks/useSupabase'; export default function Replies({ flashListRef, @@ -36,6 +36,7 @@ export default function Replies({ postId?: string; replyId?: string; }): JSX.Element { + const {supabase} = useSupabase(); const {bottom} = useSafeAreaInsets(); const {theme} = useDooboo(); const {authId} = useRecoilValue(authRecoilState); @@ -48,7 +49,12 @@ export default function Replies({ const {error, isValidating, mutate} = useSWR( ['replies', postId, cursor], - () => fetchReplyPagination({cursor, postId: postId as string}), + () => + fetchReplyPagination({ + cursor, + postId: postId as string, + supabase: supabase!, + }), { revalidateOnFocus: false, onSuccess: (data) => { @@ -63,7 +69,7 @@ export default function Replies({ ); const handleCreateReply = async () => { - if (!authId || !postId) return; + if (!authId || !postId || !supabase) return; setIsCreateReplyInFlight(true); @@ -77,12 +83,14 @@ export default function Replies({ fileType: asset.type === 'video' ? 'Video' : 'Image', bucket: 'images', destPath, + supabase, }); }); const images = await Promise.all(imageUploadPromises); const newReply = await fetchCreateReply({ + supabase, reply: { message: reply, user_id: authId, @@ -93,7 +101,10 @@ export default function Replies({ .map((el) => ({ ...el, image_url: el?.image_url - ? getPublicUrlFromPath(el.image_url) + ? getPublicUrlFromPath({ + path: el.image_url, + supabase, + }) : undefined, })), }); @@ -126,7 +137,7 @@ export default function Replies({ }; const handleToggleLike = async (replyId: string) => { - if (!authId) return; + if (!authId || !supabase) return; const updatedReplies = replies.map((reply) => { if (reply.id === replyId) { @@ -153,6 +164,7 @@ export default function Replies({ await toggleLike({ userId: authId, replyId, + supabase, }); mutate(); @@ -162,7 +174,7 @@ export default function Replies({ const updatedReplies = replies.filter((reply) => reply.id !== replyId); setReplies(updatedReplies); - await supabase + await supabase! .from('replies') .update({deleted_at: new Date().toISOString()}) .eq('id', replyId); diff --git a/app/(home)/post/[id]/update.tsx b/app/(home)/post/[id]/update.tsx index bc89d5e..441f796 100644 --- a/app/(home)/post/[id]/update.tsx +++ b/app/(home)/post/[id]/update.tsx @@ -28,6 +28,7 @@ import {filterUploadableAssets} from '../../../../src/utils/common'; import {RectButton} from 'react-native-gesture-handler'; import ErrorBoundary from 'react-native-error-boundary'; import FallbackComponent from '../../../../src/components/uis/FallbackComponent'; +import useSupabase from '../../../../src/hooks/useSupabase'; const Container = styled.SafeAreaView` flex: 1; @@ -49,6 +50,7 @@ type FormData = yup.InferType; export default function PostUpdate(): JSX.Element { const {id} = useLocalSearchParams<{id: string}>(); + const {supabase} = useSupabase(); const {back} = useRouter(); const {theme, snackbar} = useDooboo(); const {authId} = useRecoilValue(authRecoilState); @@ -68,28 +70,36 @@ export default function PostUpdate(): JSX.Element { data: post, error, isValidating, - } = useSWR(id ? `post-${id}` : null, () => fetchPostById(id || ''), { - onSuccess: (data) => { - if (data) { - reset({ - title: data.title, - content: data.content, - url: data?.url || undefined, - }); - setAssets( - (data.images || []).map((el) => ({ - uri: el.image_url as string, - type: 'image', - height: el.height || 0, - width: el.width || 0, - })), - ); - } + } = useSWR( + id ? `post-${id}` : null, + () => + fetchPostById({ + id: id || '', + supabase: supabase!, + }), + { + onSuccess: (data) => { + if (data) { + reset({ + title: data.title, + content: data.content, + url: data?.url || undefined, + }); + setAssets( + (data.images || []).map((el) => ({ + uri: el.image_url as string, + type: 'image', + height: el.height || 0, + width: el.width || 0, + })), + ); + } + }, }, - }); + ); const handleUpdatePost: SubmitHandler = async (data) => { - if (!authId || !id) return; + if (!authId || !id || !supabase) return; try { const imageUploadPromises = filterUploadableAssets(assets).map( @@ -102,6 +112,7 @@ export default function PostUpdate(): JSX.Element { fileType: asset.type === 'video' ? 'Video' : 'Image', bucket: 'images', destPath, + supabase, }); }, ); @@ -129,6 +140,7 @@ export default function PostUpdate(): JSX.Element { image_url: el?.image_url || undefined, })), imageUrlsToDelete: deleteImageUrls, + supabase, }); setPosts((prevPosts) => diff --git a/app/(home)/post/write.tsx b/app/(home)/post/write.tsx index 65daedb..8dbf374 100644 --- a/app/(home)/post/write.tsx +++ b/app/(home)/post/write.tsx @@ -21,6 +21,7 @@ import {MAX_IMAGES_UPLOAD_LENGTH} from '../../../src/utils/constants'; import CustomScrollView from '../../../src/components/uis/CustomScrollView'; import {uploadFileToSupabase} from '../../../src/supabase'; import {RectButton} from 'react-native-gesture-handler'; +import useSupabase from '../../../src/hooks/useSupabase'; const Container = styled.SafeAreaView` flex: 1; @@ -41,6 +42,7 @@ const schema = yup.object().shape({ type FormData = yup.InferType; export default function PostWrite(): JSX.Element { + const {supabase} = useSupabase(); const {back} = useRouter(); const {theme, snackbar} = useDooboo(); const {authId} = useRecoilValue(authRecoilState); @@ -57,7 +59,7 @@ export default function PostWrite(): JSX.Element { }); const handleWritePost: SubmitHandler = async (data) => { - if (!authId) return; + if (!authId || !supabase) return; setIsCreatePostInFlight(true); @@ -71,21 +73,25 @@ export default function PostWrite(): JSX.Element { fileType: asset.type === 'video' ? 'Video' : 'Image', bucket: 'images', destPath, + supabase, }); }); const images = await Promise.all(imageUploadPromises); const newPost = await fetchCreatePost({ - title: data.title, - content: data.content, - user_id: authId, - images: images - .filter((el) => !!el) - .map((el) => ({ - ...el, - image_url: el?.image_url || undefined, - })), + supabase, + post: { + title: data.title, + content: data.content, + user_id: authId, + images: images + .filter((el) => !!el) + .map((el) => ({ + ...el, + image_url: el?.image_url || undefined, + })), + }, }); setPosts((prevPosts) => [newPost, ...prevPosts]); diff --git a/app/(home)/settings/block-users.tsx b/app/(home)/settings/block-users.tsx index 382ca4f..2913060 100644 --- a/app/(home)/settings/block-users.tsx +++ b/app/(home)/settings/block-users.tsx @@ -18,6 +18,7 @@ import { import {User} from '../../../src/types'; import ErrorBoundary from 'react-native-error-boundary'; import FallbackComponent from '../../../src/components/uis/FallbackComponent'; +import useSupabase from '../../../src/hooks/useSupabase'; const Profile = styled.View` padding: 16px 16px 8px 16px; @@ -82,10 +83,17 @@ export default function BlockUser(): JSX.Element { const [cursor, setCursor] = useState(undefined); const [loadingMore, setLoadingMore] = useState(false); const [refreshing, setRefreshing] = useState(false); + const {supabase} = useSupabase(); const loadBlockedUsers = useCallback( async (loadMore = false) => { - if (loadingMore || refreshing || !authId || blockedUserIds.length === 0) + if ( + !supabase || + loadingMore || + refreshing || + !authId || + blockedUserIds.length === 0 + ) return; if (loadMore) { @@ -96,11 +104,12 @@ export default function BlockUser(): JSX.Element { } try { - const data = await fetchBlockUsersPagination( - authId, - loadMore ? cursor : new Date().toISOString(), - PAGE_SIZE, - ); + const data = await fetchBlockUsersPagination({ + userId: authId, + limit: PAGE_SIZE, + supabase, + cursor: loadMore ? cursor : new Date().toISOString(), + }); setBlockUsers((prev) => (loadMore ? [...prev, ...data] : data)); @@ -114,7 +123,7 @@ export default function BlockUser(): JSX.Element { setRefreshing(false); } }, - [authId, blockedUserIds.length, cursor, loadingMore, refreshing], + [authId, blockedUserIds.length, cursor, loadingMore, refreshing, supabase], ); useEffect(() => { @@ -147,7 +156,11 @@ export default function BlockUser(): JSX.Element {