import { defineStore } from 'pinia' import { type RouteLocationNormalizedLoaded, type RouteRecordRaw } from 'vue-router' import type { GalleryListDefinition, HeaderEntry, Link, ArticleListDefinition, RouteDefinition, SiteGlobals, TemplateType, } from '@goldenwere/mackenzii-types' const markdownBody = () => import ('./views/markdown/markdown.vue') const articleViewBody = () => import ('./views/article/article-view.vue') const galleryViewBody = () => import ('./views/gallery/gallery-view.vue') const mediaListBody = () => import ('./views/shared/media-list.vue') export const templates: Record Promise> = { 'markdown': markdownBody, 'article-list': mediaListBody, 'gallery-list': mediaListBody, } export const createRoutes = (globals: SiteGlobals): RouteRecordRaw[] => { const routeRecord: RouteRecordRaw[] = [] Object.keys(globals.routes).forEach(route => { const toPush: RouteRecordRaw = { name: globals.routes[route].id, path: route, component: templates[globals.routes[route].template], } if (globals.routes[route].template === 'article-list') { routeRecord.push({ name: `${globals.routes[route].id}: View Article`, path: `${route}/view`, component: articleViewBody, props: route => ({ id: route.query.id }), }) } else if (globals.routes[route].template === 'gallery-list') { toPush.props = route => ({ variants: (route.query.v as string || '').split(';') }) routeRecord.push({ name: `${globals.routes[route].id}: View Entry`, path: `${route}/view`, component: galleryViewBody, props: route => ({ variants: (route.query.v as string || '').split(';') }), }) } routeRecord.push(toPush) }) return routeRecord } export const useRouteStore = defineStore('routeStore', { state: () => ({ _header: [] as HeaderEntry[], _routes: {} as Record, _globals: {} as SiteGlobals, _routesAlreadyWarned: {} as Record, _breadcrumbs: [] as Link[], _cachedFiles: {} as { [path: string]: any }, }), actions: { /** * Adds a file to the cache * @param path the path to the file to cache * @param result the parsed file to cache */ cacheFile(path: string, result: T) { this._cachedFiles[path] = result }, /** * Retrieves a file from the cache if it is defined * @param path the path to the file to retrieve * @returns the parsed and cached file or undefined */ getFile(path: string) { return this._cachedFiles[path] as T | undefined }, /** * Determines whether a route has showed a warning or not * @param route the route to check * @returns `true` if previously warned, `false` otherwise */ doesRouteRememberWarning(route: string) { return this._routesAlreadyWarned[route] }, /** * Remembers whether a route showed a warning or not * @param route the route to remember */ rememberRouteWarning(route: string) { this._routesAlreadyWarned[route] = true }, /** * Sets the breadcrumbs array in the store * @param route the current route * @param resolvedTitle a title related to dynamic content loaded from a list-based template when in the view for that list */ setBreadcrumbs(route: RouteLocationNormalizedLoaded, resolvedTitle?: string) { // breadcrumb shouldn't show on home page if (route.path === '/') { this._breadcrumbs = [] return } // split '/my/path' into '', 'my', 'path' const split = route.path.split('/') // adjust for root; '', 'my', 'path' becomes '/', 'my', 'path' split[0] = '/' // compile paths from the split through some array functions const paths = [] as string[] split.forEach((val, i) => { // get the paths leading up to the current index plus the next one const sliced = split.slice(0, i + 1) // reduce converts '/', 'my', 'path' to a breadcrumb trail '/', '/my', '/my/path' // the conditional '/' check prevents accidentally prepending '//' to each breadcrumb paths.push(sliced.reduce((prev, curr) => prev === '/' ? `/${curr}` : `${prev}/${curr}`)) }) // map paths into breadcrumb info this._breadcrumbs = paths.map((path, index) => { return { href: path, // the tail end should be the resolvedTitle from a view route if specified, // otherwise use the title in the route definition caption: index === paths.length - 1 && !!resolvedTitle ? resolvedTitle : this._routes[path]?.title, } }) } }, }) export const initializeRouteStore = (routerRoutes: readonly RouteRecordRaw[], globals: SiteGlobals) => { const routeStore = useRouteStore() Object.keys(globals.routes).forEach(route => { routeStore._routes[route] = { ...routerRoutes.find(other => other.path === route) as RouteRecordRaw, ...globals.routes[route] as RouteDefinition, } if (globals.routes[route].template === 'article-list' || globals.routes[route].template === 'gallery-list') { routeStore._routes[`${route}/view`] = { ...routerRoutes.find(other => other.path === `${route}/view`) as RouteRecordRaw, ...(globals.routes[route] as ArticleListDefinition | GalleryListDefinition).view, } as any } }) routeStore._globals = globals } export type RouteStoreDefinition = Omit< ReturnType, keyof ReturnType >