diff options
Diffstat (limited to 'apps/files/src/router')
-rw-r--r-- | apps/files/src/router/router.js | 57 | ||||
-rw-r--r-- | apps/files/src/router/router.ts | 145 |
2 files changed, 145 insertions, 57 deletions
diff --git a/apps/files/src/router/router.js b/apps/files/src/router/router.js deleted file mode 100644 index cf5e5ec5ea8..00000000000 --- a/apps/files/src/router/router.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -import Vue from 'vue' -import Router from 'vue-router' -import { generateUrl } from '@nextcloud/router' -import { stringify } from 'query-string' - -Vue.use(Router) - -const router = new Router({ - mode: 'history', - - // if index.php is in the url AND we got this far, then it's working: - // let's keep using index.php in the url - base: generateUrl('/apps/files', ''), - linkActiveClass: 'active', - - routes: [ - { - path: '/', - // Pretending we're using the default view - alias: '/files', - }, - { - path: '/:view/:fileid?', - name: 'filelist', - props: true, - }, - ], - - // Custom stringifyQuery to prevent encoding of slashes in the url - stringifyQuery(query) { - const result = stringify(query).replace(/%2F/gmi, '/') - return result ? ('?' + result) : '' - }, -}) - -export default router diff --git a/apps/files/src/router/router.ts b/apps/files/src/router/router.ts new file mode 100644 index 00000000000..fccb4a0a2b2 --- /dev/null +++ b/apps/files/src/router/router.ts @@ -0,0 +1,145 @@ +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import type { RawLocation, Route } from 'vue-router' + +import { generateUrl } from '@nextcloud/router' +import { relative } from 'path' +import queryString from 'query-string' +import Router, { isNavigationFailure, NavigationFailureType } from 'vue-router' +import Vue from 'vue' + +import { useFilesStore } from '../store/files.ts' +import { usePathsStore } from '../store/paths.ts' +import { defaultView } from '../utils/filesViews.ts' +import logger from '../logger.ts' + +Vue.use(Router) + +// Prevent router from throwing errors when we're already on the page we're trying to go to +const originalPush = Router.prototype.push +Router.prototype.push = (function(this: Router, ...args: Parameters<typeof originalPush>) { + if (args.length > 1) { + return originalPush.call(this, ...args) + } + return originalPush.call<Router, [RawLocation], Promise<Route>>(this, args[0]).catch(ignoreDuplicateNavigation) +}) as typeof originalPush + +const originalReplace = Router.prototype.replace +Router.prototype.replace = (function(this: Router, ...args: Parameters<typeof originalReplace>) { + if (args.length > 1) { + return originalReplace.call(this, ...args) + } + return originalReplace.call<Router, [RawLocation], Promise<Route>>(this, args[0]).catch(ignoreDuplicateNavigation) +}) as typeof originalReplace + +/** + * Ignore duplicated-navigation error but forward real exceptions + * @param error The thrown error + */ +function ignoreDuplicateNavigation(error: unknown): void { + if (isNavigationFailure(error, NavigationFailureType.duplicated)) { + logger.debug('Ignoring duplicated navigation from vue-router', { error }) + } else { + throw error + } +} + +const router = new Router({ + mode: 'history', + + // if index.php is in the url AND we got this far, then it's working: + // let's keep using index.php in the url + base: generateUrl('/apps/files'), + linkActiveClass: 'active', + + routes: [ + { + path: '/', + // Pretending we're using the default view + redirect: { name: 'filelist', params: { view: defaultView() } }, + }, + { + path: '/:view/:fileid(\\d+)?', + name: 'filelist', + props: true, + }, + ], + + // Custom stringifyQuery to prevent encoding of slashes in the url + stringifyQuery(query) { + const result = queryString.stringify(query).replace(/%2F/gmi, '/') + return result ? ('?' + result) : '' + }, +}) + +// Handle aborted navigation (NavigationGuards) gracefully +router.onError((error) => { + if (isNavigationFailure(error, NavigationFailureType.aborted)) { + logger.debug('Navigation was aboorted', { error }) + } else { + throw error + } +}) + +// If navigating back from a folder to a parent folder, +// we need to keep the current dir fileid so it's highlighted +// and scrolled into view. +router.beforeResolve((to, from, next) => { + if (to.params?.parentIntercept) { + delete to.params.parentIntercept + return next() + } + + if (to.params.view !== from.params.view) { + // skip if different views + return next() + } + + const fromDir = (from.query?.dir || '/') as string + const toDir = (to.query?.dir || '/') as string + + // We are going back to a parent directory + if (relative(fromDir, toDir) === '..') { + const { getNode } = useFilesStore() + const { getPath } = usePathsStore() + + if (!from.params.view) { + logger.error('No current view id found, cannot navigate to parent directory', { fromDir, toDir }) + return next() + } + + // Get the previous parent's file id + const fromSource = getPath(from.params.view, fromDir) + if (!fromSource) { + logger.error('No source found for the parent directory', { fromDir, toDir }) + return next() + } + + const fileId = getNode(fromSource)?.fileid + if (!fileId) { + logger.error('No fileid found for the parent directory', { fromDir, toDir, fromSource }) + return next() + } + + logger.debug('Navigating back to parent directory', { fromDir, toDir, fileId }) + return next({ + name: 'filelist', + query: to.query, + params: { + ...to.params, + fileid: String(fileId), + // Prevents the beforeEach from being called again + parentIntercept: 'true', + }, + // Replace the current history entry + replace: true, + }) + } + + // else, we just continue + next() +}) + +export default router |