feat(app): auto-start geolocation after login (#756)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Anton Tranelis 2026-03-13 10:24:22 +01:00 committed by GitHub
parent 9a6ec7d7c4
commit 606b0fb649
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 86 additions and 1 deletions

View File

@ -207,6 +207,7 @@ function App() {
embedded={embedded}
openCollectiveApiKey={config.openCollectiveApiKey}
hideSignup={map.hide_signup}
autoLocateOnLogin={map.auto_locate_on_login}
>
<Permissions api={permissionsApiInstance} adminRole={config.adminRole} />
{tagsApi && <Tags api={tagsApi}></Tags>}

View File

@ -0,0 +1,45 @@
{
"collection": "maps",
"field": "auto_locate_on_login",
"type": "boolean",
"meta": {
"collection": "maps",
"conditions": null,
"display": null,
"display_options": null,
"field": "auto_locate_on_login",
"group": "Presets",
"hidden": false,
"interface": "boolean",
"note": "Automatically start geolocation after user login",
"options": null,
"readonly": false,
"required": false,
"sort": 3,
"special": [
"cast-boolean"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "auto_locate_on_login",
"table": "maps",
"data_type": "boolean",
"default_value": false,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -16,6 +16,7 @@ export function AppShell({
embedded,
openCollectiveApiKey,
hideSignup,
autoLocateOnLogin,
}: {
appName: string
children: React.ReactNode
@ -23,6 +24,7 @@ export function AppShell({
embedded?: boolean
openCollectiveApiKey?: string
hideSignup?: boolean
autoLocateOnLogin?: boolean
}) {
return (
<ContextWrapper>
@ -32,6 +34,7 @@ export function AppShell({
embedded={embedded}
openCollectiveApiKey={openCollectiveApiKey}
hideSignup={hideSignup}
autoLocateOnLogin={autoLocateOnLogin}
/>
<NavBar appName={appName}></NavBar>
<div id='app-content' className='tw:flex'>

View File

@ -9,11 +9,13 @@ export const SetAppState = ({
embedded,
openCollectiveApiKey,
hideSignup,
autoLocateOnLogin,
}: {
assetsApi: AssetsApi
embedded?: boolean
openCollectiveApiKey?: string
hideSignup?: boolean
autoLocateOnLogin?: boolean
}) => {
const setAppState = useSetAppState()
@ -33,5 +35,9 @@ export const SetAppState = ({
setAppState({ hideSignup: hideSignup ?? false })
}, [hideSignup, setAppState])
useEffect(() => {
setAppState({ autoLocateOnLogin: autoLocateOnLogin ?? false })
}, [autoLocateOnLogin, setAppState])
return <></>
}

View File

@ -11,6 +11,7 @@ interface AppState {
embedded: boolean
openCollectiveApiKey: string
hideSignup: boolean
autoLocateOnLogin: boolean
}
type UseAppManagerResult = ReturnType<typeof useAppManager>
@ -23,6 +24,7 @@ const initialAppState: AppState = {
embedded: false,
openCollectiveApiKey: '',
hideSignup: false,
autoLocateOnLogin: false,
}
const AppContext = createContext<UseAppManagerResult>({

View File

@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify'
import TargetSVG from '#assets/target.svg'
import { useAppState } from '#components/AppShell/hooks/useAppState'
import { useAuth } from '#components/Auth/useAuth'
import { useAddItem, useUpdateItem } from '#components/Map/hooks/useItems'
import { useLayers } from '#components/Map/hooks/useLayers'
@ -39,11 +40,16 @@ export const LocateControl = (): React.JSX.Element => {
const updateItem = useUpdateItem()
const addItem = useAddItem()
const layers = useLayers()
const { user } = useAuth()
const { user, isInitialized } = useAuth()
const { autoLocateOnLogin } = useAppState()
const navigate = useNavigate()
// Prevent React 18 StrictMode from calling useEffect twice
const init = useRef(false)
// Track whether auto-locate has already fired (one-shot per session)
const hasAutoLocatedRef = useRef(false)
// Snapshot of user after initial auth completes — changes after this are real logins
const initialUserRef = useRef<typeof user>(undefined)
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
const [lc, setLc] = useState<any>(null)
@ -89,6 +95,28 @@ export const LocateControl = (): React.JSX.Element => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// Auto-start location tracking after a real login (not page reload)
useEffect(() => {
if (!isInitialized || !autoLocateOnLogin || !lc) return
// First time isInitialized is true: snapshot the current user as baseline
if (initialUserRef.current === undefined) {
initialUserRef.current = user
return
}
// If user changed from null to a value after the baseline was set, it's a real login
if (!hasAutoLocatedRef.current && user && initialUserRef.current === null) {
hasAutoLocatedRef.current = true
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
lc.start()
setLoading(true)
setHasDeclinedModal(false)
}
initialUserRef.current = user
}, [isInitialized, user, lc, autoLocateOnLogin])
// Check if user logged in while location is active and found
useEffect(() => {
if (