diff --git a/frontend/.env b/frontend/.env index b6b98fb1..038109d3 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1,5 +1,4 @@ VITE_OPEN_COLLECTIVE_API_KEY=your_key - VITE_API_URL=https://api.utopia-lab.org VITE_VALIDATE_INVITE_FLOW_ID=01d61db0-25aa-4bfa-bc24-c6a8f208a455 VITE_REDEEM_INVITE_FLOW_ID=todo diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3fbd2747..4a3833bb 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -52,6 +52,7 @@ import { Landingpage } from './pages/Landingpage' import MapContainer from './pages/MapContainer' import { getBottomRoutes, routes } from './routes/sidebar' import { InviteApi } from './api/InviteApi' +import { config } from '@/config' const ProfileForm = lazy(() => import('utopia-ui/Profile').then((mod) => ({ @@ -164,7 +165,7 @@ function App() { assetsApi={new assetsApi('https://api.utopia-lab.org/assets/')} appName={map.name} embedded={embedded} - openCollectiveApiKey={import.meta.env.VITE_OPEN_COLLECTIVE_API_KEY} + openCollectiveApiKey={config.openCollectiveApiKey} > }> } /> - } /> + } /> } /> ('') const [password, setPassword] = useState('') @@ -17,12 +23,26 @@ export function LoginPage() { const navigate = useNavigate() - // eslint-disable-next-line react-hooks/exhaustive-deps - const onLogin = async () => { + const handleSuccess = useCallback(async () => { + const inviteCode = localStorage.getItem('inviteCode') + let invitingProfileId: string | null = null + if (inviteCode) { + // If an invite code is stored, redeem it + invitingProfileId = await inviteApi.redeemInvite(inviteCode) + localStorage.removeItem('inviteCode') // Clear invite code after redeeming + } + if (invitingProfileId) { + navigate(`/item/${invitingProfileId}`) + } else { + navigate('/') + } + }, [inviteApi, navigate]) + + const onLogin = useCallback(async () => { await toast.promise(login({ email, password }), { success: { render({ data }) { - navigate('/') + void handleSuccess() return `Hi ${data?.first_name ? data.first_name : 'Traveler'}` }, // other options @@ -36,7 +56,7 @@ export function LoginPage() { }, pending: 'logging in ...', }) - } + }, [email, handleSuccess, login, password]) useEffect(() => { const keyDownHandler = (event: KeyboardEvent) => { diff --git a/lib/src/Components/Auth/useAuth.tsx b/lib/src/Components/Auth/useAuth.tsx index fc69408f..dadd2458 100644 --- a/lib/src/Components/Auth/useAuth.tsx +++ b/lib/src/Components/Auth/useAuth.tsx @@ -1,5 +1,4 @@ import { createContext, useState, useContext, useEffect, useCallback } from 'react' -import { useNavigate } from 'react-router-dom' import type { InviteApi } from '#types/InviteApi' import type { UserApi } from '#types/UserApi' @@ -22,6 +21,7 @@ interface AuthCredentials { interface AuthContextProps { isAuthenticated: boolean + isInitialized: boolean user: UserItem | null login: (credentials: AuthCredentials) => Promise register: (credentials: AuthCredentials, userName: string) => Promise @@ -35,6 +35,7 @@ interface AuthContextProps { const AuthContext = createContext({ isAuthenticated: false, + isInitialized: false, user: null, login: () => Promise.reject(Error('Unimplemented')), register: () => Promise.reject(Error('Unimplemented')), @@ -49,11 +50,11 @@ const AuthContext = createContext({ /** * @category Auth */ -export const AuthProvider = ({ userApi, inviteApi, children }: AuthProviderProps) => { - const navigate = useNavigate() +export const AuthProvider = ({ userApi, children }: AuthProviderProps) => { const [user, setUser] = useState(null) const [token, setToken] = useState() - const [loading, setLoading] = useState(true) + const [loading, setLoading] = useState(false) + const [isInitialized, setIsInitialized] = useState(false) const isAuthenticated = !!user const loadUser: () => Promise = useCallback(async () => { @@ -65,11 +66,15 @@ export const AuthProvider = ({ userApi, inviteApi, children }: AuthProviderProps setUser(me) setLoading(false) return me - } else return undefined + } else { + return undefined + } // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { setLoading(false) return undefined + } finally { + setIsInitialized(true) } }, [userApi]) @@ -83,15 +88,7 @@ export const AuthProvider = ({ userApi, inviteApi, children }: AuthProviderProps const user = await userApi.login(credentials.email, credentials.password) setToken(user?.access_token) const fullUser = await loadUser() - const inviteCode = localStorage.getItem('inviteCode') - if (inviteCode) { - // If an invite code is stored, redeem it - const invitingProfileId = await inviteApi.redeemInvite(inviteCode) - localStorage.removeItem('inviteCode') // Clear invite code after redeeming - if (invitingProfileId) { - navigate(`/item/${invitingProfileId}`) - } - } + return fullUser } catch (error) { setLoading(false) @@ -163,6 +160,7 @@ export const AuthProvider = ({ userApi, inviteApi, children }: AuthProviderProps () const navigate = useNavigate() @@ -35,7 +35,7 @@ export function InvitePage({ inviteApi }: Props) { } } - if (isLoadingAuthentication) return + if (!isAuthenticationInitialized) return if (isAuthenticated) { void redeemInvite() @@ -46,7 +46,7 @@ export function InvitePage({ inviteApi }: Props) { // Redirect to login page navigate('/login') } - }, [id, isAuthenticated, inviteApi, navigate, isLoadingAuthentication]) + }, [id, isAuthenticated, inviteApi, navigate, isAuthenticationInitialized]) return (