diff --git a/src/App.vue b/src/App.vue index 02cabbd2..e0328477 100644 --- a/src/App.vue +++ b/src/App.vue @@ -192,7 +192,7 @@ export default defineComponent({ }, onlyRouterView(): boolean { - return ['nxsetup'].includes(this.$route.name ?? ''); + return this.routeIsNxSetup; }, isFirstStart(): boolean { @@ -224,12 +224,6 @@ export default defineComponent({ }, }, - watch: { - route() { - this.doRouteChecks(); - }, - }, - created() { // No real need to unbind these, as the app is never destroyed const onResize = () => { @@ -249,7 +243,6 @@ export default defineComponent({ }, mounted() { - this.doRouteChecks(); this.refreshNav(); // Store CSS variables modified @@ -409,28 +402,6 @@ export default defineComponent({ } }, - doRouteChecks() { - if (this.$route.name?.endsWith('-share')) { - this.putShareToken(this.$route.params.token); - } - }, - - putShareToken(token: string) { - // Viewer looks for an input with ID sharingToken with the value as the token - // Create this element or update it otherwise files not gonna open - // https://github.com/nextcloud/viewer/blob/a8c46050fb687dcbb48a022a15a5d1275bf54a8e/src/utils/davUtils.js#L61 - let tokenInput = document.getElementById('sharingToken') as HTMLInputElement; - if (!tokenInput) { - tokenInput = document.createElement('input'); - tokenInput.id = 'sharingToken'; - tokenInput.type = 'hidden'; - tokenInput.style.display = 'none'; - document.body.appendChild(tokenInput); - } - - tokenInput.value = token; - }, - showSettings() { this.settingsOpen = true; }, diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 69221e3c..dbc48cde 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -12,7 +12,7 @@ import XLoadingIcon from './components/XLoadingIcon.vue'; import VueVirtualScroller from 'vue-virtual-scroller'; // Locals -import router from './router'; +import router, { routes } from './router'; import { constants, initstate } from './services/utils'; // CSS for components @@ -28,6 +28,7 @@ globalThis._m = { get route() { return router.currentRoute; }, + routes: routes, modals: {} as any, sidebar: {} as any, diff --git a/src/components/ClusterView.vue b/src/components/ClusterView.vue index fe28cffa..940fbb25 100644 --- a/src/components/ClusterView.vue +++ b/src/components/ClusterView.vue @@ -100,8 +100,10 @@ export default defineComponent({ this.items = await dav.getAlbums(this.config.album_list_sort); } else if (this.routeIsTags) { this.items = await dav.getTags(); - } else if (this.routeIsPeople) { - this.items = await dav.getFaceList(this.$route.name); + } else if (this.routeIsRecognize) { + this.items = await dav.getFaceList('recognize'); + } else if (this.routeIsFaceRecognition) { + this.items = await dav.getFaceList('facerecognition'); } else if (this.routeIsPlaces) { this.items = await dav.getPlaces(); } diff --git a/src/components/SelectionManager.vue b/src/components/SelectionManager.vue index 8cc8c626..09e086e0 100644 --- a/src/components/SelectionManager.vue +++ b/src/components/SelectionManager.vue @@ -840,9 +840,7 @@ export default defineComponent({ async removeSelectionFromPerson(selection: Selection) { // Make sure route is valid const { user, name } = this.$route.params; - if (this.$route.name !== 'recognize' || !user || !name) { - return; - } + if (!this.routeIsRecognize || !user || !name) return; // Check photo ownership if (this.$route.params.user !== utils.uid) { diff --git a/src/components/SplitTimeline.vue b/src/components/SplitTimeline.vue index 0435f2ef..79f059b4 100644 --- a/src/components/SplitTimeline.vue +++ b/src/components/SplitTimeline.vue @@ -63,10 +63,10 @@ export default defineComponent({ primary() { switch (this.$route.name) { - case 'map': + case _m.routes.Map.name: return MapSplitMatter; default: - return 'None'; + return null; } }, diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index b552e65a..c90be78c 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -594,8 +594,7 @@ export default defineComponent({ } // Albums - const user = this.$route.params.user; - const name = this.$route.params.name; + const { user, name } = this.$route.params; if (this.routeIsAlbums) { if (!user || !name) { throw new Error('Invalid album route'); @@ -606,7 +605,7 @@ export default defineComponent({ // People if (this.routeIsPeople) { if (!user || !name) { - throw new Error('Invalid album route'); + throw new Error('Invalid face route'); } // name is "recognize" or "facerecognition" @@ -625,7 +624,7 @@ export default defineComponent({ throw new Error('Invalid place route'); } - const id = name.split('-', 1)[0]; + const id = name.split('-', 1)[0]; set(DaysFilterType.PLACE, id); } @@ -690,7 +689,7 @@ export default defineComponent({ const startState = this.state; let data: IDay[] = []; - if (this.$route.name === 'thisday') { + if (this.routeIsThisDay) { data = await dav.getOnThisDayData(); } else if (dav.isSingleItem()) { data = await dav.getSingleItemData(); @@ -793,7 +792,7 @@ export default defineComponent({ }; // Special headers - if (this.$route.name === 'thisday' && (!prevDay || Math.abs(prevDay.dayid - day.dayid) > 30)) { + if (this.routeIsThisDay && (!prevDay || Math.abs(prevDay.dayid - day.dayid) > 30)) { // thisday view with new year title head.size = 67; head.super = utils.getFromNowStr(utils.dayIdToDate(day.dayid)); diff --git a/src/components/modal/AlbumCreateModal.vue b/src/components/modal/AlbumCreateModal.vue index b73da2b4..ac9fa85d 100644 --- a/src/components/modal/AlbumCreateModal.vue +++ b/src/components/modal/AlbumCreateModal.vue @@ -46,7 +46,7 @@ export default defineComponent({ async open(edit: boolean) { if (edit) { try { - this.album = await dav.getAlbum(this.$route.params.user, this.$route.params.name); + this.album = await dav.getAlbum(this.$route.params.user, this.$route.params.name); } catch (e) { console.error(e); showError(this.t('photos', 'Could not load the selected album')); diff --git a/src/components/modal/AlbumShareModal.vue b/src/components/modal/AlbumShareModal.vue index b2b9b17d..d49e1554 100644 --- a/src/components/modal/AlbumShareModal.vue +++ b/src/components/modal/AlbumShareModal.vue @@ -65,8 +65,7 @@ export default defineComponent({ async open() { this.show = true; this.loadingAddCollaborators = true; - const user = this.$route.params.user || ''; - const name = this.$route.params.name || ''; + const { user, name } = this.$route.params; this.album = await dav.getAlbum(user, name); this.loadingAddCollaborators = false; }, diff --git a/src/components/modal/FaceDeleteModal.vue b/src/components/modal/FaceDeleteModal.vue index d7f58f54..76656b70 100644 --- a/src/components/modal/FaceDeleteModal.vue +++ b/src/components/modal/FaceDeleteModal.vue @@ -72,13 +72,13 @@ export default defineComponent({ }, refreshParams() { - this.user = this.$route.params.user || ''; - this.name = this.$route.params.name || ''; + this.user = this.$route.params.user; + this.name = this.$route.params.name; }, async save() { try { - if (this.$route.name === 'recognize') { + if (this.routeIsRecognize) { await dav.recognizeDeleteFace(this.user, this.name); } else { await dav.faceRecognitionSetPersonVisibility(this.name, false); diff --git a/src/components/modal/FaceEditModal.vue b/src/components/modal/FaceEditModal.vue index 4a035b87..e3e347d3 100644 --- a/src/components/modal/FaceEditModal.vue +++ b/src/components/modal/FaceEditModal.vue @@ -103,7 +103,7 @@ export default defineComponent({ if (!this.canSave) return; try { - if (this.$route.name === 'recognize') { + if (this.routeIsRecognize) { await dav.recognizeRenameFace(this.user, this.oldName, this.name); } else { await dav.faceRecognitionRenamePerson(this.oldName, this.name); diff --git a/src/components/modal/FaceList.vue b/src/components/modal/FaceList.vue index fde058ce..3cdc9432 100644 --- a/src/components/modal/FaceList.vue +++ b/src/components/modal/FaceList.vue @@ -85,12 +85,15 @@ export default defineComponent({ methods: { async refreshParams() { - this.user = this.$route.params.user || ''; - this.name = this.$route.params.name || ''; + this.user = this.$route.params.user; + this.name = this.$route.params.name; this.list = null; this.search = ''; - const faces = await dav.getFaceList(this.$route.name as any); + const backend = this.routeIsRecognize ? 'recognize' : this.routeIsFaceRecognition ? 'facerecognition' : null; + console.assert(backend, '[BUG] Invalid route for FaceList'); + + const faces = await dav.getFaceList(backend!); this.list = faces.filter((c: IFace) => c.user_id === this.user && String(c.name || c.cluster_id) !== this.name); this.fuse = new Fuse(this.list, { keys: ['name'] }); diff --git a/src/components/top-matter/AlbumTopMatter.vue b/src/components/top-matter/AlbumTopMatter.vue index e09cd640..f18d346b 100644 --- a/src/components/top-matter/AlbumTopMatter.vue +++ b/src/components/top-matter/AlbumTopMatter.vue @@ -180,9 +180,7 @@ export default defineComponent({ }, async downloadAlbum() { - const res = await axios.post( - API.ALBUM_DOWNLOAD(this.$route.params.user, this.$route.params.name), - ); + const res = await axios.post(API.ALBUM_DOWNLOAD(this.$route.params.user, this.$route.params.name)); if (res.status === 200 && res.data.handle) { downloadWithHandle(res.data.handle); } diff --git a/src/components/top-matter/ClusterTopMatter.vue b/src/components/top-matter/ClusterTopMatter.vue index 29085cbd..e82eba8f 100644 --- a/src/components/top-matter/ClusterTopMatter.vue +++ b/src/components/top-matter/ClusterTopMatter.vue @@ -29,14 +29,14 @@ export default defineComponent({ computed: { viewname(): string { - return strings.viewName(this.$route.name); + return strings.viewName(this.$route.name!); }, name(): string | null { switch (this.$route.name) { - case 'tags': + case _m.routes.Tags.name: return this.t('recognize', this.$route.params.name); - case 'places': + case _m.routes.Places.name: return this.$route.params.name?.split('-').slice(1).join('-'); default: return null; diff --git a/src/components/top-matter/DynamicTopMatter.vue b/src/components/top-matter/DynamicTopMatter.vue index 45e631d6..bdd9428f 100644 --- a/src/components/top-matter/DynamicTopMatter.vue +++ b/src/components/top-matter/DynamicTopMatter.vue @@ -63,7 +63,7 @@ export default defineComponent({ return ''; } - return strings.viewName(this.$route.name); + return strings.viewName(this.$route.name!); }, }, diff --git a/src/components/top-matter/EmptyContent.vue b/src/components/top-matter/EmptyContent.vue index 81e041d8..3f588aa9 100644 --- a/src/components/top-matter/EmptyContent.vue +++ b/src/components/top-matter/EmptyContent.vue @@ -38,7 +38,7 @@ export default defineComponent({ computed: { emptyViewDescription(): string { - return strings.emptyDescription(this.$route.name); + return strings.emptyDescription(this.$route.name!); }, }, }); diff --git a/src/components/top-matter/TopMatter.vue b/src/components/top-matter/TopMatter.vue index da89d7b6..22846835 100644 --- a/src/components/top-matter/TopMatter.vue +++ b/src/components/top-matter/TopMatter.vue @@ -44,15 +44,15 @@ export default defineComponent({ computed: { currentmatter() { switch (this.$route.name) { - case 'folders': + case _m.routes.Folders.name: return FolderTopMatter; - case 'albums': + case _m.routes.Albums.name: return AlbumTopMatter; - case 'tags': - case 'places': + case _m.routes.Tags.name: + case _m.routes.Places.name: return ClusterTopMatter; - case 'recognize': - case 'facerecognition': + case _m.routes.Recognize.name: + case _m.routes.FaceRecognition.name: return FaceTopMatter; default: return null; diff --git a/src/globals.d.ts b/src/globals.d.ts index 277c2a95..cb3055b9 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -8,7 +8,7 @@ import type videojsType from 'video.js'; import type { IPhoto, IRow } from './types'; import type { constants, initstate } from './services/utils'; -import type { GlobalRouteCheckers } from './router'; +import type { GlobalRouteCheckers, routes } from './router'; // Global exposed variables declare global { @@ -32,6 +32,7 @@ declare global { var _m: { mode: 'admin' | 'user'; route: Route; + routes: typeof routes; modals: { editMetadata: (photos: IPhoto[], sections?: number[]) => void; diff --git a/src/router.ts b/src/router.ts index 4cdcea51..bd06c195 100644 --- a/src/router.ts +++ b/src/router.ts @@ -31,7 +31,7 @@ export type RouteId = | 'Explore' | 'NxSetup'; -const routes: { [key in RouteId]: RouteConfig } = { +export const routes: { [key in RouteId]: RouteConfig } = { Base: { path: '/', component: Timeline, @@ -176,15 +176,17 @@ function defineRouteChecker(key: keyof GlobalRouteCheckers, condition: (route?: }); } -// Defined routes +// Build basic route checkers for (const [key, value] of Object.entries(routes)) { defineRouteChecker(`routeIs${key}`, (route) => route?.name === value.name); } // Extra route checkers defineRouteChecker('routeIsPublic', (route) => route?.name?.endsWith('-share') ?? false); -defineRouteChecker('routeIsPeople', (route) => ['recognize', 'facerecognition'].includes(route?.name ?? '')); +defineRouteChecker('routeIsPeople', (route) => + [routes.Recognize.name, routes.FaceRecognition.name].includes(route?.name ?? ''), +); defineRouteChecker( 'routeIsRecognizeUnassigned', - (route) => route?.name === 'recognize' && route.params.name === c.FACE_NULL, + (route) => route?.name === routes.Recognize.name && route!.params.name === c.FACE_NULL, ); diff --git a/src/services/API.ts b/src/services/API.ts index ba90d5c0..121f4e4c 100644 --- a/src/services/API.ts +++ b/src/services/API.ts @@ -7,11 +7,11 @@ const gen = generateUrl; /** Add auth token to this URL */ function tok(url: string) { - const token = _m.route.params.token; + const { token } = _m.route.params; switch (_m.route.name) { - case 'folder-share': + case _m.routes.FolderShare.name: return API.Q(url, { token }); - case 'album-share': + case _m.routes.AlbumShare.name: return API.Q(url, { token, albums: token }); } return url; diff --git a/src/services/dav/base.ts b/src/services/dav/base.ts index e6a8ba99..8d8b7730 100644 --- a/src/services/dav/base.ts +++ b/src/services/dav/base.ts @@ -29,8 +29,8 @@ type GetFilesOpts = { export async function getFiles(photos: IPhoto[], opts?: GetFilesOpts): Promise { // Some routes may have special handling of filenames if (!opts?.ignoreRoute) { - if (_m.route.name === 'albums') { - return getAlbumFileInfos(photos, _m.route.params.user, _m.route.params.name); + if (_m.route.name === _m.routes.Albums.name) { + return getAlbumFileInfos(photos, _m.route.params.user, _m.route.params.name); } } @@ -192,7 +192,7 @@ export async function* deletePhotos(photos: IPhoto[], confirm: boolean = true) { if (photos.length === 0) return; // Extend with Live Photos unless this is an album - const routeIsAlbums = _m.route.name === 'albums'; + const routeIsAlbums = _m.route.name === _m.routes.Albums.name; if (!routeIsAlbums) { photos = await extendWithLivePhotos(photos); } diff --git a/src/services/strings.ts b/src/services/strings.ts index 22f75229..35e1f7c2 100644 --- a/src/services/strings.ts +++ b/src/services/strings.ts @@ -1,59 +1,57 @@ import staticConfig from './static-config'; import { translate as t } from '@nextcloud/l10n'; -type RouteNameType = string | null | undefined; - -export function emptyDescription(routeName: RouteNameType): string { +export function emptyDescription(routeName: string): string { switch (routeName) { - case 'timeline': + case _m.routes.Base.name: return t('memories', 'Upload some photos and make sure the timeline path is configured'); - case 'favorites': + case _m.routes.Favorites.name: return t('memories', 'Mark photos as favorite to find them easily'); - case 'thisday': + case _m.routes.ThisDay.name: return t('memories', 'Memories from past years will appear here'); - case 'facerecognition': + case _m.routes.Recognize.name: + return t('memories', 'Recognize is still working on your photos'); + case _m.routes.FaceRecognition.name: return staticConfig.getSync('facerecognition_enabled') ? t('memories', 'You will find your friends soon. Please be patient') : t('memories', 'Face Recognition is disabled. Enable in settings to find your friends'); - case 'videos': + case _m.routes.Videos.name: return t('memories', 'Your videos will appear here'); - case 'albums': + case _m.routes.Albums.name: return _m.route.params.name ? t('memories', 'Add photos to albums by selecting them on your timeline.') : t('memories', 'Create an album to get started'); - case 'archive': + case _m.routes.Archive.name: return t('memories', "Archive photos you don't want to see in your timeline"); - case 'tags': + case _m.routes.Tags.name: return t('memories', 'Tag photos to find them easily'); - case 'recognize': - return t('memories', 'Recognize is still working on your photos'); - case 'places': + case _m.routes.Places.name: return t('memories', 'Places you have been to will appear here'); default: - return ''; + return String(); } } -export function viewName(routeName: RouteNameType): string { +export function viewName(routeName: string): string { switch (routeName) { - case 'favorites': + case _m.routes.Favorites.name: return t('memories', 'Favorites'); - case 'recognize': - case 'facerecognition': + case _m.routes.Recognize.name: + case _m.routes.FaceRecognition.name: return t('memories', 'People'); - case 'videos': + case _m.routes.Videos.name: return t('memories', 'Videos'); - case 'albums': + case _m.routes.Albums.name: return t('memories', 'Albums'); - case 'archive': + case _m.routes.Archive.name: return t('memories', 'Archive'); - case 'thisday': + case _m.routes.ThisDay.name: return t('memories', 'On this day'); - case 'tags': + case _m.routes.Tags.name: return t('memories', 'Tags'); - case 'places': + case _m.routes.Places.name: return t('memories', 'Places'); default: - return ''; + return String(); } }