Compare commits
3 Commits
main
...
feature/nv
Author | SHA1 | Date |
---|---|---|
Cleo John | bf91e5251c | |
Cleo John | 3dfefc9f16 | |
Cleo John | 9289178624 |
|
@ -9,7 +9,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@khmyznikov/pwa-install": "^0.2.0",
|
"@khmyznikov/pwa-install": "^0.2.0",
|
||||||
"chartjs-chart-matrix": "^2.0.1",
|
"chartjs-chart-matrix": "^2.0.1",
|
||||||
"gsap": "^3.11.4"
|
"gsap": "^3.11.4",
|
||||||
|
"vue-router": "v4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@discordapp/twemoji": "14.0.2",
|
"@discordapp/twemoji": "14.0.2",
|
||||||
|
|
|
@ -29,8 +29,9 @@ import { url } from '@/config';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { mainRouter, routes } from '@/router';
|
import { mainRouter, routes } from '@/router';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
|
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
||||||
import { Router } from '@/nirax';
|
|
||||||
|
import VueRouter from "vue-router"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
initialPath: string;
|
initialPath: string;
|
||||||
|
@ -41,11 +42,11 @@ defineEmits<{
|
||||||
(ev: 'click'): void;
|
(ev: 'click'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const router = new Router(routes, props.initialPath);
|
const router = VueRouter.createRouter({
|
||||||
|
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
|
||||||
router.addListener('push', ctx => {
|
history: VueRouter.createWebHashHistory(),
|
||||||
|
routes, // short for `routes: routes`
|
||||||
});
|
})
|
||||||
|
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||||
let rootEl = $ref();
|
let rootEl = $ref();
|
||||||
|
|
|
@ -32,10 +32,11 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||||
import { url } from '@/config';
|
import { url } from '@/config';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { mainRouter, routes } from '@/router';
|
import { mainRouter, routes } from '@/router';
|
||||||
import { Router } from '@/nirax';
|
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
|
import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
|
||||||
|
|
||||||
|
import VueRouter from "vue-router"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
initialPath: string;
|
initialPath: string;
|
||||||
}>();
|
}>();
|
||||||
|
@ -44,40 +45,15 @@ defineEmits<{
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const router = new Router(routes, props.initialPath);
|
const router = VueRouter.createRouter({
|
||||||
|
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
|
||||||
|
history: VueRouter.createWebHashHistory(),
|
||||||
|
routes, // short for `routes: routes`
|
||||||
|
})
|
||||||
|
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||||
let windowEl = $ref<InstanceType<typeof XWindow>>();
|
let windowEl = $ref<InstanceType<typeof XWindow>>();
|
||||||
const history = $ref<{ path: string; key: any; }[]>([{
|
|
||||||
path: router.getCurrentPath(),
|
|
||||||
key: router.getCurrentKey(),
|
|
||||||
}]);
|
|
||||||
const buttonsLeft = $computed(() => {
|
|
||||||
const buttons = [];
|
|
||||||
|
|
||||||
if (history.length > 1) {
|
|
||||||
buttons.push({
|
|
||||||
icon: 'ph-caret-left-bold ph-lg',
|
|
||||||
onClick: back,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
});
|
|
||||||
const buttonsRight = $computed(() => {
|
|
||||||
const buttons = [{
|
|
||||||
icon: 'ph-arrows-out-simple-bold ph-lg',
|
|
||||||
title: i18n.ts.showInPage,
|
|
||||||
onClick: expand,
|
|
||||||
}];
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.addListener('push', ctx => {
|
|
||||||
history.push({ path: ctx.path, key: ctx.key });
|
|
||||||
});
|
|
||||||
|
|
||||||
provide('router', router);
|
provide('router', router);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
pageMetadata = info;
|
pageMetadata = info;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<KeepAlive :max="defaultStore.state.numberOfPageCache">
|
<KeepAlive :max="defaultStore.state.numberOfPageCache">
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
|
<component/>
|
||||||
|
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
<MkLoading/>
|
<MkLoading/>
|
||||||
|
@ -12,11 +12,12 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch } from 'vue';
|
import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch } from 'vue';
|
||||||
import { Resolved, Router } from '@/nirax';
|
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
|
import VueRouter from "vue-router"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
router?: Router;
|
router?: VueRouter.Router;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const router = props.router ?? inject('router');
|
const router = props.router ?? inject('router');
|
||||||
|
@ -27,35 +28,4 @@ if (router == null) {
|
||||||
|
|
||||||
const currentDepth = inject('routerCurrentDepth', 0);
|
const currentDepth = inject('routerCurrentDepth', 0);
|
||||||
provide('routerCurrentDepth', currentDepth + 1);
|
provide('routerCurrentDepth', currentDepth + 1);
|
||||||
|
|
||||||
function resolveNested(current: Resolved, d = 0): Resolved | null {
|
|
||||||
if (d === currentDepth) {
|
|
||||||
return current;
|
|
||||||
} else {
|
|
||||||
if (current.child) {
|
|
||||||
return resolveNested(current.child, d + 1);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const current = resolveNested(router.current)!;
|
|
||||||
let currentPageComponent = $shallowRef(current.route.component);
|
|
||||||
let currentPageProps = $ref(current.props);
|
|
||||||
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
|
|
||||||
|
|
||||||
function onChange({ resolved, key: newKey }) {
|
|
||||||
const current = resolveNested(resolved);
|
|
||||||
if (current == null) return;
|
|
||||||
currentPageComponent = current.route.component;
|
|
||||||
currentPageProps = current.props;
|
|
||||||
key = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
|
||||||
}
|
|
||||||
|
|
||||||
router.addListener('change', onChange);
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
router.removeListener('change', onChange);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,287 +0,0 @@
|
||||||
// NIRAX --- A lightweight router
|
|
||||||
|
|
||||||
import { EventEmitter } from "eventemitter3";
|
|
||||||
import { Ref, Component, ref, shallowRef, ShallowRef } from "vue";
|
|
||||||
import { pleaseLogin } from "@/scripts/please-login";
|
|
||||||
import { safeURIDecode } from "@/scripts/safe-uri-decode";
|
|
||||||
|
|
||||||
type RouteDef = {
|
|
||||||
path: string;
|
|
||||||
component: Component;
|
|
||||||
query?: Record<string, string>;
|
|
||||||
loginRequired?: boolean;
|
|
||||||
name?: string;
|
|
||||||
hash?: string;
|
|
||||||
globalCacheKey?: string;
|
|
||||||
children?: RouteDef[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type ParsedPath = (
|
|
||||||
| string
|
|
||||||
| {
|
|
||||||
name: string;
|
|
||||||
startsWith?: string;
|
|
||||||
wildcard?: boolean;
|
|
||||||
optional?: boolean;
|
|
||||||
}
|
|
||||||
)[];
|
|
||||||
|
|
||||||
export type Resolved = {
|
|
||||||
route: RouteDef;
|
|
||||||
props: Map<string, string>;
|
|
||||||
child?: Resolved;
|
|
||||||
};
|
|
||||||
|
|
||||||
function parsePath(path: string): ParsedPath {
|
|
||||||
const res = [] as ParsedPath;
|
|
||||||
|
|
||||||
path = path.substring(1);
|
|
||||||
|
|
||||||
for (const part of path.split("/")) {
|
|
||||||
if (part.includes(":")) {
|
|
||||||
const prefix = part.substring(0, part.indexOf(":"));
|
|
||||||
const placeholder = part.substring(part.indexOf(":") + 1);
|
|
||||||
const wildcard = placeholder.includes("(*)");
|
|
||||||
const optional = placeholder.endsWith("?");
|
|
||||||
res.push({
|
|
||||||
name: placeholder.replace("(*)", "").replace("?", ""),
|
|
||||||
startsWith: prefix !== "" ? prefix : undefined,
|
|
||||||
wildcard,
|
|
||||||
optional,
|
|
||||||
});
|
|
||||||
} else if (part.length !== 0) {
|
|
||||||
res.push(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Router extends EventEmitter<{
|
|
||||||
change: (ctx: {
|
|
||||||
beforePath: string;
|
|
||||||
path: string;
|
|
||||||
resolved: Resolved;
|
|
||||||
key: string;
|
|
||||||
}) => void;
|
|
||||||
replace: (ctx: {
|
|
||||||
path: string;
|
|
||||||
key: string;
|
|
||||||
}) => void;
|
|
||||||
push: (ctx: {
|
|
||||||
beforePath: string;
|
|
||||||
path: string;
|
|
||||||
route: RouteDef | null;
|
|
||||||
props: Map<string, string> | null;
|
|
||||||
key: string;
|
|
||||||
}) => void;
|
|
||||||
same: () => void;
|
|
||||||
}> {
|
|
||||||
private routes: RouteDef[];
|
|
||||||
public current: Resolved;
|
|
||||||
public currentRef: ShallowRef<Resolved> = shallowRef();
|
|
||||||
public currentRoute: ShallowRef<RouteDef> = shallowRef();
|
|
||||||
private currentPath: string;
|
|
||||||
private currentKey = Date.now().toString();
|
|
||||||
|
|
||||||
public navHook: ((path: string, flag?: any) => boolean) | null = null;
|
|
||||||
|
|
||||||
constructor(routes: Router["routes"], currentPath: Router["currentPath"]) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.routes = routes;
|
|
||||||
this.currentPath = currentPath;
|
|
||||||
this.navigate(currentPath, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public resolve(path: string): Resolved | null {
|
|
||||||
let queryString: string | null = null;
|
|
||||||
let hash: string | null = null;
|
|
||||||
if (path[0] === "/") path = path.substring(1);
|
|
||||||
if (path.includes("#")) {
|
|
||||||
hash = path.substring(path.indexOf("#") + 1);
|
|
||||||
path = path.substring(0, path.indexOf("#"));
|
|
||||||
}
|
|
||||||
if (path.includes("?")) {
|
|
||||||
queryString = path.substring(path.indexOf("?") + 1);
|
|
||||||
path = path.substring(0, path.indexOf("?"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_DEV_) console.log("Routing: ", path, queryString);
|
|
||||||
|
|
||||||
function check(routes: RouteDef[], _parts: string[]): Resolved | null {
|
|
||||||
forEachRouteLoop: for (const route of routes) {
|
|
||||||
let parts = [..._parts];
|
|
||||||
const props = new Map<string, string>();
|
|
||||||
|
|
||||||
pathMatchLoop: for (const p of parsePath(route.path)) {
|
|
||||||
if (typeof p === "string") {
|
|
||||||
if (p === parts[0]) {
|
|
||||||
parts.shift();
|
|
||||||
} else {
|
|
||||||
continue forEachRouteLoop;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (parts[0] == null && !p.optional) {
|
|
||||||
continue forEachRouteLoop;
|
|
||||||
}
|
|
||||||
if (p.wildcard) {
|
|
||||||
if (parts.length !== 0) {
|
|
||||||
props.set(p.name, safeURIDecode(parts.join("/")));
|
|
||||||
parts = [];
|
|
||||||
}
|
|
||||||
break pathMatchLoop;
|
|
||||||
} else {
|
|
||||||
if (p.startsWith) {
|
|
||||||
if (parts[0] == null || !parts[0].startsWith(p.startsWith))
|
|
||||||
continue forEachRouteLoop;
|
|
||||||
|
|
||||||
props.set(
|
|
||||||
p.name,
|
|
||||||
safeURIDecode(parts[0].substring(p.startsWith.length)),
|
|
||||||
);
|
|
||||||
parts.shift();
|
|
||||||
} else {
|
|
||||||
if (parts[0]) {
|
|
||||||
props.set(p.name, safeURIDecode(parts[0]));
|
|
||||||
}
|
|
||||||
parts.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parts.length === 0) {
|
|
||||||
if (route.children) {
|
|
||||||
const child = check(route.children, []);
|
|
||||||
if (child) {
|
|
||||||
return {
|
|
||||||
route,
|
|
||||||
props,
|
|
||||||
child,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
continue forEachRouteLoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.hash != null && hash != null) {
|
|
||||||
props.set(route.hash, safeURIDecode(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.query != null && queryString != null) {
|
|
||||||
const queryObject = [
|
|
||||||
...new URLSearchParams(queryString).entries(),
|
|
||||||
].reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {});
|
|
||||||
|
|
||||||
for (const q in route.query) {
|
|
||||||
const as = route.query[q];
|
|
||||||
if (queryObject[q]) {
|
|
||||||
props.set(as, safeURIDecode(queryObject[q]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
route,
|
|
||||||
props,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
if (route.children) {
|
|
||||||
const child = check(route.children, parts);
|
|
||||||
if (child) {
|
|
||||||
return {
|
|
||||||
route,
|
|
||||||
props,
|
|
||||||
child,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _parts = path.split("/").filter((part) => part.length !== 0);
|
|
||||||
|
|
||||||
return check(this.routes, _parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private navigate(
|
|
||||||
path: string,
|
|
||||||
key: string | null | undefined,
|
|
||||||
emitChange = true,
|
|
||||||
) {
|
|
||||||
const beforePath = this.currentPath;
|
|
||||||
this.currentPath = path;
|
|
||||||
|
|
||||||
const res = this.resolve(this.currentPath);
|
|
||||||
|
|
||||||
if (res == null) {
|
|
||||||
throw new Error(`no route found for: ${path}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.route.loginRequired) {
|
|
||||||
pleaseLogin("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSamePath = beforePath === path;
|
|
||||||
if (isSamePath && key == null) key = this.currentKey;
|
|
||||||
this.current = res;
|
|
||||||
this.currentRef.value = res;
|
|
||||||
this.currentRoute.value = res.route;
|
|
||||||
this.currentKey = res.route.globalCacheKey ?? key ?? path;
|
|
||||||
|
|
||||||
if (emitChange) {
|
|
||||||
this.emit("change", {
|
|
||||||
beforePath,
|
|
||||||
path,
|
|
||||||
resolved: res,
|
|
||||||
key: this.currentKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCurrentPath() {
|
|
||||||
return this.currentPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCurrentKey() {
|
|
||||||
return this.currentKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public push(path: string, flag?: any) {
|
|
||||||
const beforePath = this.currentPath;
|
|
||||||
if (path === beforePath) {
|
|
||||||
this.emit("same");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.navHook) {
|
|
||||||
const cancel = this.navHook(path, flag);
|
|
||||||
if (cancel) return;
|
|
||||||
}
|
|
||||||
const res = this.navigate(path, null);
|
|
||||||
this.emit("push", {
|
|
||||||
beforePath,
|
|
||||||
path,
|
|
||||||
route: res.route,
|
|
||||||
props: res.props,
|
|
||||||
key: this.currentKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public replace(path: string, key?: string | null, emitEvent = true) {
|
|
||||||
this.navigate(path, key);
|
|
||||||
if (emitEvent) {
|
|
||||||
this.emit("replace", {
|
|
||||||
path,
|
|
||||||
key: this.currentKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,8 @@
|
||||||
import { AsyncComponentLoader, defineAsyncComponent, inject } from "vue";
|
import { AsyncComponentLoader, defineAsyncComponent, inject } from "vue";
|
||||||
import { Router } from "@/nirax";
|
import VueRouter from "vue-router"
|
||||||
import { $i, iAmModerator } from "@/account";
|
import { $i, iAmModerator } from "@/account";
|
||||||
import MkLoading from "@/pages/_loading_.vue";
|
import MkLoading from "@/pages/_loading_.vue";
|
||||||
import MkError from "@/pages/_error_.vue";
|
import MkError from "@/pages/_error_.vue";
|
||||||
import { api } from "@/os";
|
|
||||||
import { ui } from "@/config";
|
|
||||||
|
|
||||||
function getGuestTimelineStatus() {
|
|
||||||
api("meta", {
|
|
||||||
detail: false,
|
|
||||||
}).then((meta) => {
|
|
||||||
return meta.enableGuestTimeline;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const guestTimeline = getGuestTimelineStatus();
|
|
||||||
|
|
||||||
const page = (loader: AsyncComponentLoader<any>) =>
|
const page = (loader: AsyncComponentLoader<any>) =>
|
||||||
defineAsyncComponent({
|
defineAsyncComponent({
|
||||||
|
@ -637,19 +625,13 @@ export const routes = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const mainRouter = new Router(
|
export const mainRouter = VueRouter.createRouter({
|
||||||
routes,
|
routes,
|
||||||
location.pathname + location.search + location.hash,
|
history: VueRouter.createWebHistory(),
|
||||||
);
|
});
|
||||||
|
|
||||||
window.history.replaceState(
|
|
||||||
{ key: mainRouter.getCurrentKey() },
|
|
||||||
"",
|
|
||||||
location.href,
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: このファイルでスクロール位置も管理する設計だとdeckに対応できないのでなんとかする
|
// TODO: このファイルでスクロール位置も管理する設計だとdeckに対応できないのでなんとかする
|
||||||
// スクロール位置取得+スクロール位置設定関数をprovideする感じでも良いかも
|
// スクロール位置取得+スクロール位置設定関数をprovideする感じでも良いかも <- indeed, we figured that out too ~ CLEO
|
||||||
|
|
||||||
const scrollPosStore = new Map<string, number>();
|
const scrollPosStore = new Map<string, number>();
|
||||||
|
|
||||||
|
@ -657,40 +639,6 @@ window.setInterval(() => {
|
||||||
scrollPosStore.set(window.history.state?.key, window.scrollY);
|
scrollPosStore.set(window.history.state?.key, window.scrollY);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
mainRouter.addListener("push", (ctx) => {
|
export function useRouter(): VueRouter.Router {
|
||||||
window.history.pushState({ key: ctx.key }, "", ctx.path);
|
return inject<VueRouter.Router | null>("router", null) ?? mainRouter;
|
||||||
const scrollPos = scrollPosStore.get(ctx.key) ?? 0;
|
|
||||||
window.scroll({ top: scrollPos, behavior: "instant" });
|
|
||||||
if (scrollPos !== 0) {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
// 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール
|
|
||||||
window.scroll({ top: scrollPos, behavior: "instant" });
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mainRouter.addListener("replace", (ctx) => {
|
|
||||||
window.history.replaceState({ key: ctx.key }, "", ctx.path);
|
|
||||||
});
|
|
||||||
|
|
||||||
mainRouter.addListener("same", () => {
|
|
||||||
window.scroll({ top: 0, behavior: "smooth" });
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("popstate", (event) => {
|
|
||||||
mainRouter.replace(
|
|
||||||
location.pathname + location.search + location.hash,
|
|
||||||
event.state?.key,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
const scrollPos = scrollPosStore.get(event.state?.key) ?? 0;
|
|
||||||
window.scroll({ top: scrollPos, behavior: "instant" });
|
|
||||||
window.setTimeout(() => {
|
|
||||||
// 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール
|
|
||||||
window.scroll({ top: scrollPos, behavior: "instant" });
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
export function useRouter(): Router {
|
|
||||||
return inject<Router | null>("router", null) ?? mainRouter;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ import * as os from "@/os";
|
||||||
import { userActions } from "@/store";
|
import { userActions } from "@/store";
|
||||||
import { $i, iAmModerator } from "@/account";
|
import { $i, iAmModerator } from "@/account";
|
||||||
import { mainRouter } from "@/router";
|
import { mainRouter } from "@/router";
|
||||||
import { Router } from "@/nirax";
|
import VueRouter from "vue-router"
|
||||||
|
|
||||||
export function getUserMenu(user, router: Router = mainRouter) {
|
export function getUserMenu(user, router: VueRouter.Router = mainRouter) {
|
||||||
const meId = $i ? $i.id : null;
|
const meId = $i ? $i.id : null;
|
||||||
|
|
||||||
async function pushList() {
|
async function pushList() {
|
||||||
|
|
|
@ -463,11 +463,13 @@ importers:
|
||||||
vue-isyourpasswordsafe: ^2.0.0
|
vue-isyourpasswordsafe: ^2.0.0
|
||||||
vue-plyr: ^7.0.0
|
vue-plyr: ^7.0.0
|
||||||
vue-prism-editor: 2.0.0-alpha.2
|
vue-prism-editor: 2.0.0-alpha.2
|
||||||
|
vue-router: v4
|
||||||
vuedraggable: 4.1.0
|
vuedraggable: 4.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@khmyznikov/pwa-install': 0.2.0
|
'@khmyznikov/pwa-install': 0.2.0
|
||||||
chartjs-chart-matrix: 2.0.1_chart.js@4.1.1
|
chartjs-chart-matrix: 2.0.1_chart.js@4.1.1
|
||||||
gsap: 3.11.4
|
gsap: 3.11.4
|
||||||
|
vue-router: 4.1.6_vue@3.2.45
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@discordapp/twemoji': 14.0.2
|
'@discordapp/twemoji': 14.0.2
|
||||||
'@rollup/plugin-alias': 3.1.9_rollup@3.9.1
|
'@rollup/plugin-alias': 3.1.9_rollup@3.9.1
|
||||||
|
@ -2631,14 +2633,12 @@ packages:
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/compiler-dom/3.2.45:
|
/@vue/compiler-dom/3.2.45:
|
||||||
resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==}
|
resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-core': 3.2.45
|
'@vue/compiler-core': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/compiler-sfc/2.7.14:
|
/@vue/compiler-sfc/2.7.14:
|
||||||
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
|
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
|
||||||
|
@ -2661,14 +2661,16 @@ packages:
|
||||||
magic-string: 0.25.9
|
magic-string: 0.25.9
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/compiler-ssr/3.2.45:
|
/@vue/compiler-ssr/3.2.45:
|
||||||
resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==}
|
resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-dom': 3.2.45
|
'@vue/compiler-dom': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
/@vue/devtools-api/6.5.0:
|
||||||
|
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@vue/reactivity-transform/3.2.45:
|
/@vue/reactivity-transform/3.2.45:
|
||||||
resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==}
|
resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==}
|
||||||
|
@ -2678,20 +2680,17 @@ packages:
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
magic-string: 0.25.9
|
magic-string: 0.25.9
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/reactivity/3.2.45:
|
/@vue/reactivity/3.2.45:
|
||||||
resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==}
|
resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/runtime-core/3.2.45:
|
/@vue/runtime-core/3.2.45:
|
||||||
resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==}
|
resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/reactivity': 3.2.45
|
'@vue/reactivity': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/runtime-dom/3.2.45:
|
/@vue/runtime-dom/3.2.45:
|
||||||
resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==}
|
resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==}
|
||||||
|
@ -2699,7 +2698,6 @@ packages:
|
||||||
'@vue/runtime-core': 3.2.45
|
'@vue/runtime-core': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
csstype: 2.6.21
|
csstype: 2.6.21
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/server-renderer/3.2.45_vue@3.2.45:
|
/@vue/server-renderer/3.2.45_vue@3.2.45:
|
||||||
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
|
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
|
||||||
|
@ -2709,11 +2707,9 @@ packages:
|
||||||
'@vue/compiler-ssr': 3.2.45
|
'@vue/compiler-ssr': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/shared/3.2.45:
|
/@vue/shared/3.2.45:
|
||||||
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
|
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@webassemblyjs/ast/1.11.1:
|
/@webassemblyjs/ast/1.11.1:
|
||||||
resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
|
resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
|
||||||
|
@ -4999,7 +4995,6 @@ packages:
|
||||||
|
|
||||||
/csstype/2.6.21:
|
/csstype/2.6.21:
|
||||||
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/csstype/3.1.1:
|
/csstype/3.1.1:
|
||||||
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
||||||
|
@ -5889,7 +5884,6 @@ packages:
|
||||||
|
|
||||||
/estree-walker/2.0.2:
|
/estree-walker/2.0.2:
|
||||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/esutils/2.0.3:
|
/esutils/2.0.3:
|
||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
|
@ -8805,7 +8799,6 @@ packages:
|
||||||
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec: 1.4.8
|
sourcemap-codec: 1.4.8
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mailcheck/1.1.1:
|
/mailcheck/1.1.1:
|
||||||
resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
|
resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
|
||||||
|
@ -11622,7 +11615,6 @@ packages:
|
||||||
/sourcemap-codec/1.4.8:
|
/sourcemap-codec/1.4.8:
|
||||||
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||||
deprecated: Please use @jridgewell/sourcemap-codec instead
|
deprecated: Please use @jridgewell/sourcemap-codec instead
|
||||||
dev: true
|
|
||||||
|
|
||||||
/sparkles/1.0.1:
|
/sparkles/1.0.1:
|
||||||
resolution: {integrity: sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==}
|
resolution: {integrity: sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==}
|
||||||
|
@ -12952,6 +12944,15 @@ packages:
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vue-router/4.1.6_vue@3.2.45:
|
||||||
|
resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.2.0
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.5.0
|
||||||
|
vue: 3.2.45
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vue/2.7.14:
|
/vue/2.7.14:
|
||||||
resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
|
resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12967,7 +12968,6 @@ packages:
|
||||||
'@vue/runtime-dom': 3.2.45
|
'@vue/runtime-dom': 3.2.45
|
||||||
'@vue/server-renderer': 3.2.45_vue@3.2.45
|
'@vue/server-renderer': 3.2.45_vue@3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/vuedraggable/4.1.0_vue@3.2.45:
|
/vuedraggable/4.1.0_vue@3.2.45:
|
||||||
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
|
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
|
||||||
|
|
Loading…
Reference in New Issue