From 8d96bc97c434bbea55ffc040087c033171ce7f0a Mon Sep 17 00:00:00 2001 From: jedi Date: Mon, 24 Aug 2026 12:36:17 +0200 Subject: [PATCH] stash --- frontend/src/router.js | 30 +++++++++++++++++++++--------- frontend/src/store.js | 13 ++++++++++++- frontend/src/views/ShortId.vue | 30 +++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/frontend/src/router.js b/frontend/src/router.js index b355620..93e45d7 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -63,6 +63,12 @@ const EXPANDED_ROUTE_BUILDERS = { workflow: ({workflow_id}) => `/workflows/${workflow_id}`, }; +// Only these two builders read identityHandleById/groupHandleById (derived from state.idmap) - +// ShortId.vue checks this to decide whether a cold-open fetch of idmap is worth waiting on before +// giving up, so a storage_location/group/workflow/file short id never waits on an unrelated +// network call. +export const NEEDS_IDMAP = new Set(['item', 'group_item']); + export function expandedRoute({kind, ...fields}) { const buildRoute = EXPANDED_ROUTE_BUILDERS[kind]; return buildRoute ? buildRoute(fields) : null; @@ -96,13 +102,24 @@ const routes = [{path: '/', component: Dashboard, meta: {requiresAuth: true}}, { path: '/i/:handle/:id', redirect: to => `/inventory/${to.params.handle}/${to.params.id}` }, { + // A beforeEnter guard, not `redirect`: `redirect` is called synchronously and its return value + // is used as-is (never awaited), and it also *must* resolve to a valid location on every match + // (an unresolvable one throws, see vue-router's handleRedirectRecord) - it can't itself wait on + // fetchIdMap (see NEEDS_IDMAP) for the item/group_item kinds whose owner handle isn't + // resolvable from the token alone. A guard can return `null`/undefined to mean "proceed to the + // component instead", which is exactly what's needed here: when expandedRoute can't resolve yet + // (or ever - an unrecognized kind), stay on this same URL and mount ShortId.vue in place, which + // has full component-lifecycle async support and takes it from there - fetch idmap, retry, + // redirect once resolved, or keep showing the decode view. path: '/:short_id', - redirect: to => { - console.log(to) + component: ShortId, + props: true, + beforeEnter: to => { + console.log(to); const p = deserializeShortId(decodeShortId(to.params.short_id)) console.log(p) - const url = expandedRoute(p) - console.log(url) + const url = expandedRoute(p); + console.log(url); return url; } }, {path: '/inventory/new', component: InventoryNew, meta: {requiresAuth: true}}, { @@ -197,11 +214,6 @@ const routes = [{path: '/', component: Dashboard, meta: {requiresAuth: true}}, { path: '/debug/:short_id', component: ShortId, props: true -}, { - path: '/:short_id', - redirect: to => { - - } }, {path: '/:pathMatch(.*)*', redirect: '/'}] const router = createRouter({ diff --git a/frontend/src/store.js b/frontend/src/store.js index cd61ff3..645ffde 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -75,6 +75,7 @@ export default createStore({ groups: [], groupInvites: [], idmap: {identities: [], groups: []}, + idmapLoaded: false, item_map: {}, home_servers: null, all_friends_servers: null, @@ -115,6 +116,7 @@ export default createStore({ }, setIdMap(state, idmap) { state.idmap = idmap; + state.idmapLoaded = true; }, setHomeServers(state, home_servers) { state.home_servers = home_servers; @@ -334,9 +336,18 @@ export default createStore({ // signature check on the receiving end fail against the real request. (answer) => answer.port === 443 ? answer.target : answer.target + ':' + answer.port)) }, - async getHomeServers({state, dispatch, commit}) { + async getHomeServers({state, dispatch, commit, getters}) { if (state.home_servers) return state.home_servers + // isLoggedIn (store.js's getters) is what lazily hydrates state.user/token/keypair + // from localStorage on first read - a route with no requiresAuth meta (e.g. the + // short-id redirect) never triggers that beforeEach check, so state.user can still be + // null here even for an actually-logged-in visitor. Reading the getter first forces + // that hydration; if it's still false afterwards, the visitor really isn't logged in, + // so fail with a clear error instead of lookupServer crashing on username.split(...). + if (!getters.isLoggedIn) { + throw new Error('Not logged in') + } const promise = dispatch('lookupServer', {username: state.user}).then(servers => new ServerSet(servers, state.unreachable_neighbors)) commit('setHomeServers', promise) return promise diff --git a/frontend/src/views/ShortId.vue b/frontend/src/views/ShortId.vue index a41e95d..af9f4bc 100644 --- a/frontend/src/views/ShortId.vue +++ b/frontend/src/views/ShortId.vue @@ -23,6 +23,7 @@
Expanded
{{ expandedRoute }} + {{ error }} No page exists for kind '{{ deserialized.kind }}'
@@ -61,9 +62,10 @@