Resume the last garden on this device (#97)
`/` always dumped you on the gardens list; a phone user who lives in one garden had to open the list and tap in every time. Now the device remembers the garden it was last in and `/` resumes there. - lib/lastGarden.ts: per-device localStorage (pansy:last-garden), same swallow-failures rationale as the seed tray / recents. getLastGardenId guards against a non-positive/garbage stored value. - The `/` route redirects to the stored garden when present, else /gardens. - The editor records the garden on successful load, and — if it 404s (deleted or access revoked) — forgets it (only if it's the stored one, so a bad direct link can't wipe a good resume target) and bounces to the list, so a stale id can't trap the user on an error screen. Transient errors still show the retryable message. - ApiError.isNotFound getter (mirrors isConflict/isUnauthorized). Verified live at 390px: resume into the last garden; a stale id bounces to /gardens and clears itself. tsc + vitest (incl. new lastGarden test) green. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -18,6 +18,7 @@ import { SettingsPage } from '@/pages/SettingsPage'
|
||||
import { meQueryOptions } from '@/lib/auth'
|
||||
import { queryClient } from '@/lib/queryClient'
|
||||
import { safeRedirectPath } from '@/lib/redirect'
|
||||
import { getLastGardenId } from '@/lib/lastGarden'
|
||||
|
||||
interface RouterContext {
|
||||
queryClient: QueryClient
|
||||
@@ -66,7 +67,15 @@ async function requireAdmin(context: RouterContext, path: string) {
|
||||
const indexRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/',
|
||||
// Resume the garden this device was last in, so a returning user lands back in
|
||||
// their plot rather than on the list every time. A stored id that no longer
|
||||
// loads is cleared by the editor and bounces here to /gardens on the next
|
||||
// visit, so this can't trap anyone on a dead garden.
|
||||
beforeLoad: () => {
|
||||
const last = getLastGardenId()
|
||||
if (last != null) {
|
||||
throw redirect({ to: '/gardens/$gardenId', params: { gardenId: String(last) } })
|
||||
}
|
||||
throw redirect({ to: '/gardens' })
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user