feat(frontend/nirax): リダイレクトを設定できるように
This commit is contained in:
parent
67a41c09ae
commit
40e352abcc
|
@ -63,7 +63,7 @@ const history = ref<{ path: string; key: any; }[]>([{
|
||||||
key: windowRouter.getCurrentKey(),
|
key: windowRouter.getCurrentKey(),
|
||||||
}]);
|
}]);
|
||||||
const buttonsLeft = computed(() => {
|
const buttonsLeft = computed(() => {
|
||||||
const buttons = [];
|
const buttons: any[] = [];
|
||||||
|
|
||||||
if (history.value.length > 1) {
|
if (history.value.length > 1) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
|
@ -93,6 +93,13 @@ windowRouter.addListener('push', ctx => {
|
||||||
history.value.push({ path: ctx.path, key: ctx.key });
|
history.value.push({ path: ctx.path, key: ctx.key });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
windowRouter.addListener('replace', ctx => {
|
||||||
|
history.value.pop();
|
||||||
|
history.value.push({ path: ctx.path, key: ctx.key });
|
||||||
|
});
|
||||||
|
|
||||||
|
windowRouter.init();
|
||||||
|
|
||||||
provide('router', windowRouter);
|
provide('router', windowRouter);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
pageMetadata.value = info;
|
pageMetadata.value = info;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { App, AsyncComponentLoader, defineAsyncComponent, provide } from 'vue';
|
import { App, AsyncComponentLoader, defineAsyncComponent, provide } from 'vue';
|
||||||
import { IRouter, Router } from '@/nirax.js';
|
import { IRouter, Router } from '@/nirax.js';
|
||||||
|
import type { RouteDef } from '@/nirax.js';
|
||||||
import { $i, iAmModerator } from '@/account.js';
|
import { $i, iAmModerator } from '@/account.js';
|
||||||
import MkLoading from '@/pages/_loading_.vue';
|
import MkLoading from '@/pages/_loading_.vue';
|
||||||
import MkError from '@/pages/_error_.vue';
|
import MkError from '@/pages/_error_.vue';
|
||||||
|
@ -15,7 +16,7 @@ const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({
|
||||||
loadingComponent: MkLoading,
|
loadingComponent: MkLoading,
|
||||||
errorComponent: MkError,
|
errorComponent: MkError,
|
||||||
});
|
});
|
||||||
const routes = [{
|
const routes: RouteDef[] = [{
|
||||||
path: '/@:initUser/pages/:initPageName/view-source',
|
path: '/@:initUser/pages/:initPageName/view-source',
|
||||||
component: page(() => import('@/pages/page-editor/page-editor.vue')),
|
component: page(() => import('@/pages/page-editor/page-editor.vue')),
|
||||||
}, {
|
}, {
|
||||||
|
@ -332,6 +333,10 @@ const routes = [{
|
||||||
component: page(() => import('@/pages/registry.vue')),
|
component: page(() => import('@/pages/registry.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/install-extentions',
|
path: '/install-extentions',
|
||||||
|
redirect: '/install-extensions',
|
||||||
|
loginRequired: true,
|
||||||
|
}, {
|
||||||
|
path: '/install-extensions',
|
||||||
component: page(() => import('@/pages/install-extentions.vue')),
|
component: page(() => import('@/pages/install-extentions.vue')),
|
||||||
loginRequired: true,
|
loginRequired: true,
|
||||||
}, {
|
}, {
|
||||||
|
@ -561,8 +566,6 @@ export function setupRouter(app: App) {
|
||||||
|
|
||||||
const mainRouter = createRouterImpl(location.pathname + location.search + location.hash);
|
const mainRouter = createRouterImpl(location.pathname + location.search + location.hash);
|
||||||
|
|
||||||
window.history.replaceState({ key: mainRouter.getCurrentKey() }, '', location.href);
|
|
||||||
|
|
||||||
window.addEventListener('popstate', (event) => {
|
window.addEventListener('popstate', (event) => {
|
||||||
mainRouter.replace(location.pathname + location.search + location.hash, event.state?.key);
|
mainRouter.replace(location.pathname + location.search + location.hash, event.state?.key);
|
||||||
});
|
});
|
||||||
|
@ -571,5 +574,11 @@ export function setupRouter(app: App) {
|
||||||
window.history.pushState({ key: ctx.key }, '', ctx.path);
|
window.history.pushState({ key: ctx.key }, '', ctx.path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mainRouter.addListener('replace', ctx => {
|
||||||
|
window.history.replaceState({ key: ctx.key }, '', ctx.path);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainRouter.init();
|
||||||
|
|
||||||
setMainRouter(mainRouter);
|
setMainRouter(mainRouter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,25 @@ import { Component, onMounted, shallowRef, ShallowRef } from 'vue';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
|
import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
|
||||||
|
|
||||||
export type RouteDef = {
|
interface RouteDefBase {
|
||||||
path: string;
|
path: string;
|
||||||
component: Component;
|
|
||||||
query?: Record<string, string>;
|
query?: Record<string, string>;
|
||||||
loginRequired?: boolean;
|
loginRequired?: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
hash?: string;
|
hash?: string;
|
||||||
globalCacheKey?: string;
|
globalCacheKey?: string;
|
||||||
children?: RouteDef[];
|
children?: RouteDef[];
|
||||||
};
|
}
|
||||||
|
|
||||||
|
interface RouteDefWithComponent extends RouteDefBase {
|
||||||
|
component: Component,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RouteDefWithRedirect extends RouteDefBase {
|
||||||
|
redirect: string | ((props: Map<string, string | boolean>) => string);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect;
|
||||||
|
|
||||||
type ParsedPath = (string | {
|
type ParsedPath = (string | {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -48,7 +57,19 @@ export type RouterEvent = {
|
||||||
same: () => void;
|
same: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Resolved = { route: RouteDef; props: Map<string, string | boolean>; child?: Resolved; };
|
export type Resolved = {
|
||||||
|
route: RouteDef;
|
||||||
|
props: Map<string, string | boolean>;
|
||||||
|
child?: Resolved;
|
||||||
|
redirected?: boolean;
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
_parsedRoute: {
|
||||||
|
fullPath: string;
|
||||||
|
queryString: string | null;
|
||||||
|
hash: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function parsePath(path: string): ParsedPath {
|
function parsePath(path: string): ParsedPath {
|
||||||
const res = [] as ParsedPath;
|
const res = [] as ParsedPath;
|
||||||
|
@ -81,6 +102,11 @@ export interface IRouter extends EventEmitter<RouterEvent> {
|
||||||
currentRoute: ShallowRef<RouteDef>;
|
currentRoute: ShallowRef<RouteDef>;
|
||||||
navHook: ((path: string, flag?: any) => boolean) | null;
|
navHook: ((path: string, flag?: any) => boolean) | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ルートの初期化(eventListenerの定義後に必ず呼び出すこと)
|
||||||
|
*/
|
||||||
|
init(): void;
|
||||||
|
|
||||||
resolve(path: string): Resolved | null;
|
resolve(path: string): Resolved | null;
|
||||||
|
|
||||||
getCurrentPath(): any;
|
getCurrentPath(): any;
|
||||||
|
@ -156,12 +182,13 @@ export interface IRouter extends EventEmitter<RouterEvent> {
|
||||||
export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
private routes: RouteDef[];
|
private routes: RouteDef[];
|
||||||
public current: Resolved;
|
public current: Resolved;
|
||||||
public currentRef: ShallowRef<Resolved> = shallowRef();
|
public currentRef: ShallowRef<Resolved>;
|
||||||
public currentRoute: ShallowRef<RouteDef> = shallowRef();
|
public currentRoute: ShallowRef<RouteDef>;
|
||||||
private currentPath: string;
|
private currentPath: string;
|
||||||
private isLoggedIn: boolean;
|
private isLoggedIn: boolean;
|
||||||
private notFoundPageComponent: Component;
|
private notFoundPageComponent: Component;
|
||||||
private currentKey = Date.now().toString();
|
private currentKey = Date.now().toString();
|
||||||
|
private redirectCount = 0;
|
||||||
|
|
||||||
public navHook: ((path: string, flag?: any) => boolean) | null = null;
|
public navHook: ((path: string, flag?: any) => boolean) | null = null;
|
||||||
|
|
||||||
|
@ -169,13 +196,24 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.routes = routes;
|
this.routes = routes;
|
||||||
|
this.current = this.resolve(currentPath)!;
|
||||||
|
this.currentRef = shallowRef(this.current);
|
||||||
|
this.currentRoute = shallowRef(this.current.route);
|
||||||
this.currentPath = currentPath;
|
this.currentPath = currentPath;
|
||||||
this.isLoggedIn = isLoggedIn;
|
this.isLoggedIn = isLoggedIn;
|
||||||
this.notFoundPageComponent = notFoundPageComponent;
|
this.notFoundPageComponent = notFoundPageComponent;
|
||||||
this.navigate(currentPath, null, false);
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
const res = this.navigate(this.currentPath, null, false);
|
||||||
|
this.emit('replace', {
|
||||||
|
path: res._parsedRoute.fullPath,
|
||||||
|
key: this.currentKey,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public resolve(path: string): Resolved | null {
|
public resolve(path: string): Resolved | null {
|
||||||
|
const fullPath = path;
|
||||||
let queryString: string | null = null;
|
let queryString: string | null = null;
|
||||||
let hash: string | null = null;
|
let hash: string | null = null;
|
||||||
if (path[0] === '/') path = path.substring(1);
|
if (path[0] === '/') path = path.substring(1);
|
||||||
|
@ -188,6 +226,12 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
path = path.substring(0, path.indexOf('?'));
|
path = path.substring(0, path.indexOf('?'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _parsedRoute = {
|
||||||
|
fullPath,
|
||||||
|
queryString,
|
||||||
|
hash,
|
||||||
|
};
|
||||||
|
|
||||||
if (_DEV_) console.log('Routing: ', path, queryString);
|
if (_DEV_) console.log('Routing: ', path, queryString);
|
||||||
|
|
||||||
function check(routes: RouteDef[], _parts: string[]): Resolved | null {
|
function check(routes: RouteDef[], _parts: string[]): Resolved | null {
|
||||||
|
@ -238,6 +282,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
route,
|
route,
|
||||||
props,
|
props,
|
||||||
child,
|
child,
|
||||||
|
_parsedRoute,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
continue forEachRouteLoop;
|
continue forEachRouteLoop;
|
||||||
|
@ -263,6 +308,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
return {
|
return {
|
||||||
route,
|
route,
|
||||||
props,
|
props,
|
||||||
|
_parsedRoute,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (route.children) {
|
if (route.children) {
|
||||||
|
@ -272,6 +318,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
route,
|
route,
|
||||||
props,
|
props,
|
||||||
child,
|
child,
|
||||||
|
_parsedRoute,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
continue forEachRouteLoop;
|
continue forEachRouteLoop;
|
||||||
|
@ -290,7 +337,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
return check(this.routes, _parts);
|
return check(this.routes, _parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private navigate(path: string, key: string | null | undefined, emitChange = true) {
|
private navigate(path: string, key: string | null | undefined, emitChange = true, _redirected = false): Resolved {
|
||||||
const beforePath = this.currentPath;
|
const beforePath = this.currentPath;
|
||||||
this.currentPath = path;
|
this.currentPath = path;
|
||||||
|
|
||||||
|
@ -300,6 +347,20 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
throw new Error('no route found for: ' + path);
|
throw new Error('no route found for: ' + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('redirect' in res.route) {
|
||||||
|
let redirectPath: string;
|
||||||
|
if (typeof res.route.redirect === 'function') {
|
||||||
|
redirectPath = res.route.redirect(res.props);
|
||||||
|
} else {
|
||||||
|
redirectPath = res.route.redirect + (res._parsedRoute.queryString ? '?' + res._parsedRoute.queryString : '') + (res._parsedRoute.hash ? '#' + res._parsedRoute.hash : '');
|
||||||
|
}
|
||||||
|
if (_DEV_) console.log('Redirecting to: ', redirectPath);
|
||||||
|
if (_redirected || this.redirectCount++ > 10) {
|
||||||
|
throw new Error('redirect loop detected');
|
||||||
|
}
|
||||||
|
return this.navigate(redirectPath, null, emitChange, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (res.route.loginRequired && !this.isLoggedIn) {
|
if (res.route.loginRequired && !this.isLoggedIn) {
|
||||||
res.route.component = this.notFoundPageComponent;
|
res.route.component = this.notFoundPageComponent;
|
||||||
res.props.set('showLoginPopup', true);
|
res.props.set('showLoginPopup', true);
|
||||||
|
@ -321,7 +382,11 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
this.redirectCount = 0;
|
||||||
|
return {
|
||||||
|
...res,
|
||||||
|
redirected: _redirected,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrentPath() {
|
public getCurrentPath() {
|
||||||
|
@ -354,6 +419,10 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
||||||
|
|
||||||
public replace(path: string, key?: string | null) {
|
public replace(path: string, key?: string | null) {
|
||||||
this.navigate(path, key);
|
this.navigate(path, key);
|
||||||
|
this.emit('replace', {
|
||||||
|
path,
|
||||||
|
key: this.currentKey,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue