mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Fix some logic with login and redeeming
This commit is contained in:
parent
2b3b3d5889
commit
c0f4715bc8
@ -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
|
||||
|
||||
@ -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}
|
||||
>
|
||||
<Permissions
|
||||
api={permissionsApiInstance}
|
||||
@ -180,7 +181,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route path='/*' element={<MapContainer map={map} layers={layers} />}>
|
||||
<Route path='invite/:id' element={<InvitePage inviteApi={inviteApi} />} />
|
||||
<Route path='login' element={<LoginPage />} />
|
||||
<Route path='login' element={<LoginPage inviteApi={inviteApi} />} />
|
||||
<Route path='signup' element={<SignupPage />} />
|
||||
<Route
|
||||
path='reset-password'
|
||||
|
||||
@ -4,4 +4,5 @@ export const config = {
|
||||
import.meta.env.VITE_VALIDATE_INVITE_FLOW_ID ?? '01d61db0-25aa-4bfa-bc24-c6a8f208a455',
|
||||
),
|
||||
redeemInviteFlowId: String(import.meta.env.VITE_REDEEM_INVITE_FLOW_ID ?? 'todo'),
|
||||
openCollectiveApiKey: String(import.meta.env.VITE_OPEN_COLLECTIVE_API_KEY ?? ''),
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
@ -6,10 +6,16 @@ import { MapOverlayPage } from '#components/Templates/MapOverlayPage'
|
||||
|
||||
import { useAuth } from './useAuth'
|
||||
|
||||
import type { InviteApi } from '#types/InviteApi'
|
||||
|
||||
interface Props {
|
||||
inviteApi: InviteApi
|
||||
}
|
||||
|
||||
/**
|
||||
* @category Auth
|
||||
*/
|
||||
export function LoginPage() {
|
||||
export function LoginPage({ inviteApi }: Props) {
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const [password, setPassword] = useState<string>('')
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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<UserItem | undefined>
|
||||
register: (credentials: AuthCredentials, userName: string) => Promise<UserItem | undefined>
|
||||
@ -35,6 +35,7 @@ interface AuthContextProps {
|
||||
|
||||
const AuthContext = createContext<AuthContextProps>({
|
||||
isAuthenticated: false,
|
||||
isInitialized: false,
|
||||
user: null,
|
||||
login: () => Promise.reject(Error('Unimplemented')),
|
||||
register: () => Promise.reject(Error('Unimplemented')),
|
||||
@ -49,11 +50,11 @@ const AuthContext = createContext<AuthContextProps>({
|
||||
/**
|
||||
* @category Auth
|
||||
*/
|
||||
export const AuthProvider = ({ userApi, inviteApi, children }: AuthProviderProps) => {
|
||||
const navigate = useNavigate()
|
||||
export const AuthProvider = ({ userApi, children }: AuthProviderProps) => {
|
||||
const [user, setUser] = useState<UserItem | null>(null)
|
||||
const [token, setToken] = useState<string>()
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [isInitialized, setIsInitialized] = useState<boolean>(false)
|
||||
const isAuthenticated = !!user
|
||||
|
||||
const loadUser: () => Promise<UserItem | undefined> = 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
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
isAuthenticated,
|
||||
isInitialized,
|
||||
user,
|
||||
login,
|
||||
register,
|
||||
|
||||
@ -15,7 +15,7 @@ interface Props {
|
||||
* @category Onboarding
|
||||
*/
|
||||
export function InvitePage({ inviteApi }: Props) {
|
||||
const { isAuthenticated, loading: isLoadingAuthentication } = useAuth()
|
||||
const { isAuthenticated, isInitialized: isAuthenticationInitialized } = useAuth()
|
||||
const { id } = useParams<{ id: string }>()
|
||||
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 (
|
||||
<MapOverlayPage backdrop className='tw:max-w-xs tw:h-fit'>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user