From a93621d66296445fc8084a70828306ed3ad0e70f Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Sun, 6 Nov 2022 17:08:46 -0800 Subject: [PATCH] Add hash routes for viewer --- src/components/Timeline.vue | 48 ++++++++++++++++++++++++++++++++----- src/components/Viewer.vue | 41 +++++++++++++++++++++++++------ src/services/Utils.ts | 7 ++++++ 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index c09248cc..0374be51 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -227,12 +227,45 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { mounted() { this.selectionManager = this.$refs.selectionManager; this.scrollerManager = this.$refs.scrollerManager; - this.createState(); + this.routeChange(this.$route); } @Watch("$route") - async routeChange(from: any, to: any) { - await this.refresh(); + async routeChange(to: any, from?: any) { + if (from?.path !== to.path) { + await this.refresh(); + } + + // Check if hash has changed + const viewerIsOpen = (this.$refs.viewer as any).isOpen; + if (from?.hash !== to.hash && to.hash?.startsWith("#v") && !viewerIsOpen) { + // Open viewer + const parts = to.hash.split("/"); + if (parts.length !== 3) return; + + const dayid = parseInt(parts[1]); + const fileid = parseInt(parts[2]); + if (isNaN(dayid) || isNaN(fileid)) return; + + const day = this.heads[dayid]?.day; + if (day && !day.detail) { + const state = this.state; + await this.fetchDay(dayid, true); + if (state !== this.state) return; + } + + const photo = day?.detail?.find((p) => p.fileid === fileid); + if (!photo) return; + + (this.$refs.viewer as any).open(photo, this.list); + } else if ( + from?.hash?.startsWith("#v") && + !to.hash?.startsWith("#v") && + viewerIsOpen + ) { + // Close viewer + (this.$refs.viewer as any).close(); + } } beforeDestroy() { @@ -784,7 +817,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { } /** Fetch image data for one dayId */ - async fetchDay(dayId: number) { + async fetchDay(dayId: number, now = false) { const head = this.heads[dayId]; if (!head) return; @@ -804,7 +837,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { this.fetchDayQueue.push(dayId); // Only single queries allowed for month vie - if (this.isMonthView()) { + if (now || this.isMonthView()) { return this.fetchDayExpire(); } @@ -1146,7 +1179,10 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { // selection mode this.selectionManager.selectPhoto(photo); } else { - (this.$refs.viewer).open(photo, this.list); + this.$router.push({ + ...this.$route, + hash: utils.getViewerHash(photo), + }); } } diff --git a/src/components/Viewer.vue b/src/components/Viewer.vue index 3756063b..32c197dc 100644 --- a/src/components/Viewer.vue +++ b/src/components/Viewer.vue @@ -6,7 +6,7 @@ :style="{ width: outerWidth }" >
-
+
{ + this.isOpen = true; this.fullyOpened = false; - this.opened = true; + this.showControls = true; if (this.sidebarOpen) { this.openSidebar(); } @@ -226,9 +229,11 @@ export default class Viewer extends Mixins(GlobalMixin) { this.fullyOpened = true; }); this.photoswipe.on("close", () => { + this.isOpen = false; this.fullyOpened = false; - this.opened = false; + this.showControls = false; this.hideSidebar(); + this.setRouteHash(undefined); }); this.photoswipe.on("destroy", () => { document.body.classList.remove(klass); @@ -236,8 +241,9 @@ export default class Viewer extends Mixins(GlobalMixin) { // reset everything this.show = false; - this.opened = false; + this.isOpen = false; this.fullyOpened = false; + this.showControls = false; this.photoswipe = null; this.list = []; this.days.clear(); @@ -248,7 +254,12 @@ export default class Viewer extends Mixins(GlobalMixin) { // toggle-controls this.photoswipe.on("tapAction", () => { - this.opened = !this.opened; + this.showControls = !this.showControls; + }); + + // Update vue route for deep linking + this.photoswipe.on("slideActivate", (e) => { + this.setRouteHash(e.slide?.data?.photo); }); // Video support @@ -442,6 +453,11 @@ export default class Viewer extends Mixins(GlobalMixin) { this.photoswipe.init(); } + /** Close the viewer */ + public close() { + this.photoswipe?.close(); + } + /** Open with a static list of photos */ public async openStatic(photo: IPhoto, list: IPhoto[]) { this.list = list; @@ -492,6 +508,17 @@ export default class Viewer extends Mixins(GlobalMixin) { return elem; } + /** Set the route hash to the given photo */ + private setRouteHash(photo: IPhoto | undefined) { + const hash = photo ? utils.getViewerHash(photo) : ""; + if (hash !== this.$route.hash) { + this.$router.replace({ + ...this.$route, + hash, + }); + } + } + /** Delete this photo and refresh */ private async deleteCurrent() { const idx = this.photoswipe.currIndex - this.globalAnchor; @@ -641,7 +668,7 @@ export default class Viewer extends Mixins(GlobalMixin) { transition: opacity 0.2s ease-in-out; opacity: 0; - &.opened { + &.showControls { opacity: 1; } } diff --git a/src/services/Utils.ts b/src/services/Utils.ts index 1d8a9217..8fef2a5a 100644 --- a/src/services/Utils.ts +++ b/src/services/Utils.ts @@ -216,6 +216,13 @@ export function getFolderRoutePath(basePath: string) { return path; } +/** + * Get route hash for viewer for photo + */ +export function getViewerHash(photo: IPhoto) { + return `#v/${photo.dayid}/${photo.fileid}`; +} + /** Set a timer that renews if existing */ export function setRenewingTimeout( ctx: any,