715 lines
17 KiB
TypeScript
715 lines
17 KiB
TypeScript
import { AsyncComponentLoader, defineAsyncComponent, inject } from "vue";
|
|
import { Router } from "@/nirax";
|
|
import { $i, iAmModerator } from "@/account";
|
|
import MkLoading from "@/pages/_loading_.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>) =>
|
|
defineAsyncComponent({
|
|
loader: loader,
|
|
loadingComponent: MkLoading,
|
|
errorComponent: MkError,
|
|
});
|
|
|
|
export const routes = [
|
|
{
|
|
path: "/@:initUser/pages/:initPageName/view-source",
|
|
component: page(() => import("./pages/page-editor/page-editor.vue")),
|
|
},
|
|
{
|
|
path: "/@:username/pages/:pageName",
|
|
component: page(() => import("./pages/page.vue")),
|
|
},
|
|
{
|
|
path: "/@:acct/following",
|
|
component: page(() => import("./pages/user/following.vue")),
|
|
},
|
|
{
|
|
path: "/@:acct/followers",
|
|
component: page(() => import("./pages/user/followers.vue")),
|
|
},
|
|
{
|
|
name: "user",
|
|
path: "/@:acct/:page?",
|
|
component: page(() => import("./pages/user/index.vue")),
|
|
},
|
|
{
|
|
name: "note",
|
|
path: "/notes/:noteId",
|
|
component: page(() => import("./pages/note.vue")),
|
|
},
|
|
{
|
|
path: "/clips/:clipId",
|
|
component: page(() => import("./pages/clip.vue")),
|
|
},
|
|
{
|
|
path: "/user-info/:userId",
|
|
component: page(() => import("./pages/user-info.vue")),
|
|
},
|
|
{
|
|
path: "/instance-info/:host",
|
|
component: page(() => import("./pages/instance-info.vue")),
|
|
},
|
|
{
|
|
name: "settings",
|
|
path: "/settings",
|
|
component: page(() => import("./pages/settings/index.vue")),
|
|
loginRequired: true,
|
|
children: [
|
|
{
|
|
path: "/profile",
|
|
name: "profile",
|
|
component: page(() => import("./pages/settings/profile.vue")),
|
|
},
|
|
{
|
|
path: "/privacy",
|
|
name: "privacy",
|
|
component: page(() => import("./pages/settings/privacy.vue")),
|
|
},
|
|
{
|
|
path: "/reaction",
|
|
name: "reaction",
|
|
component: page(() => import("./pages/settings/reaction.vue")),
|
|
},
|
|
{
|
|
path: "/drive",
|
|
name: "drive",
|
|
component: page(() => import("./pages/settings/drive.vue")),
|
|
},
|
|
{
|
|
path: "/notifications",
|
|
name: "notifications",
|
|
component: page(() => import("./pages/settings/notifications.vue")),
|
|
},
|
|
{
|
|
path: "/email",
|
|
name: "email",
|
|
component: page(() => import("./pages/settings/email.vue")),
|
|
},
|
|
{
|
|
path: "/integration",
|
|
name: "integration",
|
|
component: page(() => import("./pages/settings/integration.vue")),
|
|
},
|
|
{
|
|
path: "/security",
|
|
name: "security",
|
|
component: page(() => import("./pages/settings/security.vue")),
|
|
},
|
|
{
|
|
path: "/general",
|
|
name: "general",
|
|
component: page(() => import("./pages/settings/general.vue")),
|
|
},
|
|
{
|
|
path: "/theme/install",
|
|
name: "theme",
|
|
component: page(() => import("./pages/settings/theme.install.vue")),
|
|
},
|
|
{
|
|
path: "/theme/manage",
|
|
name: "theme",
|
|
component: page(() => import("./pages/settings/theme.manage.vue")),
|
|
},
|
|
{
|
|
path: "/theme",
|
|
name: "theme",
|
|
component: page(() => import("./pages/settings/theme.vue")),
|
|
},
|
|
{
|
|
path: "/custom-css",
|
|
name: "custom-css",
|
|
component: page(() => import("./pages/settings/custom-css.vue")),
|
|
},
|
|
{
|
|
path: "/custom-katex-macro",
|
|
name: "custom-katex-macro",
|
|
component: page(
|
|
() => import("./pages/settings/custom-katex-macro.vue"),
|
|
),
|
|
},
|
|
{
|
|
path: "/account-info",
|
|
name: "account-info",
|
|
component: page(() => import("./pages/settings/account-info.vue")),
|
|
},
|
|
{
|
|
path: "/navbar",
|
|
name: "navbar",
|
|
component: page(() => import("./pages/settings/navbar.vue")),
|
|
},
|
|
{
|
|
path: "/statusbar",
|
|
name: "statusbar",
|
|
component: page(() => import("./pages/settings/statusbar.vue")),
|
|
},
|
|
{
|
|
path: "/sounds",
|
|
name: "sounds",
|
|
component: page(() => import("./pages/settings/sounds.vue")),
|
|
},
|
|
{
|
|
path: "/plugin/install",
|
|
name: "plugin",
|
|
component: page(() => import("./pages/settings/plugin.install.vue")),
|
|
},
|
|
{
|
|
path: "/plugin",
|
|
name: "plugin",
|
|
component: page(() => import("./pages/settings/plugin.vue")),
|
|
},
|
|
{
|
|
path: "/import-export",
|
|
name: "import-export",
|
|
component: page(() => import("./pages/settings/import-export.vue")),
|
|
},
|
|
{
|
|
path: "/instance-mute",
|
|
name: "instance-mute",
|
|
component: page(() => import("./pages/settings/instance-mute.vue")),
|
|
},
|
|
{
|
|
path: "/mute-block",
|
|
name: "mute-block",
|
|
component: page(() => import("./pages/settings/mute-block.vue")),
|
|
},
|
|
{
|
|
path: "/word-mute",
|
|
name: "word-mute",
|
|
component: page(() => import("./pages/settings/word-mute.vue")),
|
|
},
|
|
{
|
|
path: "/api",
|
|
name: "api",
|
|
component: page(() => import("./pages/settings/api.vue")),
|
|
},
|
|
{
|
|
path: "/webhook/edit/:webhookId",
|
|
name: "webhook",
|
|
component: page(() => import("./pages/settings/webhook.edit.vue")),
|
|
},
|
|
{
|
|
path: "/webhook/new",
|
|
name: "webhook",
|
|
component: page(() => import("./pages/settings/webhook.new.vue")),
|
|
},
|
|
{
|
|
path: "/webhook",
|
|
name: "webhook",
|
|
component: page(() => import("./pages/settings/webhook.vue")),
|
|
},
|
|
{
|
|
path: "/deck",
|
|
name: "deck",
|
|
component: page(() => import("./pages/settings/deck.vue")),
|
|
},
|
|
{
|
|
path: "/delete-account",
|
|
name: "delete-account",
|
|
component: page(() => import("./pages/settings/delete-account.vue")),
|
|
},
|
|
{
|
|
path: "/preferences-backups",
|
|
name: "preferences-backups",
|
|
component: page(
|
|
() => import("./pages/settings/preferences-backups.vue"),
|
|
),
|
|
},
|
|
{
|
|
path: "/migration",
|
|
name: "migration",
|
|
component: page(() => import("./pages/settings/migration.vue")),
|
|
},
|
|
{
|
|
path: "/custom-css",
|
|
name: "general",
|
|
component: page(() => import("./pages/settings/custom-css.vue")),
|
|
},
|
|
{
|
|
path: "/custom-katex-macro",
|
|
name: "general",
|
|
component: page(
|
|
() => import("./pages/settings/custom-katex-macro.vue"),
|
|
),
|
|
},
|
|
{
|
|
path: "/accounts",
|
|
name: "profile",
|
|
component: page(() => import("./pages/settings/accounts.vue")),
|
|
},
|
|
{
|
|
path: "/account-info",
|
|
name: "other",
|
|
component: page(() => import("./pages/settings/account-info.vue")),
|
|
},
|
|
{
|
|
path: "/delete-account",
|
|
name: "other",
|
|
component: page(() => import("./pages/settings/delete-account.vue")),
|
|
},
|
|
{
|
|
path: "/other",
|
|
name: "other",
|
|
component: page(() => import("./pages/settings/other.vue")),
|
|
},
|
|
{
|
|
path: "/",
|
|
component: page(() => import("./pages/_empty_.vue")),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
path: "/reset-password/:token?",
|
|
component: page(() => import("./pages/reset-password.vue")),
|
|
},
|
|
{
|
|
path: "/signup-complete/:code",
|
|
component: page(() => import("./pages/signup-complete.vue")),
|
|
},
|
|
{
|
|
path: "/verify-email/:code",
|
|
component: page(() => import("./pages/verify-email.vue")),
|
|
},
|
|
{
|
|
path: "/announcements",
|
|
component: page(() => import("./pages/announcements.vue")),
|
|
},
|
|
{
|
|
path: "/about",
|
|
component: page(() => import("./pages/about.vue")),
|
|
hash: "initialTab",
|
|
},
|
|
{
|
|
path: "/about-calckey",
|
|
component: page(() => import("./pages/about-calckey.vue")),
|
|
},
|
|
{
|
|
path: "/theme-editor",
|
|
component: page(() => import("./pages/theme-editor.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/explore/tags/:tag",
|
|
component: page(() => import("./pages/explore.vue")),
|
|
},
|
|
{
|
|
path: "/explore",
|
|
component: page(() => import("./pages/explore.vue")),
|
|
},
|
|
{
|
|
path: "/search",
|
|
component: page(() => import("./pages/search.vue")),
|
|
query: {
|
|
q: "query",
|
|
channel: "channel",
|
|
},
|
|
},
|
|
{
|
|
path: "/authorize-follow",
|
|
component: page(() => import("./pages/follow.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/share",
|
|
component: page(() => import("./pages/share.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/api-console",
|
|
component: page(() => import("./pages/api-console.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/mfm-cheat-sheet",
|
|
component: page(() => import("./pages/mfm-cheat-sheet.vue")),
|
|
},
|
|
{
|
|
path: "/scratchpad",
|
|
component: page(() => import("./pages/scratchpad.vue")),
|
|
},
|
|
{
|
|
path: "/preview",
|
|
component: page(() => import("./pages/preview.vue")),
|
|
},
|
|
{
|
|
path: "/auth/:token",
|
|
component: page(() => import("./pages/auth.vue")),
|
|
},
|
|
{
|
|
path: "/miauth/:session",
|
|
component: page(() => import("./pages/miauth.vue")),
|
|
query: {
|
|
callback: "callback",
|
|
name: "name",
|
|
icon: "icon",
|
|
permission: "permission",
|
|
},
|
|
},
|
|
{
|
|
path: "/tags/:tag",
|
|
component: page(() => import("./pages/tag.vue")),
|
|
},
|
|
{
|
|
path: "/pages/new",
|
|
component: page(() => import("./pages/page-editor/page-editor.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/pages/edit/:initPageId",
|
|
component: page(() => import("./pages/page-editor/page-editor.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/pages",
|
|
component: page(() => import("./pages/pages.vue")),
|
|
},
|
|
{
|
|
path: "/gallery/:postId/edit",
|
|
component: page(() => import("./pages/gallery/edit.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/gallery/new",
|
|
component: page(() => import("./pages/gallery/edit.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/gallery/:postId",
|
|
component: page(() => import("./pages/gallery/post.vue")),
|
|
},
|
|
{
|
|
path: "/gallery",
|
|
component: page(() => import("./pages/gallery/index.vue")),
|
|
},
|
|
{
|
|
path: "/channels/:channelId/edit",
|
|
component: page(() => import("./pages/channel-editor.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/channels/new",
|
|
component: page(() => import("./pages/channel-editor.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/channels/:channelId",
|
|
component: page(() => import("./pages/channel.vue")),
|
|
},
|
|
{
|
|
path: "/channels",
|
|
component: page(() => import("./pages/channels.vue")),
|
|
},
|
|
{
|
|
path: "/registry/keys/system/:path(*)?",
|
|
component: page(() => import("./pages/registry.keys.vue")),
|
|
},
|
|
{
|
|
path: "/registry/value/system/:path(*)?",
|
|
component: page(() => import("./pages/registry.value.vue")),
|
|
},
|
|
{
|
|
path: "/registry",
|
|
component: page(() => import("./pages/registry.vue")),
|
|
},
|
|
{
|
|
path: "/admin/file/:fileId",
|
|
component: iAmModerator
|
|
? page(() => import("./pages/admin-file.vue"))
|
|
: page(() => import("./pages/not-found.vue")),
|
|
},
|
|
{
|
|
path: "/admin",
|
|
component: iAmModerator
|
|
? page(() => import("./pages/admin/index.vue"))
|
|
: page(() => import("./pages/not-found.vue")),
|
|
children: [
|
|
{
|
|
path: "/overview",
|
|
name: "overview",
|
|
component: page(() => import("./pages/admin/overview.vue")),
|
|
},
|
|
{
|
|
path: "/users",
|
|
name: "users",
|
|
component: page(() => import("./pages/admin/users.vue")),
|
|
},
|
|
{
|
|
path: "/hashtags",
|
|
name: "hashtags",
|
|
component: page(() => import("./pages/admin/hashtags.vue")),
|
|
},
|
|
{
|
|
path: "/emojis",
|
|
name: "emojis",
|
|
component: page(() => import("./pages/admin/emojis.vue")),
|
|
},
|
|
{
|
|
path: "/federation",
|
|
name: "federation",
|
|
component: page(() => import("./pages/admin/federation.vue")),
|
|
},
|
|
{
|
|
path: "/queue",
|
|
name: "queue",
|
|
component: page(() => import("./pages/admin/queue.vue")),
|
|
},
|
|
{
|
|
path: "/files",
|
|
name: "files",
|
|
component: page(() => import("./pages/admin/files.vue")),
|
|
},
|
|
{
|
|
path: "/announcements",
|
|
name: "announcements",
|
|
component: page(() => import("./pages/admin/announcements.vue")),
|
|
},
|
|
{
|
|
path: "/ads",
|
|
name: "ads",
|
|
component: page(() => import("./pages/admin/promotions.vue")),
|
|
},
|
|
{
|
|
path: "/database",
|
|
name: "database",
|
|
component: page(() => import("./pages/admin/database.vue")),
|
|
},
|
|
{
|
|
path: "/abuses",
|
|
name: "abuses",
|
|
component: page(() => import("./pages/admin/abuses.vue")),
|
|
},
|
|
{
|
|
path: "/settings",
|
|
name: "settings",
|
|
component: page(() => import("./pages/admin/settings.vue")),
|
|
},
|
|
{
|
|
path: "/email-settings",
|
|
name: "email-settings",
|
|
component: page(() => import("./pages/admin/email-settings.vue")),
|
|
},
|
|
{
|
|
path: "/object-storage",
|
|
name: "object-storage",
|
|
component: page(() => import("./pages/admin/object-storage.vue")),
|
|
},
|
|
{
|
|
path: "/security",
|
|
name: "security",
|
|
component: page(() => import("./pages/admin/security.vue")),
|
|
},
|
|
{
|
|
path: "/relays",
|
|
name: "relays",
|
|
component: page(() => import("./pages/admin/relays.vue")),
|
|
},
|
|
{
|
|
path: "/integrations",
|
|
name: "integrations",
|
|
component: page(() => import("./pages/admin/integrations.vue")),
|
|
},
|
|
{
|
|
path: "/instance-block",
|
|
name: "instance-block",
|
|
component: page(() => import("./pages/admin/instance-block.vue")),
|
|
},
|
|
{
|
|
path: "/proxy-account",
|
|
name: "proxy-account",
|
|
component: page(() => import("./pages/admin/proxy-account.vue")),
|
|
},
|
|
{
|
|
path: "/other-settings",
|
|
name: "other-settings",
|
|
component: page(() => import("./pages/admin/other-settings.vue")),
|
|
},
|
|
{
|
|
path: "/other-settings",
|
|
name: "other-settings",
|
|
component: page(() => import("./pages/admin/custom-css.vue")),
|
|
},
|
|
{
|
|
path: "/experiments",
|
|
name: "experiments",
|
|
component: page(() => import("./pages/admin/experiments.vue")),
|
|
},
|
|
{
|
|
path: "/",
|
|
component: page(() => import("./pages/_empty_.vue")),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
path: "/my/notifications",
|
|
component: page(() => import("./pages/notifications.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/favorites",
|
|
component: page(() => import("./pages/favorites.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
name: "messaging",
|
|
path: "/my/messaging",
|
|
component: page(() => import("./pages/messaging/index.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/messaging/:userAcct",
|
|
component: page(() => import("./pages/messaging/messaging-room.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/messaging/group/:groupId",
|
|
component: page(() => import("./pages/messaging/messaging-room.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/drive/folder/:folder",
|
|
component: page(() => import("./pages/drive.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/drive",
|
|
component: page(() => import("./pages/drive.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/follow-requests",
|
|
component: page(() => import("./pages/follow-requests.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/lists/:listId",
|
|
component: page(() => import("./pages/my-lists/list.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/lists",
|
|
component: page(() => import("./pages/my-lists/index.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/clips",
|
|
component: page(() => import("./pages/my-clips/index.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/groups",
|
|
component: page(() => import("./pages/my-groups/index.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/groups/:groupId",
|
|
component: page(() => import("./pages/my-groups/group.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/antennas/create",
|
|
component: page(() => import("./pages/my-antennas/create.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/antennas/:antennaId",
|
|
component: page(() => import("./pages/my-antennas/edit.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/my/antennas",
|
|
component: page(() => import("./pages/my-antennas/index.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/timeline/list/:listId",
|
|
component: page(() => import("./pages/user-list-timeline.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
path: "/timeline/antenna/:antennaId",
|
|
component: page(() => import("./pages/antenna-timeline.vue")),
|
|
loginRequired: true,
|
|
},
|
|
{
|
|
name: "index",
|
|
path: "/",
|
|
component: $i
|
|
? page(() => import("./pages/timeline.vue"))
|
|
: page(() => import("./pages/welcome.vue")),
|
|
globalCacheKey: "index",
|
|
},
|
|
{
|
|
path: "/:(*)",
|
|
component: page(() => import("./pages/not-found.vue")),
|
|
},
|
|
];
|
|
|
|
export const mainRouter = new Router(
|
|
routes,
|
|
location.pathname + location.search + location.hash,
|
|
);
|
|
|
|
window.history.replaceState(
|
|
{ key: mainRouter.getCurrentKey() },
|
|
"",
|
|
location.href,
|
|
);
|
|
|
|
// TODO: このファイルでスクロール位置も管理する設計だとdeckに対応できないのでなんとかする
|
|
// スクロール位置取得+スクロール位置設定関数をprovideする感じでも良いかも
|
|
|
|
const scrollPosStore = new Map<string, number>();
|
|
|
|
window.setInterval(() => {
|
|
scrollPosStore.set(window.history.state?.key, window.scrollY);
|
|
}, 1000);
|
|
|
|
mainRouter.addListener("push", (ctx) => {
|
|
window.history.pushState({ key: ctx.key }, "", ctx.path);
|
|
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;
|
|
}
|