mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Pass profile to redeem Api and adapt to changed redeem flow
This commit is contained in:
parent
5dba5e0ca9
commit
4209bf20d0
@ -46,7 +46,7 @@ import { itemsApi } from './api/itemsApi'
|
||||
import { layersApi } from './api/layersApi'
|
||||
import { mapApi } from './api/mapApi'
|
||||
import { permissionsApi } from './api/permissionsApi'
|
||||
import { userApi } from './api/userApi'
|
||||
import { UserApi } from './api/userApi'
|
||||
import { ModalContent } from './ModalContent'
|
||||
import { Landingpage } from './pages/Landingpage'
|
||||
import MapContainer from './pages/MapContainer'
|
||||
@ -72,7 +72,8 @@ const UserSettings = lazy(() =>
|
||||
})),
|
||||
)
|
||||
|
||||
const inviteApi = new InviteApi()
|
||||
const userApi = new UserApi()
|
||||
const inviteApi = new InviteApi(userApi)
|
||||
|
||||
function App() {
|
||||
const [permissionsApiInstance, setPermissionsApiInstance] = useState<permissionsApi>()
|
||||
@ -160,7 +161,7 @@ function App() {
|
||||
if (map && layers)
|
||||
return (
|
||||
<div className='App overflow-x-hidden'>
|
||||
<AuthProvider userApi={new userApi()} inviteApi={inviteApi}>
|
||||
<AuthProvider userApi={userApi} inviteApi={inviteApi}>
|
||||
<AppShell
|
||||
assetsApi={new assetsApi('https://api.utopia-lab.org/assets/')}
|
||||
appName={map.name}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import type { UserApi } from 'utopia-ui'
|
||||
|
||||
import { config } from '@/config'
|
||||
|
||||
type InvitingProfileResponse = [
|
||||
@ -7,6 +9,12 @@ type InvitingProfileResponse = [
|
||||
]
|
||||
|
||||
export class InviteApi {
|
||||
userApi: UserApi
|
||||
|
||||
constructor(userApi: UserApi) {
|
||||
this.userApi = userApi
|
||||
}
|
||||
|
||||
async validateInvite(inviteId: string): Promise<string | null> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
@ -36,24 +44,27 @@ export class InviteApi {
|
||||
}
|
||||
}
|
||||
|
||||
async redeemInvite(inviteId: string): Promise<string | null> {
|
||||
async redeemInvite(inviteId: string, itemId: string): Promise<string | null> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${config.apiUrl}/flows/trigger/${config.redeemInviteFlowId}?secret=${inviteId}`,
|
||||
{
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
const token = await this.userApi.getToken()
|
||||
|
||||
if (!token) {
|
||||
throw new Error('User is not authenticated. Cannot redeem invite.')
|
||||
}
|
||||
|
||||
const response = await fetch(`${config.apiUrl}/flows/trigger/${config.redeemInviteFlowId}`, {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
)
|
||||
body: JSON.stringify({ secret: inviteId, item: itemId }),
|
||||
})
|
||||
|
||||
if (!response.ok) return null
|
||||
|
||||
const data = (await response.json()) as InvitingProfileResponse
|
||||
|
||||
return data[0].item
|
||||
return (await response.json()) as string
|
||||
} catch (error: unknown) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error fetching inviting profile:', error)
|
||||
|
||||
@ -8,7 +8,7 @@ import { createUser, passwordRequest, passwordReset, readMe, updateMe } from '@d
|
||||
|
||||
import { directusClient } from './directus'
|
||||
|
||||
import type { UserApi, UserItem } from 'utopia-ui'
|
||||
import type { UserItem } from 'utopia-ui'
|
||||
|
||||
interface DirectusError {
|
||||
errors: {
|
||||
@ -17,7 +17,7 @@ interface DirectusError {
|
||||
}[]
|
||||
}
|
||||
|
||||
export class userApi implements UserApi {
|
||||
export class UserApi {
|
||||
async register(email: string, password: string, userName: string): Promise<any> {
|
||||
try {
|
||||
return await directusClient.request(createUser({ email, password, first_name: userName }))
|
||||
|
||||
@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { useMyProfile } from '#components/Map/hooks/useMyProfile'
|
||||
import { MapOverlayPage } from '#components/Templates/MapOverlayPage'
|
||||
|
||||
import { useAuth } from './useAuth'
|
||||
@ -21,22 +22,36 @@ export function LoginPage({ inviteApi }: Props) {
|
||||
|
||||
const { login, loading } = useAuth()
|
||||
|
||||
const myProfile = useMyProfile()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const redeemInvite = useCallback(
|
||||
async (inviteCode: string): Promise<string | null> => {
|
||||
if (!myProfile) {
|
||||
toast.error('Could not find your profile to redeem the invite.')
|
||||
return null
|
||||
}
|
||||
|
||||
const invitingProfileId = await inviteApi.redeemInvite(inviteCode, myProfile.id)
|
||||
localStorage.removeItem('inviteCode') // Clear invite code after redeeming
|
||||
return invitingProfileId
|
||||
},
|
||||
[inviteApi, myProfile],
|
||||
)
|
||||
|
||||
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
|
||||
invitingProfileId = await redeemInvite(inviteCode)
|
||||
}
|
||||
if (invitingProfileId) {
|
||||
navigate(`/item/${invitingProfileId}`)
|
||||
} else {
|
||||
navigate('/')
|
||||
}
|
||||
}, [inviteApi, navigate])
|
||||
}, [navigate, redeemInvite])
|
||||
|
||||
const onLogin = useCallback(async () => {
|
||||
await toast.promise(login({ email, password }), {
|
||||
|
||||
15
lib/src/Components/Map/hooks/useMyProfile.ts
Normal file
15
lib/src/Components/Map/hooks/useMyProfile.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { useAuth } from '#components/Auth/useAuth'
|
||||
|
||||
import { useItems } from './useItems'
|
||||
|
||||
export const useMyProfile = () => {
|
||||
const items = useItems()
|
||||
const user = useAuth().user
|
||||
|
||||
// Find the user's profile item
|
||||
const myProfile = items.find(
|
||||
(item) => item.layer?.userProfileLayer && item.user_created?.id === user?.id,
|
||||
)
|
||||
|
||||
return myProfile
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { useAuth } from '#components/Auth/useAuth'
|
||||
import { useMyProfile } from '#components/Map/hooks/useMyProfile'
|
||||
import { MapOverlayPage } from '#components/Templates/MapOverlayPage'
|
||||
|
||||
import type { InviteApi } from '#types/InviteApi'
|
||||
@ -19,13 +20,21 @@ export function InvitePage({ inviteApi }: Props) {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const myProfile = useMyProfile()
|
||||
|
||||
if (!id) throw new Error('Invite ID is required')
|
||||
|
||||
useEffect(() => {
|
||||
async function redeemInvite() {
|
||||
if (!id) throw new Error('Invite ID is required')
|
||||
|
||||
const invitingProfileId = await inviteApi.redeemInvite(id)
|
||||
if (!myProfile) {
|
||||
toast.error('Could not find your profile to redeem the invite.')
|
||||
return
|
||||
}
|
||||
|
||||
const invitingProfileId = await inviteApi.redeemInvite(id, myProfile.id)
|
||||
|
||||
if (invitingProfileId) {
|
||||
toast.success('Invite redeemed successfully!')
|
||||
navigate(`/item/${invitingProfileId}`)
|
||||
@ -46,7 +55,7 @@ export function InvitePage({ inviteApi }: Props) {
|
||||
// Redirect to login page
|
||||
navigate('/login')
|
||||
}
|
||||
}, [id, isAuthenticated, inviteApi, navigate, isAuthenticationInitialized])
|
||||
}, [id, isAuthenticated, inviteApi, navigate, isAuthenticationInitialized, myProfile])
|
||||
|
||||
return (
|
||||
<MapOverlayPage backdrop className='tw:max-w-xs tw:h-fit'>
|
||||
|
||||
2
lib/src/types/InviteApi.d.ts
vendored
2
lib/src/types/InviteApi.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
export interface InviteApi {
|
||||
validateInvite(inviteId: string): Promise<string | null>
|
||||
redeemInvite(inviteId: string): Promise<string | null>
|
||||
redeemInvite(inviteId: string, itemId: string): Promise<string | null>
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user