This commit is contained in:
j3d1 2026-08-24 15:57:17 +02:00
parent 8d96bc97c4
commit ed04d98bf1
54 changed files with 661 additions and 1214 deletions

View file

@ -46,9 +46,7 @@ export function decodeHandleFromUrl(segment) {
return segment.replace(/\+/g, "#");
}
// Both item-ish kinds land on the same /inventory/:handle/:id shape - only how they resolve
// their owner's handle differs (a personal owner vs. a group, see identityHandleById/
// groupHandleById in store.js), so that resolution is the only part that stays separate.
// item/group_item share this /inventory/:handle/:id shape; only owner-handle resolution (identity vs. group, see store.js) differs.
function itemDetailRoute(handle, item_local_id) {
return handle ? `/inventory/${encodeHandleForUrl(handle)}/${item_local_id}` : null;
}
@ -63,10 +61,7 @@ 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.
// Kinds whose route needs identityHandleById/groupHandleById (from state.idmap); ShortId.vue only waits on an idmap fetch for these.
export const NEEDS_IDMAP = new Set(['item', 'group_item']);
export function expandedRoute({kind, ...fields}) {
@ -93,24 +88,11 @@ const routes = [{path: '/', component: Dashboard, meta: {requiresAuth: true}}, {
meta: {requiresAuth: true},
props: true
}, {
// The self-contained label/short-link entry point (see label.js's LABEL_CONTENT_BUILDERS
// and docs/design-in-progress/items-labels.md) - :handle is already URL-escaped the same
// way /inventory/:handle/:id expects it, so this is just a shorter alias for that route,
// with no owner-is-the-viewer special case: get_shared_item (and friends_or_self()) already
// treat "it's the viewer's own item" as one case of "the viewer may see this owner's item",
// not a separate path.
// Label/short-link entry point; alias of /inventory/:handle/:id. See docs/implementation.md#item-short-link-redirect-route.
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.
// beforeEnter, not redirect: falls through to ShortId.vue when the route can't resolve synchronously. See docs/implementation.md#beforeenter-guard-vs-redirect-for-short_id.
path: '/:short_id',
component: ShortId,
props: true,