import { RouteLocationNormalizedLoaded, RouteParams, RouteRecordName } from 'vue-router' import { useI18n } from '@/i18n' import { parseISO } from 'date-fns' import { ref, shallowReadonly, watch } from 'vue' import { Show, TimeSlot } from '@/types' import { useNavigationContext } from '@/stores/nav' type Route = { name: RouteRecordName params?: RouteParams } type BreadcrumbLabel = { title: string } type BreadcrumbLink = BreadcrumbLabel & ({ route: Route } | { link: string }) export type Breadcrumb = string | BreadcrumbLabel | BreadcrumbLink export type BreadcrumbNormalized = { title: string; as: string; attrs: unknown } type MenuItem = { title: string route: Route testId?: string requiresAdmin?: boolean } export type MenuItemCollection = { title: string items: MenuItem[] } type MenuItemCollectionGeneratorOptions = Record<string, unknown> & { route: RouteLocationNormalizedLoaded } type MenuItemCollectionGenerator = ( options: MenuItemCollectionGeneratorOptions, ) => AsyncGenerator<MenuItemCollection, void, unknown> async function* getMainNavigation() { const { t } = useI18n() yield { title: t('navigation._title'), items: [ { title: t('navigation.shows'), route: { name: 'shows' }, testId: 'nav:shows', }, { title: t('navigation.filesPlaylists'), route: { name: 'files' }, }, { title: t('navigation.calendar'), route: { name: 'calendar' }, testId: 'nav:calendar', }, ], } } async function* getEpisodeContextMenu( options: MenuItemCollectionGeneratorOptions, ): AsyncGenerator<MenuItemCollection, void, unknown> { if (!('episode' in options) || !options.episode) return const episode = options.episode as TimeSlot const { t, locale } = useI18n() const start = parseISO(episode.start).toLocaleString(locale.value, { dateStyle: 'short', timeStyle: 'short', }) yield { title: t('navigation.episode._title', { start }), items: [ { title: t('navigation.episode.details'), route: { name: 'show-episode-details', params: options.route.params }, }, ], } } async function* getShowContextMenu( options: MenuItemCollectionGeneratorOptions, ): AsyncGenerator<MenuItemCollection, void, unknown> { if (!('show' in options) || !options.show) return const show = options.show as Show const { t } = useI18n() yield { title: show.name, items: [ { title: t('navigation.show.episodes'), route: { name: 'show-episodes', params: { ...options.route.params, showId: show.id.toString() }, }, }, { title: t('navigation.show.basicData'), route: { name: 'show-basic-data', params: { ...options.route.params, showId: show.id.toString() }, }, }, ], } } function useNavigationData(...menuCollectionGenerators: MenuItemCollectionGenerator[]) { const menuItemCollections = ref<MenuItemCollection[]>([]) const navigationContext = useNavigationContext() watch( navigationContext, async () => { const result: MenuItemCollection[] = [] for (const generator of menuCollectionGenerators) { try { const collections = generator(navigationContext.value) for await (const collection of collections) { result.push(collection) } } catch (e) { // pass } } menuItemCollections.value = result }, { immediate: true }, ) return shallowReadonly(menuItemCollections) } export function useMainNavigationData() { return useNavigationData(getMainNavigation, getEpisodeContextMenu, getShowContextMenu) }