import { RouteLocationNormalizedLoaded, RouteParams, RouteRecordName, useRoute } from 'vue-router' import { useShowStore } from '@/stores/shows' import { useI18n } from '@/i18n' import { useTimeSlotStore } from '@/stores/timeslots' import { parseISO } from 'date-fns' import { ref, shallowReadonly, watch } from 'vue' import { useSelectedShow } from '@/util' import { Show } from '@/types' 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 = { route: RouteLocationNormalizedLoaded selectedShow: Show | null } 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({ route, }: MenuItemCollectionGeneratorOptions): AsyncGenerator<MenuItemCollection, void, unknown> { if (!route.params.episodeId) return const episodeId = parseInt(route.params.episodeId as string) const timeslotStore = useTimeSlotStore() const timeslot = await timeslotStore.retrieve(episodeId, { useCached: true }) if (!timeslot) return const { t } = useI18n() const start = parseISO(timeslot.start).toLocaleString() yield { title: t('navigation.episode._title', { start }), items: [ { title: t('navigation.episode.description'), route: { name: 'show-episode-description', params: route.params }, }, { title: t('navigation.episode.playlist'), route: { name: 'show-episode-playlist', params: route.params }, }, ], } } async function* getShowContextMenu({ route, selectedShow, }: MenuItemCollectionGeneratorOptions): AsyncGenerator<MenuItemCollection, void, unknown> { if (!route.params.showId && !selectedShow) return const showId = route.params.showId ? parseInt(route.params.showId as string) : (selectedShow?.id as number) const showStore = useShowStore() const show = await showStore.retrieve(showId, { useCached: true }) if (!show) return const { t } = useI18n() yield { title: show.name, items: [ { title: t('navigation.show.episodes'), route: { name: 'show-episodes', params: { ...route.params, showId: showId.toString() } }, }, { title: t('navigation.show.basicData'), route: { name: 'show-basic-data', params: { ...route.params, showId: showId.toString() } }, }, ], } } function useNavigationData(...menuCollectionGenerators: MenuItemCollectionGenerator[]) { const route = useRoute() const selectedShow = useSelectedShow() const menuItemCollections = ref<MenuItemCollection[]>([]) watch( [route, () => route.params, () => route.name, selectedShow], async () => { const result: MenuItemCollection[] = [] for (const generator of menuCollectionGenerators) { try { const collections = generator({ route, selectedShow: selectedShow.value ?? null, }) 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) }