mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-03-01 12:44:17 +00:00
working for new users without profile now
This commit is contained in:
parent
c2695cca60
commit
9bb0309062
@ -2,10 +2,13 @@ import { control } from 'leaflet'
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import SVG from 'react-inlinesvg'
|
import SVG from 'react-inlinesvg'
|
||||||
import { useMap, useMapEvents } from 'react-leaflet'
|
import { useMap, useMapEvents } from 'react-leaflet'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import TargetSVG from '#assets/target.svg'
|
import TargetSVG from '#assets/target.svg'
|
||||||
import { useUpdateItem } from '#components/Map/hooks/useItems'
|
import { useAuth } from '#components/Auth/useAuth'
|
||||||
|
import { useAddItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
||||||
|
import { useLayers } from '#components/Map/hooks/useLayers'
|
||||||
import { useMyProfile } from '#components/Map/hooks/useMyProfile'
|
import { useMyProfile } from '#components/Map/hooks/useMyProfile'
|
||||||
import DialogModal from '#components/Templates/DialogModal'
|
import DialogModal from '#components/Templates/DialogModal'
|
||||||
|
|
||||||
@ -32,6 +35,10 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
const map = useMap()
|
const map = useMap()
|
||||||
const myProfile = useMyProfile()
|
const myProfile = useMyProfile()
|
||||||
const updateItem = useUpdateItem()
|
const updateItem = useUpdateItem()
|
||||||
|
const addItem = useAddItem()
|
||||||
|
const layers = useLayers()
|
||||||
|
const { user } = useAuth()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
// Prevent React 18 StrictMode from calling useEffect twice
|
// Prevent React 18 StrictMode from calling useEffect twice
|
||||||
const init = useRef(false)
|
const init = useRef(false)
|
||||||
@ -43,6 +50,7 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
const [showLocationModal, setShowLocationModal] = useState<boolean>(false)
|
const [showLocationModal, setShowLocationModal] = useState<boolean>(false)
|
||||||
const [foundLocation, setFoundLocation] = useState<LatLng | null>(null)
|
const [foundLocation, setFoundLocation] = useState<LatLng | null>(null)
|
||||||
const [hasUpdatedPosition, setHasUpdatedPosition] = useState<boolean>(false)
|
const [hasUpdatedPosition, setHasUpdatedPosition] = useState<boolean>(false)
|
||||||
|
const [hasDeclinedModal, setHasDeclinedModal] = useState<boolean>(false)
|
||||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
|
||||||
const currentPosition = myProfile.myProfile?.position?.coordinates ?? null
|
const currentPosition = myProfile.myProfile?.position?.coordinates ?? null
|
||||||
@ -50,14 +58,18 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
// Determine if modal should be shown based on distance and conditions
|
// Determine if modal should be shown based on distance and conditions
|
||||||
const shouldShowModal = useCallback(
|
const shouldShowModal = useCallback(
|
||||||
(targetLocation: LatLng | null, hasUpdated: boolean): boolean => {
|
(targetLocation: LatLng | null, hasUpdated: boolean): boolean => {
|
||||||
if (!myProfile.myProfile || !targetLocation || hasUpdated) return false
|
if (!targetLocation || hasUpdated || hasDeclinedModal || !user) return false
|
||||||
|
|
||||||
if (!currentPosition) return true // Show modal if user has no current position
|
// Show modal if user has no profile (new user)
|
||||||
|
if (!myProfile.myProfile) return true
|
||||||
|
|
||||||
|
// Show modal if user has no current position
|
||||||
|
if (!currentPosition) return true
|
||||||
|
|
||||||
const distance = targetLocation.distanceTo([currentPosition[1], currentPosition[0]])
|
const distance = targetLocation.distanceTo([currentPosition[1], currentPosition[0]])
|
||||||
return distance >= 100
|
return distance >= 100
|
||||||
},
|
},
|
||||||
[myProfile.myProfile, currentPosition],
|
[myProfile.myProfile, currentPosition, hasDeclinedModal, user],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -113,6 +125,7 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
lc.stop()
|
lc.stop()
|
||||||
setActive(false)
|
setActive(false)
|
||||||
|
setHasDeclinedModal(false) // Reset declined state when turning off location
|
||||||
if (timeoutRef.current) {
|
if (timeoutRef.current) {
|
||||||
clearTimeout(timeoutRef.current)
|
clearTimeout(timeoutRef.current)
|
||||||
timeoutRef.current = null
|
timeoutRef.current = null
|
||||||
@ -121,69 +134,106 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
lc.start()
|
lc.start()
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
setHasDeclinedModal(false) // Reset declined state when turning on location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemUpdatePosition = useCallback(async () => {
|
const itemUpdatePosition = useCallback(async () => {
|
||||||
if (myProfile.myProfile && foundLocation) {
|
if (!foundLocation || !user) return
|
||||||
let success = false
|
|
||||||
const updatedProfile = {
|
const toastId = toast.loading(
|
||||||
id: myProfile.myProfile.id,
|
myProfile.myProfile ? 'Updating position' : 'Creating profile at location',
|
||||||
position: { type: 'Point', coordinates: [foundLocation.lng, foundLocation.lat] },
|
)
|
||||||
}
|
|
||||||
const toastId = toast.loading('Updating item position')
|
try {
|
||||||
try {
|
let result: Item
|
||||||
if (myProfile.myProfile.layer?.api?.updateItem) {
|
|
||||||
await myProfile.myProfile.layer.api.updateItem(updatedProfile as Item)
|
if (myProfile.myProfile) {
|
||||||
}
|
// Update existing profile
|
||||||
success = true
|
const updatedProfile = {
|
||||||
} catch (error: unknown) {
|
id: myProfile.myProfile.id,
|
||||||
if (error instanceof Error) {
|
|
||||||
toast.update(toastId, {
|
|
||||||
render: error.message,
|
|
||||||
type: 'error',
|
|
||||||
isLoading: false,
|
|
||||||
autoClose: 5000,
|
|
||||||
closeButton: true,
|
|
||||||
})
|
|
||||||
} else if (typeof error === 'string') {
|
|
||||||
toast.update(toastId, {
|
|
||||||
render: error,
|
|
||||||
type: 'error',
|
|
||||||
isLoading: false,
|
|
||||||
autoClose: 5000,
|
|
||||||
closeButton: true,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
updateItem({
|
|
||||||
...myProfile.myProfile,
|
|
||||||
position: { type: 'Point', coordinates: [foundLocation.lng, foundLocation.lat] },
|
position: { type: 'Point', coordinates: [foundLocation.lng, foundLocation.lat] },
|
||||||
})
|
}
|
||||||
|
if (!myProfile.myProfile.layer?.api?.updateItem) {
|
||||||
|
throw new Error('Update API not available')
|
||||||
|
}
|
||||||
|
result = await myProfile.myProfile.layer.api.updateItem(updatedProfile as Item)
|
||||||
|
// Use server response for local state update
|
||||||
|
updateItem({ ...result, layer: myProfile.myProfile.layer })
|
||||||
toast.update(toastId, {
|
toast.update(toastId, {
|
||||||
render: 'Item position updated',
|
render: 'Position updated',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
autoClose: 5000,
|
autoClose: 5000,
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
})
|
})
|
||||||
setFoundLocation(null)
|
} else {
|
||||||
setActive(false)
|
// Create new profile
|
||||||
setHasUpdatedPosition(true)
|
const userLayer = layers.find((l) => l.userProfileLayer === true)
|
||||||
if (timeoutRef.current) {
|
if (!userLayer?.api?.createItem) {
|
||||||
clearTimeout(timeoutRef.current)
|
throw new Error('User profile layer or create API not available')
|
||||||
timeoutRef.current = null
|
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
||||||
if (lc) lc.stop()
|
const newProfile = {
|
||||||
// Reset flag after a delay to allow future updates
|
id: crypto.randomUUID(),
|
||||||
setTimeout(() => setHasUpdatedPosition(false), 5000)
|
name: user.first_name ?? 'User',
|
||||||
|
position: { type: 'Point', coordinates: [foundLocation.lng, foundLocation.lat] },
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await userLayer.api.createItem(newProfile as Item)
|
||||||
|
// Use server response for local state update
|
||||||
|
addItem({
|
||||||
|
...result,
|
||||||
|
user_created: user,
|
||||||
|
layer: userLayer,
|
||||||
|
public_edit: false,
|
||||||
|
})
|
||||||
|
toast.update(toastId, {
|
||||||
|
render: 'Profile created at location',
|
||||||
|
type: 'success',
|
||||||
|
isLoading: false,
|
||||||
|
autoClose: 5000,
|
||||||
|
closeButton: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the profile to show the popup
|
||||||
|
navigate(`/${result.id}`)
|
||||||
|
|
||||||
|
// Clean up and reset state
|
||||||
|
setFoundLocation(null)
|
||||||
|
setActive(false)
|
||||||
|
setHasUpdatedPosition(true)
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current)
|
||||||
|
timeoutRef.current = null
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
|
if (lc) lc.stop()
|
||||||
|
// Reset flag after a delay to allow future updates
|
||||||
|
setTimeout(() => setHasUpdatedPosition(false), 5000)
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
toast.update(toastId, {
|
||||||
|
render: error.message,
|
||||||
|
type: 'error',
|
||||||
|
isLoading: false,
|
||||||
|
autoClose: 5000,
|
||||||
|
closeButton: true,
|
||||||
|
})
|
||||||
|
} else if (typeof error === 'string') {
|
||||||
|
toast.update(toastId, {
|
||||||
|
render: error,
|
||||||
|
type: 'error',
|
||||||
|
isLoading: false,
|
||||||
|
autoClose: 5000,
|
||||||
|
closeButton: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [myProfile.myProfile, foundLocation, updateItem, lc])
|
}, [myProfile.myProfile, foundLocation, updateItem, addItem, layers, user, lc, navigate])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -221,7 +271,11 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
className='tw:bottom-1/3 tw:mx-4 tw:sm:mx-auto'
|
className='tw:bottom-1/3 tw:mx-4 tw:sm:mx-auto'
|
||||||
>
|
>
|
||||||
<div className='tw:text-center'>
|
<div className='tw:text-center'>
|
||||||
<p className='tw:mb-4'>Do you like to place your profile at your current location?</p>
|
<p className='tw:mb-4'>
|
||||||
|
{myProfile.myProfile
|
||||||
|
? 'Do you like to place your profile at your current location?'
|
||||||
|
: 'Do you like to create your profile at your current location?'}
|
||||||
|
</p>
|
||||||
<div className='tw:flex tw:justify-between'>
|
<div className='tw:flex tw:justify-between'>
|
||||||
<label
|
<label
|
||||||
className='tw:btn tw:mt-4 tw:btn-primary'
|
className='tw:btn tw:mt-4 tw:btn-primary'
|
||||||
@ -231,7 +285,13 @@ export const LocateControl = (): JSX.Element => {
|
|||||||
>
|
>
|
||||||
Yes
|
Yes
|
||||||
</label>
|
</label>
|
||||||
<label className='tw:btn tw:mt-4' onClick={() => setShowLocationModal(false)}>
|
<label
|
||||||
|
className='tw:btn tw:mt-4'
|
||||||
|
onClick={() => {
|
||||||
|
setShowLocationModal(false)
|
||||||
|
setHasDeclinedModal(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
No
|
No
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user