From c4516f1e2954ff829f3667c6d359b3050de2a9ef Mon Sep 17 00:00:00 2001 From: Nick Cherry Date: Wed, 31 Jan 2024 14:45:05 -0500 Subject: [PATCH] mobile auth functional --- mobile/src/contexts/AuthProvider.tsx | 75 +++++++++++-------- mobile/src/hooks/data/feed.ts | 14 ++-- mobile/src/hooks/data/profile.ts | 14 ++-- mobile/src/hooks/data/profileCasts.ts | 14 ++-- web/src/app/api/feed/[fid]/route.ts | 15 ++++ web/src/app/api/feed/route.ts | 12 --- web/src/app/api/profiles/[fid]/casts/route.ts | 10 +-- web/src/app/api/profiles/[fid]/route.ts | 10 +-- 8 files changed, 97 insertions(+), 67 deletions(-) create mode 100644 web/src/app/api/feed/[fid]/route.ts delete mode 100644 web/src/app/api/feed/route.ts diff --git a/mobile/src/contexts/AuthProvider.tsx b/mobile/src/contexts/AuthProvider.tsx index 67e2521..27e789c 100644 --- a/mobile/src/contexts/AuthProvider.tsx +++ b/mobile/src/contexts/AuthProvider.tsx @@ -1,4 +1,8 @@ -import { AuthKitProvider, useSignIn } from '@farcaster/auth-kit'; +import { + AuthKitProvider, + StatusAPIResponse, + useSignIn, +} from '@farcaster/auth-kit'; import { FullscreenLoader } from '@mobile/components/loader/FullscreenLoader'; import { useFetchProfile } from '@mobile/hooks/data/profile'; import { User } from '@shared/types/models'; @@ -10,6 +14,7 @@ import { useContext, useEffect, useReducer, + useRef, } from 'react'; import { Linking } from 'react-native'; @@ -87,48 +92,58 @@ type AuthProviderProps = { function AuthProviderContent({ children }: AuthProviderProps) { const [state, dispatch] = useReducer(reducer, initialState); const fetchProfile = useFetchProfile(); + const hasConnectedRef = useRef(false); + const hasLinkedUserRef = useRef(false); const { connect, url, signIn: authKitSignIn, } = useSignIn({ - onSuccess: async (res) => { - const signInResponse = await fetch( - 'http://localhost:3000/api/auth/sign-in', - { - method: 'POST', - body: JSON.stringify({ - message: res.message, - nonce: res.nonce, - signature: res.signature, - }), - }, - ); - - if (signInResponse.ok) { - const { token, fid }: { token: string; fid: string } = - await signInResponse.json(); - - const { profile: user } = await fetchProfile({ fid }); - dispatch({ type: 'signIn', session: { token, fid }, user }); - } else { - alert('Sign in failed'); - } - }, + onSuccess: useCallback( + async (res: StatusAPIResponse) => { + const signInResponse = await fetch( + 'http://localhost:3000/api/auth/sign-in', + { + method: 'POST', + body: JSON.stringify({ + message: res.message, + nonce: res.nonce, + signature: res.signature, + }), + }, + ); + + if (signInResponse.ok) { + const { token, fid }: { token: string; fid: string } = + await signInResponse.json(); + + const { profile: user } = await fetchProfile({ fid }); + dispatch({ type: 'signIn', session: { token, fid }, user }); + } else { + alert('Sign in failed'); + } + }, + [fetchProfile], + ), }); const requestSignIn = useCallback(async () => { - if (!url) { - throw new Error('Expected authkit to provide url'); + if (!hasConnectedRef.current) { + hasConnectedRef.current = true; + connect(); } + }, [connect, hasConnectedRef]); + useEffect(() => { if (url) { - await connect(); - await authKitSignIn(); // Starts polling - Linking.openURL(url); + authKitSignIn(); + if (!hasLinkedUserRef.current) { + hasLinkedUserRef.current = true; + Linking.openURL(url); + } } - }, [authKitSignIn, connect, url]); + }, [authKitSignIn, url]); const signOut = useCallback(async () => { await fetch('http://localhost:3000/api/auth/sign-out', { method: 'POST' }); diff --git a/mobile/src/hooks/data/feed.ts b/mobile/src/hooks/data/feed.ts index ae8174d..db105ea 100644 --- a/mobile/src/hooks/data/feed.ts +++ b/mobile/src/hooks/data/feed.ts @@ -1,6 +1,7 @@ import { baseApiUrl } from '@mobile/constants/api'; import { FeedApiResponse } from '@shared/types/api'; import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; +import { useCallback } from 'react'; function buildFeedKey({ fid }: { fid: string }) { return ['feed', fid]; @@ -23,9 +24,12 @@ export function useFeed({ fid }: { fid: string }) { export function useFetchFeed() { const queryClient = useQueryClient(); - return async ({ fid }: { fid: string }) => { - const data = buildFeedFetcher({ fid })(); - queryClient.setQueryData(buildFeedKey({ fid }), data); - return data; - }; + return useCallback( + async ({ fid }: { fid: string }) => { + const data = buildFeedFetcher({ fid })(); + queryClient.setQueryData(buildFeedKey({ fid }), data); + return data; + }, + [queryClient], + ); } diff --git a/mobile/src/hooks/data/profile.ts b/mobile/src/hooks/data/profile.ts index b8bd609..588f07f 100644 --- a/mobile/src/hooks/data/profile.ts +++ b/mobile/src/hooks/data/profile.ts @@ -1,6 +1,7 @@ import { baseApiUrl } from '@mobile/constants/api'; import { ProfileApiResponse } from '@shared/types/api'; import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; +import { useCallback } from 'react'; function buildProfileKey({ fid }: { fid: string }) { return ['profile', fid]; @@ -23,9 +24,12 @@ export function useProfile({ fid }: { fid: string }) { export function useFetchProfile() { const queryClient = useQueryClient(); - return async ({ fid }: { fid: string }) => { - const data = buildProfileFetcher({ fid })(); - queryClient.setQueryData(buildProfileKey({ fid }), data); - return data; - }; + return useCallback( + async ({ fid }: { fid: string }) => { + const data = buildProfileFetcher({ fid })(); + queryClient.setQueryData(buildProfileKey({ fid }), data); + return data; + }, + [queryClient], + ); } diff --git a/mobile/src/hooks/data/profileCasts.ts b/mobile/src/hooks/data/profileCasts.ts index ac992ae..5052978 100644 --- a/mobile/src/hooks/data/profileCasts.ts +++ b/mobile/src/hooks/data/profileCasts.ts @@ -1,6 +1,7 @@ import { baseApiUrl } from '@mobile/constants/api'; import { ProfileCastsApiResponse } from '@shared/types/api'; import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; +import { useCallback } from 'react'; function buildProfileCastsKey({ fid }: { fid: string }) { return ['profileCasts', fid]; @@ -23,9 +24,12 @@ export function useProfileCasts({ fid }: { fid: string }) { export function useFetchProfileCasts() { const queryClient = useQueryClient(); - return async ({ fid }: { fid: string }) => { - const data = buildProfileCastsFetcher({ fid })(); - queryClient.setQueryData(buildProfileCastsKey({ fid }), data); - return data; - }; + return useCallback( + async ({ fid }: { fid: string }) => { + const data = buildProfileCastsFetcher({ fid })(); + queryClient.setQueryData(buildProfileCastsKey({ fid }), data); + return data; + }, + [queryClient], + ); } diff --git a/web/src/app/api/feed/[fid]/route.ts b/web/src/app/api/feed/[fid]/route.ts new file mode 100644 index 0000000..3d784b4 --- /dev/null +++ b/web/src/app/api/feed/[fid]/route.ts @@ -0,0 +1,15 @@ +import { getFeed } from '@lib/services/feed'; +import { FeedApiResponse } from '@shared/types/api'; +import { NextResponse } from 'next/server'; + +export async function GET( + request: Request, + { params: { fid } }: { params: { fid: string | undefined } }, +) { + if (!fid) { + return NextResponse.json({ error: 'fid is required' }, { status: 400 }); + } + + const payload: FeedApiResponse = { feed: await getFeed({ fid }) }; + return NextResponse.json(payload); +} diff --git a/web/src/app/api/feed/route.ts b/web/src/app/api/feed/route.ts deleted file mode 100644 index 3a4eed9..0000000 --- a/web/src/app/api/feed/route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { getFeed } from "@lib/services/feed"; -import { FeedApiResponse } from "@shared/types/api"; -import { NextResponse } from "next/server"; - -export async function GET(request: Request) { - const fid = new URL(request.url).searchParams.get("fid"); - if (!fid) { - return NextResponse.json({ error: "fid is required" }, { status: 400 }); - } - const payload: FeedApiResponse = { feed: await getFeed({ fid }) }; - return NextResponse.json(payload); -} diff --git a/web/src/app/api/profiles/[fid]/casts/route.ts b/web/src/app/api/profiles/[fid]/casts/route.ts index 400cb85..d9ad6cd 100644 --- a/web/src/app/api/profiles/[fid]/casts/route.ts +++ b/web/src/app/api/profiles/[fid]/casts/route.ts @@ -1,13 +1,13 @@ -import { getCasts } from "@lib/services/casts"; -import { ProfileCastsApiResponse } from "@shared/types/api"; -import { NextResponse } from "next/server"; +import { getCasts } from '@lib/services/casts'; +import { ProfileCastsApiResponse } from '@shared/types/api'; +import { NextResponse } from 'next/server'; export async function GET( _request: Request, - { params: { fid } }: { params: { fid: string } }, + { params: { fid } }: { params: { fid: string | undefined } }, ) { if (!fid) { - return NextResponse.json({ error: "fid is required" }, { status: 400 }); + return NextResponse.json({ error: 'fid is required' }, { status: 400 }); } const payload: ProfileCastsApiResponse = { casts: await getCasts({ fid }) }; diff --git a/web/src/app/api/profiles/[fid]/route.ts b/web/src/app/api/profiles/[fid]/route.ts index fd75d0c..48d745f 100644 --- a/web/src/app/api/profiles/[fid]/route.ts +++ b/web/src/app/api/profiles/[fid]/route.ts @@ -1,13 +1,13 @@ -import { getProfile } from "@lib/services/user"; -import { ProfileApiResponse } from "@shared/types/api"; -import { NextResponse } from "next/server"; +import { getProfile } from '@lib/services/user'; +import { ProfileApiResponse } from '@shared/types/api'; +import { NextResponse } from 'next/server'; export async function GET( _request: Request, - { params: { fid } }: { params: { fid: string } }, + { params: { fid } }: { params: { fid: string | undefined } }, ) { if (!fid) { - return NextResponse.json({ error: "fid is required" }, { status: 400 }); + return NextResponse.json({ error: 'fid is required' }, { status: 400 }); } const payload: ProfileApiResponse = { profile: await getProfile({ fid }) };