parent
3cd2402c4e
commit
a0f40dfcae
|
@ -67,6 +67,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
import type { Route } from 'vue-router';
|
||||||
|
|
||||||
import NcContent from '@nextcloud/vue/dist/Components/NcContent';
|
import NcContent from '@nextcloud/vue/dist/Components/NcContent';
|
||||||
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent';
|
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent';
|
||||||
|
@ -76,6 +77,7 @@ const NcAppNavigationItem = () => import('@nextcloud/vue/dist/Components/NcAppNa
|
||||||
import { generateUrl } from '@nextcloud/router';
|
import { generateUrl } from '@nextcloud/router';
|
||||||
import { translate as t } from '@nextcloud/l10n';
|
import { translate as t } from '@nextcloud/l10n';
|
||||||
|
|
||||||
|
import fragment from './services/fragment';
|
||||||
import * as utils from './services/utils';
|
import * as utils from './services/utils';
|
||||||
import * as nativex from './native';
|
import * as nativex from './native';
|
||||||
import staticConfig from './services/static-config';
|
import staticConfig from './services/static-config';
|
||||||
|
@ -159,6 +161,13 @@ export default defineComponent({
|
||||||
settingsOpen: false,
|
settingsOpen: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
async $route(to: Route, from: Route) {
|
||||||
|
// Global triggers
|
||||||
|
fragment.changeTrigger(to, from);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
native(): boolean {
|
native(): boolean {
|
||||||
return nativex.has();
|
return nativex.has();
|
||||||
|
|
|
@ -108,6 +108,7 @@ import EmptyContent from './top-matter/EmptyContent.vue';
|
||||||
import TopMatter from './top-matter/TopMatter.vue';
|
import TopMatter from './top-matter/TopMatter.vue';
|
||||||
import DynamicTopMatter from './top-matter/DynamicTopMatter.vue';
|
import DynamicTopMatter from './top-matter/DynamicTopMatter.vue';
|
||||||
|
|
||||||
|
import fragment from '../services/fragment';
|
||||||
import * as dav from '../services/dav';
|
import * as dav from '../services/dav';
|
||||||
import * as utils from '../services/utils';
|
import * as utils from '../services/utils';
|
||||||
import * as nativex from '../native';
|
import * as nativex from '../native';
|
||||||
|
@ -265,13 +266,11 @@ export default defineComponent({
|
||||||
await this.softRefreshInternal(true);
|
await this.softRefreshInternal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The viewer might change the route immediately again
|
// Check if viewer is supposed to be open
|
||||||
await this.$nextTick();
|
if (from?.hash !== to.hash && !_m.viewer.isOpen && fragment.viewer) {
|
||||||
|
|
||||||
// Check if hash has changed
|
|
||||||
if (from?.hash !== to.hash && !_m.viewer.isOpen && utils.fragment.viewer.open) {
|
|
||||||
// Open viewer
|
// Open viewer
|
||||||
const { dayid, key } = utils.fragment.viewer;
|
const [dayidStr, key] = fragment.viewer.args;
|
||||||
|
const dayid = parseInt(dayidStr);
|
||||||
if (isNaN(dayid) || !key) return;
|
if (isNaN(dayid) || !key) return;
|
||||||
|
|
||||||
// Get day
|
// Get day
|
||||||
|
@ -295,9 +294,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
_m.viewer.openDynamic(photo, this.list);
|
_m.viewer.openDynamic(photo, this.list);
|
||||||
} else if (!utils.fragment.viewer.open && _m.viewer.isOpen) {
|
|
||||||
// No viewer fragment but viewer is open
|
|
||||||
_m.viewer.close();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,7 @@ import { showError } from '@nextcloud/dialogs';
|
||||||
import axios from '@nextcloud/axios';
|
import axios from '@nextcloud/axios';
|
||||||
|
|
||||||
import { API } from '../../services/API';
|
import { API } from '../../services/API';
|
||||||
|
import fragment from '../../services/fragment';
|
||||||
import * as dav from '../../services/dav';
|
import * as dav from '../../services/dav';
|
||||||
import * as utils from '../../services/utils';
|
import * as utils from '../../services/utils';
|
||||||
import * as nativex from '../../native';
|
import * as nativex from '../../native';
|
||||||
|
@ -283,6 +284,7 @@ export default defineComponent({
|
||||||
utils.bus.on('files:file:created', this.handleFileUpdated);
|
utils.bus.on('files:file:created', this.handleFileUpdated);
|
||||||
utils.bus.on('files:file:updated', this.handleFileUpdated);
|
utils.bus.on('files:file:updated', this.handleFileUpdated);
|
||||||
utils.bus.on('memories:window:resize', this.handleWindowResize);
|
utils.bus.on('memories:window:resize', this.handleWindowResize);
|
||||||
|
utils.bus.on('memories:fragment:pop:viewer', this.close);
|
||||||
|
|
||||||
// The viewer is a singleton
|
// The viewer is a singleton
|
||||||
const self = this;
|
const self = this;
|
||||||
|
@ -306,6 +308,7 @@ export default defineComponent({
|
||||||
utils.bus.off('files:file:created', this.handleFileUpdated);
|
utils.bus.off('files:file:created', this.handleFileUpdated);
|
||||||
utils.bus.off('files:file:updated', this.handleFileUpdated);
|
utils.bus.off('files:file:updated', this.handleFileUpdated);
|
||||||
utils.bus.off('memories:window:resize', this.handleWindowResize);
|
utils.bus.off('memories:window:resize', this.handleWindowResize);
|
||||||
|
utils.bus.off('memories:fragment:pop:viewer', this.close);
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -768,6 +771,7 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Close the viewer */
|
/** Close the viewer */
|
||||||
close() {
|
close() {
|
||||||
|
if (!this.isOpen) return;
|
||||||
this.photoswipe?.close();
|
this.photoswipe?.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -867,15 +871,17 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Set the route hash to the given photo */
|
/** Set the route hash to the given photo */
|
||||||
setFragment(photo: IPhoto | null) {
|
setFragment(photo: IPhoto | null) {
|
||||||
|
// Add or update fragment
|
||||||
if (photo) {
|
if (photo) {
|
||||||
const frag = utils.fragment.viewer;
|
return fragment.push({
|
||||||
frag.dayid = photo.dayid;
|
type: fragment.types.viewer,
|
||||||
frag.key = photo.key!;
|
args: [String(photo.dayid), photo.key!],
|
||||||
return utils.fragment.push(frag);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove fragment if closed
|
||||||
if (!this.isOpen) {
|
if (!this.isOpen) {
|
||||||
return utils.fragment.pop('v');
|
return fragment.pop(fragment.types.viewer);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
import type { IPhoto } from '../../types';
|
import type { Route } from 'vue-router';
|
||||||
|
import * as utils from './utils';
|
||||||
|
|
||||||
/** Viewer Fragment */
|
/** Mapping of route name to key type */
|
||||||
type FragmentTypeViewer = 'v';
|
export enum FragmentType {
|
||||||
|
viewer = 'v',
|
||||||
|
}
|
||||||
|
|
||||||
/** All types of fragmemts */
|
/** Names of fragments */
|
||||||
type FragmentType = FragmentTypeViewer;
|
export type FragmentName = keyof typeof FragmentType;
|
||||||
|
|
||||||
/** Data structure to encode to fragment */
|
/** Data structure to encode to fragment */
|
||||||
type FragmentObj = {
|
export type Fragment = {
|
||||||
type: FragmentType;
|
type: FragmentType;
|
||||||
args: string[];
|
args: string[];
|
||||||
index: number;
|
index?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode fragments from string.
|
* Decode fragments from string.
|
||||||
* @param hash Hash string
|
* @param hash Hash string
|
||||||
*/
|
*/
|
||||||
function decodeFragment(hash: string): FragmentObj[] {
|
function decodeFragment(hash: string): Fragment[] {
|
||||||
return hash
|
return hash
|
||||||
.substring(1) // remove # at start
|
.substring(1) // remove # at start
|
||||||
.split('&') // get all parts
|
.split('&') // get all parts
|
||||||
|
@ -36,7 +39,7 @@ function decodeFragment(hash: string): FragmentObj[] {
|
||||||
* Encode fragments to string.
|
* Encode fragments to string.
|
||||||
* @param fragments Fragments to encode
|
* @param fragments Fragments to encode
|
||||||
*/
|
*/
|
||||||
function encodeFragment(fragments: FragmentObj[]): string {
|
function encodeFragment(fragments: Fragment[]): string {
|
||||||
if (!fragments.length) return '';
|
if (!fragments.length) return '';
|
||||||
return '#' + fragments.map((frag) => [frag.type, ...frag.args].join('/')).join('&');
|
return '#' + fragments.map((frag) => [frag.type, ...frag.args].join('/')).join('&');
|
||||||
}
|
}
|
||||||
|
@ -46,21 +49,23 @@ function encodeFragment(fragments: FragmentObj[]): string {
|
||||||
*/
|
*/
|
||||||
const cache = {
|
const cache = {
|
||||||
hash: String(),
|
hash: String(),
|
||||||
list: [] as FragmentObj[],
|
list: [] as Fragment[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fragment = {
|
export default {
|
||||||
|
types: FragmentType,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get list of all fragments in route.
|
* Get list of all fragments in route.
|
||||||
* @returns List of fragments
|
* @returns List of fragments
|
||||||
*/
|
*/
|
||||||
get list(): FragmentObj[] {
|
get list(): Fragment[] {
|
||||||
if (cache.hash !== _m.route.hash) {
|
if (cache.hash !== _m.route.hash) {
|
||||||
cache.hash = _m.route.hash;
|
cache.hash = _m.route.hash;
|
||||||
cache.list = decodeFragment(cache.hash ?? String());
|
cache.list = decodeFragment(cache.hash ?? String());
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache.list;
|
return [...cache.list];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,11 +80,11 @@ export const fragment = {
|
||||||
* Add fragment to route.
|
* Add fragment to route.
|
||||||
* @param frag Fragment to add to route
|
* @param frag Fragment to add to route
|
||||||
*/
|
*/
|
||||||
push(frag: FragmentObj) {
|
push(frag: Fragment) {
|
||||||
const list = this.list;
|
const list = this.list;
|
||||||
|
|
||||||
// Get the top fragment
|
// Get the top fragment
|
||||||
const top = list[list.length - 1];
|
const top = list.length ? list[list.length - 1] : null;
|
||||||
|
|
||||||
// Check if we are already on this fragment
|
// Check if we are already on this fragment
|
||||||
if (top?.type === frag.type) {
|
if (top?.type === frag.type) {
|
||||||
|
@ -125,7 +130,7 @@ export const fragment = {
|
||||||
if (!frag) return;
|
if (!frag) return;
|
||||||
|
|
||||||
// Go back in history
|
// Go back in history
|
||||||
_m.router.go(-frag.index - 1);
|
_m.router.go(-frag.index! - 1);
|
||||||
|
|
||||||
// Check if the fragment still exists
|
// Check if the fragment still exists
|
||||||
// In that case, replace the route to remove the fragment
|
// In that case, replace the route to remove the fragment
|
||||||
|
@ -134,33 +139,30 @@ export const fragment = {
|
||||||
_m.router.replace({
|
_m.router.replace({
|
||||||
path: _m.route.path,
|
path: _m.route.path,
|
||||||
query: _m.route.query,
|
query: _m.route.query,
|
||||||
hash: encodeFragment(this.list.slice(0, -sfrag.index - 1)),
|
hash: encodeFragment(this.list.slice(0, -sfrag.index! - 1)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get viewer() {
|
get viewer() {
|
||||||
const frag = this.get('v');
|
return this.get(FragmentType.viewer);
|
||||||
const typed = {
|
|
||||||
open: !!frag,
|
|
||||||
type: frag?.type ?? 'v',
|
|
||||||
args: (frag?.args ?? ['0', '']) as [string, string],
|
|
||||||
index: frag?.index ?? -1,
|
|
||||||
|
|
||||||
get dayid() {
|
|
||||||
return parseInt(this.args[0]);
|
|
||||||
},
|
|
||||||
set dayid(dayid: number) {
|
|
||||||
this.args[0] = String(dayid);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get key() {
|
changeTrigger(to: Route, from: Route) {
|
||||||
return this.args[1];
|
const toF = decodeFragment(to.hash);
|
||||||
},
|
const fromF = decodeFragment(from.hash);
|
||||||
set key(key: string) {
|
|
||||||
this.args[1] = key;
|
// Emit events for popped fragments
|
||||||
},
|
fromF
|
||||||
};
|
.filter((frag) => !toF.find((f) => f.type === frag.type))
|
||||||
return typed;
|
.forEach((frag) => {
|
||||||
|
for (const [key, type] of Object.entries(FragmentType)) {
|
||||||
|
const name = key as FragmentName;
|
||||||
|
if (type === frag.type) {
|
||||||
|
utils.bus.emit(`memories:fragment:pop:${name}`, frag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
|
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
|
||||||
import { IConfig, IPhoto } from '../../types';
|
import type { IConfig, IPhoto } from '../../types';
|
||||||
|
import type { FragmentName, Fragment } from '../fragment';
|
||||||
|
|
||||||
export type BusEvent = {
|
export type BusEvent = {
|
||||||
/** Open/close the navigation drawer */
|
/** Open/close the navigation drawer */
|
||||||
|
@ -49,6 +50,9 @@ export type BusEvent = {
|
||||||
|
|
||||||
/** NativeX database was updated */
|
/** NativeX database was updated */
|
||||||
'nativex:db:updated': null;
|
'nativex:db:updated': null;
|
||||||
|
} & {
|
||||||
|
/** A fragment was removed from the route */
|
||||||
|
[key in `memories:fragment:pop:${FragmentName}`]: Fragment;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,4 +5,3 @@ export * from './date';
|
||||||
export * from './helpers';
|
export * from './helpers';
|
||||||
export * from './dialog';
|
export * from './dialog';
|
||||||
export * from './event-bus';
|
export * from './event-bus';
|
||||||
export * from './fragment';
|
|
||||||
|
|
Loading…
Reference in New Issue