Refine location handling with Zustand store
This commit is contained in:
+17
-56
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Layers, Loader2, Menu, PanelRightClose, PanelRightOpen } from 'lucide-react'
|
||||
import { Layers, Menu, PanelRightClose, PanelRightOpen } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { AppHeader } from '@/components/layout/AppHeader'
|
||||
@@ -26,34 +26,10 @@ import { cn, distanceInKm, formatCoordinate, formatRelativeTime, formatTimestamp
|
||||
import type { Point } from '@/types/api'
|
||||
import { Toaster } from '@/components/ui/toaster'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { useAppStore } from '@/store/useAppStore'
|
||||
|
||||
const RADIUS_KM = 1
|
||||
|
||||
interface LocationGateProps {
|
||||
title: string
|
||||
message: string
|
||||
actionLabel: string
|
||||
onRetry: () => void
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
function LocationGate({ title, message, actionLabel, onRetry, isLoading }: LocationGateProps) {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-background px-6 text-center">
|
||||
<div className="max-w-md space-y-6">
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-2xl font-semibold tracking-tight text-foreground">{title}</h1>
|
||||
<p className="text-sm text-muted-foreground">{message}</p>
|
||||
</div>
|
||||
<Button onClick={onRetry} disabled={isLoading} className="inline-flex items-center gap-2">
|
||||
{isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
|
||||
<span>{actionLabel}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [pendingSpot, setPendingSpot] = useState<Point | null>(null)
|
||||
const [isConfirmOpen, setIsConfirmOpen] = useState(false)
|
||||
@@ -91,12 +67,10 @@ export default function App() {
|
||||
[t],
|
||||
)
|
||||
|
||||
const {
|
||||
location: userLocation,
|
||||
error: locationError,
|
||||
isRequesting: isRequestingLocation,
|
||||
refresh: refreshLocation,
|
||||
} = useUserLocation()
|
||||
const userLocation = useAppStore((state) => state.userLocation)
|
||||
const locationError = useAppStore((state) => state.locationError)
|
||||
const isRequestingLocation = useAppStore((state) => state.isRequestingLocation)
|
||||
const { refresh: refreshLocation } = useUserLocation()
|
||||
|
||||
const {
|
||||
status,
|
||||
@@ -207,8 +181,10 @@ export default function App() {
|
||||
const handleLocateUser = useCallback(() => {
|
||||
if (userLocation) {
|
||||
focusOn(userLocation, 14)
|
||||
} else {
|
||||
refreshLocation()
|
||||
}
|
||||
}, [focusOn, userLocation])
|
||||
}, [focusOn, refreshLocation, userLocation])
|
||||
|
||||
const handleFocusMySignal = useCallback(() => {
|
||||
if (myVisibleSignal) {
|
||||
@@ -234,20 +210,21 @@ export default function App() {
|
||||
lat: formatCoordinate(cell.lat, locale),
|
||||
lng: formatCoordinate(cell.lng, locale),
|
||||
})
|
||||
const distanceLabel = userLocation
|
||||
? `${distanceFormatter.format(distanceInKm(userLocation, cell))} km`
|
||||
: null
|
||||
return {
|
||||
id: `${cell.lat}-${cell.lng}-${index}`,
|
||||
title: t('hotspots.itemTitle', { index: index + 1 }),
|
||||
subtitle: hasLocation
|
||||
? t('hotspots.itemSubtitleWithDistance', {
|
||||
distance: `${distanceFormatter.format(distanceInKm(userLocation!, cell))} km`,
|
||||
coordinates,
|
||||
})
|
||||
: t('hotspots.itemSubtitle', { coordinates }),
|
||||
subtitle:
|
||||
distanceLabel !== null
|
||||
? t('hotspots.itemSubtitleWithDistance', { distance: distanceLabel, coordinates })
|
||||
: t('hotspots.itemSubtitle', { coordinates }),
|
||||
intensity: cell.intensity,
|
||||
onFocus: () => focusOn({ lat: cell.lat, lng: cell.lng }, 15),
|
||||
}
|
||||
}),
|
||||
[visibleDensity, hasLocation, userLocation, focusOn, distanceFormatter, t, locale],
|
||||
[visibleDensity, userLocation, focusOn, distanceFormatter, t, locale],
|
||||
)
|
||||
|
||||
const recentActivity = useMemo(
|
||||
@@ -288,22 +265,6 @@ export default function App() {
|
||||
: 'translate-y-[calc(100%+1rem)] sm:translate-x-[calc(100%+2rem)]',
|
||||
)
|
||||
|
||||
if (!userLocation) {
|
||||
const gateTitle = t('location.gate.title')
|
||||
const gateMessage = locationError ? t(locationError) : t('location.gate.description')
|
||||
const gateAction = isRequestingLocation ? t('location.gate.loading') : t('location.gate.action')
|
||||
|
||||
return (
|
||||
<LocationGate
|
||||
title={gateTitle}
|
||||
message={gateMessage}
|
||||
actionLabel={gateAction}
|
||||
onRetry={refreshLocation}
|
||||
isLoading={isRequestingLocation}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative min-h-screen w-full overflow-hidden bg-background text-foreground">
|
||||
|
||||
Reference in New Issue
Block a user