build: switch from yarn to npm to manage js dependencies and move js contents to root
yarn v1 is being deprecated and starts to have some issues Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
39
src/router/actor.ts
Normal file
39
src/router/actor.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { i18n } from "@/utils/i18n";
|
||||
|
||||
const { t } = i18n.global;
|
||||
|
||||
export enum ActorRouteName {
|
||||
GROUP = "Group",
|
||||
CREATE_GROUP = "CreateGroup",
|
||||
PROFILE = "Profile",
|
||||
MY_GROUPS = "MY_GROUPS",
|
||||
}
|
||||
|
||||
export const actorRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/groups/create",
|
||||
name: ActorRouteName.CREATE_GROUP,
|
||||
component: (): Promise<any> => import("@/views/Group/CreateView.vue"),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("Create group") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername",
|
||||
name: ActorRouteName.GROUP,
|
||||
component: (): Promise<any> => import("@/views/Group/GroupView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/groups/me",
|
||||
name: ActorRouteName.MY_GROUPS,
|
||||
component: (): Promise<any> => import("@/views/Group/MyGroups.vue"),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("My groups") as string },
|
||||
},
|
||||
},
|
||||
];
|
||||
33
src/router/conversation.ts
Normal file
33
src/router/conversation.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { i18n } from "@/utils/i18n";
|
||||
|
||||
const t = i18n.global.t;
|
||||
|
||||
export enum ConversationRouteName {
|
||||
CONVERSATION_LIST = "DISCUSSION_LIST",
|
||||
CONVERSATION = "CONVERSATION",
|
||||
}
|
||||
|
||||
export const conversationRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/conversations",
|
||||
name: ConversationRouteName.CONVERSATION_LIST,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Conversations/ConversationListView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("List of conversations") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/conversations/:id/:comment_id?",
|
||||
name: ConversationRouteName.CONVERSATION,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Conversations/ConversationView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
];
|
||||
46
src/router/discussion.ts
Normal file
46
src/router/discussion.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { i18n } from "@/utils/i18n";
|
||||
|
||||
const t = i18n.global.t;
|
||||
|
||||
export enum DiscussionRouteName {
|
||||
DISCUSSION_LIST = "DISCUSSION_LIST",
|
||||
CREATE_DISCUSSION = "CREATE_DISCUSSION",
|
||||
DISCUSSION = "DISCUSSION",
|
||||
}
|
||||
|
||||
export const discussionRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/@:preferredUsername/discussions",
|
||||
name: DiscussionRouteName.DISCUSSION_LIST,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Discussions/DiscussionsListView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Discussions list") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/discussions/new",
|
||||
name: DiscussionRouteName.CREATE_DISCUSSION,
|
||||
component: (): Promise<any> => import("@/views/Discussions/CreateView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Create discussion") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/c/:slug/:comment_id?",
|
||||
name: DiscussionRouteName.DISCUSSION,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Discussions/DiscussionView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
];
|
||||
19
src/router/error.ts
Normal file
19
src/router/error.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { i18n } from "@/utils/i18n";
|
||||
|
||||
const { t } = i18n.global.t;
|
||||
|
||||
export enum ErrorRouteName {
|
||||
ERROR = "Error",
|
||||
}
|
||||
|
||||
export const errorRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/error",
|
||||
name: ErrorRouteName.ERROR,
|
||||
component: (): Promise<any> => import("../views/ErrorView.vue"),
|
||||
meta: {
|
||||
announcer: { message: (): string => t("Error") },
|
||||
},
|
||||
},
|
||||
];
|
||||
137
src/router/event.ts
Normal file
137
src/router/event.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { i18n } from "@/utils/i18n";
|
||||
import { RouteLocationNormalized, RouteRecordRaw } from "vue-router";
|
||||
|
||||
const t = i18n.global.t;
|
||||
|
||||
const participations = () => import("@/views/Event/ParticipantsView.vue");
|
||||
const editEvent = () => import("@/views/Event/EditView.vue");
|
||||
const event = () => import("@/views/Event/EventView.vue");
|
||||
const myEvents = () => import("@/views/Event/MyEventsView.vue");
|
||||
|
||||
export enum EventRouteName {
|
||||
EVENT_LIST = "EventList",
|
||||
CREATE_EVENT = "CreateEvent",
|
||||
MY_EVENTS = "MyEvents",
|
||||
EDIT_EVENT = "EditEvent",
|
||||
DUPLICATE_EVENT = "DuplicateEvent",
|
||||
PARTICIPATIONS = "Participations",
|
||||
EVENT = "Event",
|
||||
EVENT_PARTICIPATE_WITH_ACCOUNT = "EVENT_PARTICIPATE_WITH_ACCOUNT",
|
||||
EVENT_PARTICIPATE_WITHOUT_ACCOUNT = "EVENT_PARTICIPATE_WITHOUT_ACCOUNT",
|
||||
EVENT_PARTICIPATE_LOGGED_OUT = "EVENT_PARTICIPATE_LOGGED_OUT",
|
||||
EVENT_PARTICIPATE_CONFIRM = "EVENT_PARTICIPATE_CONFIRM",
|
||||
TAG = "Tag",
|
||||
}
|
||||
|
||||
export const eventRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/events/create",
|
||||
name: EventRouteName.CREATE_EVENT,
|
||||
component: editEvent,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("Create event") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/events/me",
|
||||
name: EventRouteName.MY_EVENTS,
|
||||
component: myEvents,
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("My events") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/events/edit/:eventId",
|
||||
name: EventRouteName.EDIT_EVENT,
|
||||
component: editEvent,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
props: (route: RouteLocationNormalized): Record<string, unknown> => {
|
||||
return { ...route.params, ...{ isUpdate: true } };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/events/duplicate/:eventId",
|
||||
name: EventRouteName.DUPLICATE_EVENT,
|
||||
component: editEvent,
|
||||
meta: { requiredAuth: true, announce: { skip: true } },
|
||||
props: (route: RouteLocationNormalized): Record<string, unknown> => ({
|
||||
...route.params,
|
||||
...{ isDuplicate: true },
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: "/events/:eventId/participations",
|
||||
name: EventRouteName.PARTICIPATIONS,
|
||||
component: participations,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/events/:uuid",
|
||||
name: EventRouteName.EVENT,
|
||||
component: event,
|
||||
props: true,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/events/:uuid/participate",
|
||||
name: EventRouteName.EVENT_PARTICIPATE_LOGGED_OUT,
|
||||
component: () =>
|
||||
import("../components/Participation/UnloggedParticipation.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
announcer: {
|
||||
message: (): string => t("Unlogged participation") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/events/:uuid/participate/with-account",
|
||||
name: EventRouteName.EVENT_PARTICIPATE_WITH_ACCOUNT,
|
||||
component: () =>
|
||||
import("../components/Participation/ParticipationWithAccount.vue"),
|
||||
meta: {
|
||||
announcer: {
|
||||
message: (): string => t("Participation with account") as string,
|
||||
},
|
||||
},
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/events/:uuid/participate/without-account",
|
||||
name: EventRouteName.EVENT_PARTICIPATE_WITHOUT_ACCOUNT,
|
||||
component: () =>
|
||||
import("../components/Participation/ParticipationWithoutAccount.vue"),
|
||||
meta: {
|
||||
announcer: {
|
||||
message: (): string => t("Participation without account") as string,
|
||||
},
|
||||
},
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/participation/email/confirm/:token",
|
||||
name: EventRouteName.EVENT_PARTICIPATE_CONFIRM,
|
||||
component: () =>
|
||||
import("../components/Participation/ConfirmParticipation.vue"),
|
||||
meta: {
|
||||
announcer: {
|
||||
message: (): string => t("Confirm participation") as string,
|
||||
},
|
||||
},
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/tag/:tag",
|
||||
name: EventRouteName.TAG,
|
||||
component: () => import("@/views/SearchView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Tag search") as string },
|
||||
},
|
||||
},
|
||||
];
|
||||
162
src/router/groups.ts
Normal file
162
src/router/groups.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { RouteLocationNormalized, RouteRecordRaw } from "vue-router";
|
||||
|
||||
export enum GroupsRouteName {
|
||||
TODO_LISTS = "TODO_LISTS",
|
||||
TODO_LIST = "TODO_LIST",
|
||||
TODO = "TODO",
|
||||
GROUP_SETTINGS = "GROUP_SETTINGS",
|
||||
GROUP_PUBLIC_SETTINGS = "GROUP_PUBLIC_SETTINGS",
|
||||
GROUP_MEMBERS_SETTINGS = "GROUP_MEMBERS_SETTINGS",
|
||||
GROUP_FOLLOWERS_SETTINGS = "GROUP_FOLLOWERS_SETTINGS",
|
||||
RESOURCES = "RESOURCES",
|
||||
RESOURCE_FOLDER_ROOT = "RESOURCE_FOLDER_ROOT",
|
||||
RESOURCE_FOLDER = "RESOURCE_FOLDER",
|
||||
POST_CREATE = "POST_CREATE",
|
||||
POST_EDIT = "POST_EDIT",
|
||||
POST = "POST",
|
||||
POSTS = "POSTS",
|
||||
GROUP_EVENTS = "GROUP_EVENTS",
|
||||
GROUP_JOIN = "GROUP_JOIN",
|
||||
GROUP_FOLLOW = "GROUP_FOLLOW",
|
||||
TIMELINE = "TIMELINE",
|
||||
}
|
||||
|
||||
const resourceFolder = (): Promise<any> =>
|
||||
import("@/views/Resources/ResourceFolder.vue");
|
||||
const groupEvents = (): Promise<any> => import("@/views/Event/GroupEvents.vue");
|
||||
|
||||
export const groupsRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/@:preferredUsername/todo-lists",
|
||||
name: GroupsRouteName.TODO_LISTS,
|
||||
component: (): Promise<any> => import("@/views/Todos/TodoLists.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/todo-lists/:id",
|
||||
name: GroupsRouteName.TODO_LIST,
|
||||
component: (): Promise<any> => import("@/views/Todos/TodoList.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/todo/:todoId",
|
||||
name: GroupsRouteName.TODO,
|
||||
component: (): Promise<any> => import("@/views/Todos/TodoView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/resources",
|
||||
name: GroupsRouteName.RESOURCE_FOLDER_ROOT,
|
||||
component: resourceFolder,
|
||||
props: (to) => ({
|
||||
path: "/",
|
||||
preferredUsername: to.params.preferredUsername,
|
||||
}),
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/resources/:path+",
|
||||
name: GroupsRouteName.RESOURCE_FOLDER,
|
||||
component: resourceFolder,
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/settings",
|
||||
component: (): Promise<any> => import("@/views/Group/SettingsView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true },
|
||||
redirect: { name: GroupsRouteName.GROUP_PUBLIC_SETTINGS },
|
||||
name: GroupsRouteName.GROUP_SETTINGS,
|
||||
children: [
|
||||
{
|
||||
path: "public",
|
||||
name: GroupsRouteName.GROUP_PUBLIC_SETTINGS,
|
||||
props: true,
|
||||
component: (): Promise<any> =>
|
||||
import("../views/Group/GroupSettings.vue"),
|
||||
meta: { announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "members",
|
||||
name: GroupsRouteName.GROUP_MEMBERS_SETTINGS,
|
||||
component: (): Promise<any> =>
|
||||
import("../views/Group/GroupMembers.vue"),
|
||||
props: true,
|
||||
meta: { announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "followers",
|
||||
name: GroupsRouteName.GROUP_FOLLOWERS_SETTINGS,
|
||||
component: (): Promise<any> =>
|
||||
import("../views/Group/GroupFollowers.vue"),
|
||||
props: true,
|
||||
meta: { announcer: { skip: true } },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/p/new",
|
||||
component: (): Promise<any> => import("@/views/Posts/EditView.vue"),
|
||||
props: true,
|
||||
name: GroupsRouteName.POST_CREATE,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/p/:slug/edit",
|
||||
component: (): Promise<any> => import("@/views/Posts/EditView.vue"),
|
||||
props: (route: RouteLocationNormalized): Record<string, unknown> => ({
|
||||
...route.params,
|
||||
...{ isUpdate: true },
|
||||
}),
|
||||
name: GroupsRouteName.POST_EDIT,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/p/:slug",
|
||||
component: (): Promise<any> => import("@/views/Posts/PostView.vue"),
|
||||
props: true,
|
||||
name: GroupsRouteName.POST,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/p",
|
||||
component: (): Promise<any> => import("@/views/Posts/ListView.vue"),
|
||||
props: true,
|
||||
name: GroupsRouteName.POSTS,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/events",
|
||||
component: groupEvents,
|
||||
props: true,
|
||||
name: GroupsRouteName.GROUP_EVENTS,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/join",
|
||||
component: (): Promise<any> =>
|
||||
import("@/components/Group/JoinGroupWithAccount.vue"),
|
||||
props: true,
|
||||
name: GroupsRouteName.GROUP_JOIN,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/follow",
|
||||
component: (): Promise<any> =>
|
||||
import("@/components/Group/JoinGroupWithAccount.vue"),
|
||||
props: true,
|
||||
name: GroupsRouteName.GROUP_FOLLOW,
|
||||
meta: { requiredAuth: false, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/@:preferredUsername/timeline",
|
||||
name: GroupsRouteName.TIMELINE,
|
||||
component: (): Promise<any> => import("@/views/Group/TimelineView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
];
|
||||
22
src/router/guards/auth-guard.ts
Normal file
22
src/router/guards/auth-guard.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { NavigationGuard } from "vue-router";
|
||||
import { UserRouteName } from "@/router/user";
|
||||
import { AUTH_ACCESS_TOKEN } from "@/constants";
|
||||
import { LoginErrorCode } from "@/types/enums";
|
||||
|
||||
export const authGuardIfNeeded: NavigationGuard = async (to, from, next) => {
|
||||
if (to.meta?.requiredAuth !== true) return next();
|
||||
|
||||
// We can't use "currentUser" from apollo here
|
||||
// because we may not have loaded the user from the local storage yet
|
||||
if (!localStorage.getItem(AUTH_ACCESS_TOKEN)) {
|
||||
return next({
|
||||
name: UserRouteName.LOGIN,
|
||||
query: {
|
||||
code: LoginErrorCode.NEED_TO_LOGIN,
|
||||
redirect: to.fullPath,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
33
src/router/guards/register-guard.ts
Normal file
33
src/router/guards/register-guard.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { IConfig } from "@/types/config.model";
|
||||
import { ErrorCode } from "@/types/enums";
|
||||
import { provideApolloClient, useQuery } from "@vue/apollo-composable";
|
||||
import { NavigationGuard } from "vue-router";
|
||||
import { CONFIG } from "../../graphql/config";
|
||||
import { apolloClient } from "../../vue-apollo";
|
||||
import { ErrorRouteName } from "../error";
|
||||
|
||||
export const beforeRegisterGuard: NavigationGuard = async (to, from, next) => {
|
||||
const { onResult, onError } = provideApolloClient(apolloClient)(() =>
|
||||
useQuery<{ config: IConfig }>(CONFIG)
|
||||
);
|
||||
|
||||
onResult(({ data }) => {
|
||||
if (!data) return next();
|
||||
const { config } = data;
|
||||
|
||||
if (!config.registrationsOpen && !config.registrationsAllowlist) {
|
||||
return next({
|
||||
name: ErrorRouteName.ERROR,
|
||||
query: { code: ErrorCode.REGISTRATION_CLOSED },
|
||||
});
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
onError((err) => {
|
||||
console.error(err);
|
||||
return next();
|
||||
});
|
||||
return next();
|
||||
};
|
||||
218
src/router/index.ts
Normal file
218
src/router/index.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import VueScrollTo from "vue-scrollto";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
import { eventRoutes } from "./event";
|
||||
import { actorRoutes } from "./actor";
|
||||
import { errorRoutes } from "./error";
|
||||
import { authGuardIfNeeded } from "./guards/auth-guard";
|
||||
import { settingsRoutes } from "./settings";
|
||||
import { groupsRoutes } from "./groups";
|
||||
import { discussionRoutes } from "./discussion";
|
||||
import { conversationRoutes } from "./conversation";
|
||||
import { userRoutes } from "./user";
|
||||
import RouteName from "./name";
|
||||
import { AVAILABLE_LANGUAGES, i18n } from "@/utils/i18n";
|
||||
|
||||
const { t } = i18n.global;
|
||||
|
||||
function scrollBehavior(to: any, from: any, savedPosition: any) {
|
||||
if (to.hash) {
|
||||
VueScrollTo.scrollTo(to.hash, 700);
|
||||
return {
|
||||
selector: to.hash,
|
||||
offset: { left: 0, top: 10 },
|
||||
};
|
||||
}
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
}
|
||||
|
||||
return { left: 0, top: 0 };
|
||||
}
|
||||
|
||||
export const routes = [
|
||||
...userRoutes,
|
||||
...eventRoutes,
|
||||
...settingsRoutes,
|
||||
...actorRoutes,
|
||||
...groupsRoutes,
|
||||
...discussionRoutes,
|
||||
...conversationRoutes,
|
||||
...errorRoutes,
|
||||
{
|
||||
path: "/search",
|
||||
name: RouteName.SEARCH,
|
||||
component: (): Promise<any> => import("@/views/SearchView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Search") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: RouteName.HOME,
|
||||
component: HomeView,
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Homepage") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/categories",
|
||||
name: RouteName.CATEGORIES,
|
||||
component: (): Promise<any> => import("@/views/CategoriesView.vue"),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Categories") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/about",
|
||||
name: RouteName.ABOUT,
|
||||
component: (): Promise<any> => import("@/views/AboutView.vue"),
|
||||
meta: { requiredAuth: false },
|
||||
redirect: { name: RouteName.ABOUT_INSTANCE },
|
||||
children: [
|
||||
{
|
||||
path: "instance",
|
||||
name: RouteName.ABOUT_INSTANCE,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/About/AboutInstanceView.vue"),
|
||||
meta: {
|
||||
announcer: {
|
||||
message: (): string => t("About instance") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/terms",
|
||||
name: RouteName.TERMS,
|
||||
component: (): Promise<any> => import("@/views/About/TermsView.vue"),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Terms") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/privacy",
|
||||
name: RouteName.PRIVACY,
|
||||
component: (): Promise<any> => import("@/views/About/PrivacyView.vue"),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Privacy") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/rules",
|
||||
name: RouteName.RULES,
|
||||
component: (): Promise<any> => import("@/views/About/RulesView.vue"),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Rules") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/glossary",
|
||||
name: RouteName.GLOSSARY,
|
||||
component: (): Promise<any> => import("@/views/About/GlossaryView.vue"),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Glossary") as string },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/interact",
|
||||
name: RouteName.INTERACT,
|
||||
component: (): Promise<any> => import("@/views/InteractView.vue"),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Interact") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/auth/:provider/callback",
|
||||
name: "auth-callback",
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/User/ProviderValidation.vue"),
|
||||
meta: {
|
||||
announcer: {
|
||||
message: (): string => t("Redirecting to Mobilizon") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/welcome/:step?",
|
||||
name: RouteName.WELCOME_SCREEN,
|
||||
component: (): Promise<any> =>
|
||||
import(
|
||||
/* webpackChunkName: "WelcomeScreen" */ "@/views/User/SettingsOnboard.vue"
|
||||
),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("First steps") as string },
|
||||
},
|
||||
props: (route: any): Record<string, unknown> => {
|
||||
const step = Number.parseInt(route.params.step, 10);
|
||||
if (Number.isNaN(step)) {
|
||||
return { step: 1 };
|
||||
}
|
||||
return { step };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/404",
|
||||
name: RouteName.PAGE_NOT_FOUND,
|
||||
component: (): Promise<any> =>
|
||||
import(
|
||||
/* webpackChunkName: "PageNotFound" */ "../views/PageNotFound.vue"
|
||||
),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Page not found") as string },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const locale of AVAILABLE_LANGUAGES) {
|
||||
routes.push({
|
||||
path: `/${locale}`,
|
||||
component: () =>
|
||||
import("../components/Utils/HomepageRedirectComponent.vue"),
|
||||
});
|
||||
}
|
||||
|
||||
routes.push({
|
||||
path: "/:pathMatch(.*)*",
|
||||
redirect: { name: RouteName.PAGE_NOT_FOUND },
|
||||
});
|
||||
|
||||
export const router = createRouter({
|
||||
scrollBehavior,
|
||||
history: createWebHistory("/"),
|
||||
routes,
|
||||
});
|
||||
|
||||
router.beforeEach(authGuardIfNeeded);
|
||||
// router.afterEach(() => {
|
||||
// try {
|
||||
// if (router.app.$children[0]) {
|
||||
// // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// // @ts-ignore
|
||||
// router.app.$children[0].error = null;
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// });
|
||||
|
||||
router.onError((error, to) => {
|
||||
if (
|
||||
error.message.includes("Failed to fetch dynamically imported module") ||
|
||||
error.message.includes("Importing a module script failed")
|
||||
) {
|
||||
window.location.href = to.fullPath;
|
||||
}
|
||||
});
|
||||
37
src/router/name.ts
Normal file
37
src/router/name.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { EventRouteName } from "./event";
|
||||
import { ActorRouteName } from "./actor";
|
||||
import { ErrorRouteName } from "./error";
|
||||
import { SettingsRouteName } from "./settings";
|
||||
import { GroupsRouteName } from "./groups";
|
||||
import { DiscussionRouteName } from "./discussion";
|
||||
import { ConversationRouteName } from "./conversation";
|
||||
import { UserRouteName } from "./user";
|
||||
|
||||
enum GlobalRouteName {
|
||||
HOME = "HOME",
|
||||
ABOUT = "ABOUT",
|
||||
CATEGORIES = "CATEGORIES",
|
||||
ABOUT_INSTANCE = "ABOUT_INSTANCE",
|
||||
PAGE_NOT_FOUND = "PageNotFound",
|
||||
SEARCH = "SEARCH",
|
||||
TERMS = "TERMS",
|
||||
PRIVACY = "PRIVACY",
|
||||
GLOSSARY = "GLOSSARY",
|
||||
INTERACT = "INTERACT",
|
||||
RULES = "RULES",
|
||||
WELCOME_SCREEN = "WELCOME_SCREEN",
|
||||
}
|
||||
|
||||
// Hack to merge enums
|
||||
// tslint:disable:variable-name
|
||||
export default {
|
||||
...GlobalRouteName,
|
||||
...UserRouteName,
|
||||
...EventRouteName,
|
||||
...ActorRouteName,
|
||||
...SettingsRouteName,
|
||||
...GroupsRouteName,
|
||||
...DiscussionRouteName,
|
||||
...ConversationRouteName,
|
||||
...ErrorRouteName,
|
||||
};
|
||||
292
src/router/settings.ts
Normal file
292
src/router/settings.ts
Normal file
@@ -0,0 +1,292 @@
|
||||
import { i18n } from "@/utils/i18n";
|
||||
import { RouteLocationNormalized, RouteRecordRaw } from "vue-router";
|
||||
|
||||
const { t } = i18n.global;
|
||||
|
||||
export enum SettingsRouteName {
|
||||
SETTINGS = "SETTINGS",
|
||||
ACCOUNT_SETTINGS = "ACCOUNT_SETTINGS",
|
||||
ACCOUNT_SETTINGS_GENERAL = "ACCOUNT_SETTINGS_GENERAL",
|
||||
PREFERENCES = "PREFERENCES",
|
||||
NOTIFICATIONS = "NOTIFICATIONS",
|
||||
ADMIN = "ADMIN",
|
||||
ADMIN_DASHBOARD = "ADMIN_DASHBOARD",
|
||||
ADMIN_SETTINGS = "ADMIN_SETTINGS",
|
||||
INSTANCES = "INSTANCES",
|
||||
INSTANCE = "INSTANCE",
|
||||
USERS = "USERS",
|
||||
PROFILES = "PROFILES",
|
||||
ADMIN_PROFILE = "ADMIN_PROFILE",
|
||||
ADMIN_USER_PROFILE = "ADMIN_USER_PROFILE",
|
||||
ADMIN_GROUPS = "ADMIN_GROUPS",
|
||||
ADMIN_GROUP_PROFILE = "ADMIN_GROUP_PROFILE",
|
||||
MODERATION = "MODERATION",
|
||||
REPORTS = "REPORTS",
|
||||
REPORT = "Report",
|
||||
REPORT_LOGS = "Logs",
|
||||
CREATE_IDENTITY = "CreateIdentity",
|
||||
UPDATE_IDENTITY = "UpdateIdentity",
|
||||
IDENTITIES = "IDENTITIES",
|
||||
AUTHORIZED_APPS = "AUTHORIZED_APPS",
|
||||
}
|
||||
|
||||
export const settingsRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/settings",
|
||||
component: () => import("@/views/SettingsView.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
redirect: { name: SettingsRouteName.ACCOUNT_SETTINGS },
|
||||
name: SettingsRouteName.SETTINGS,
|
||||
children: [
|
||||
{
|
||||
path: "account",
|
||||
name: SettingsRouteName.ACCOUNT_SETTINGS,
|
||||
redirect: { name: SettingsRouteName.ACCOUNT_SETTINGS_GENERAL },
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { skip: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "account/general",
|
||||
name: SettingsRouteName.ACCOUNT_SETTINGS_GENERAL,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Settings/AccountSettings.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Account settings") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "preferences",
|
||||
name: SettingsRouteName.PREFERENCES,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Settings/PreferencesView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("Preferences") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "notifications",
|
||||
name: SettingsRouteName.NOTIFICATIONS,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Settings/NotificationsView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Notifications") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "authorized-apps",
|
||||
name: SettingsRouteName.AUTHORIZED_APPS,
|
||||
component: (): Promise<any> => import("@/views/Settings/AppsView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Apps") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin",
|
||||
name: SettingsRouteName.ADMIN,
|
||||
redirect: { name: SettingsRouteName.ADMIN_DASHBOARD },
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "admin/dashboard",
|
||||
name: SettingsRouteName.ADMIN_DASHBOARD,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Admin/DashboardView.vue"),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Admin dashboard") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin/settings",
|
||||
name: SettingsRouteName.ADMIN_SETTINGS,
|
||||
component: (): Promise<any> => import("@/views/Admin/SettingsView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Admin settings") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin/users",
|
||||
name: SettingsRouteName.USERS,
|
||||
component: (): Promise<any> => import("@/views/Admin/UsersView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("Users") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin/users/:id",
|
||||
name: SettingsRouteName.ADMIN_USER_PROFILE,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Admin/AdminUserProfile.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { skip: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin/profiles",
|
||||
name: SettingsRouteName.PROFILES,
|
||||
component: (): Promise<any> => import("@/views/Admin/ProfilesView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("Profiles") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin/profiles/:id",
|
||||
name: SettingsRouteName.ADMIN_PROFILE,
|
||||
component: (): Promise<any> => import("@/views/Admin/AdminProfile.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "admin/groups",
|
||||
name: SettingsRouteName.ADMIN_GROUPS,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Admin/GroupProfiles.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Group profiles") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "admin/groups/:id",
|
||||
name: SettingsRouteName.ADMIN_GROUP_PROFILE,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Admin/AdminGroupProfile.vue"),
|
||||
props: true,
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "admin/instances",
|
||||
name: SettingsRouteName.INSTANCES,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Admin/InstancesView.vue"),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Instances") as string,
|
||||
},
|
||||
},
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "admin/instances/:domain",
|
||||
name: SettingsRouteName.INSTANCE,
|
||||
component: (): Promise<any> => import("@/views/Admin/InstanceView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Instance") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/moderation",
|
||||
name: SettingsRouteName.MODERATION,
|
||||
redirect: { name: SettingsRouteName.REPORTS },
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/moderation/reports",
|
||||
name: SettingsRouteName.REPORTS,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Moderation/ReportListView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Reports list") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/moderation/report/:reportId",
|
||||
name: SettingsRouteName.REPORT,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Moderation/ReportView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: { message: (): string => t("Report") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/moderation/logs",
|
||||
name: SettingsRouteName.REPORT_LOGS,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Moderation/LogsView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Moderation logs") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/identity",
|
||||
name: SettingsRouteName.IDENTITIES,
|
||||
redirect: { name: SettingsRouteName.UPDATE_IDENTITY },
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
{
|
||||
path: "/identity/create",
|
||||
name: SettingsRouteName.CREATE_IDENTITY,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Account/children/EditIdentity.vue"),
|
||||
props: (route: RouteLocationNormalized): Record<string, unknown> => ({
|
||||
identityName: route.params.identityName,
|
||||
isUpdate: false,
|
||||
}),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Create identity") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/identity/update/:identityName?",
|
||||
name: SettingsRouteName.UPDATE_IDENTITY,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/Account/children/EditIdentity.vue"),
|
||||
props: (route: RouteLocationNormalized): Record<string, unknown> => ({
|
||||
identityName: route.params.identityName,
|
||||
isUpdate: true,
|
||||
}),
|
||||
meta: { requiredAuth: true, announcer: { skip: true } },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
136
src/router/user.ts
Normal file
136
src/router/user.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { beforeRegisterGuard } from "@/router/guards/register-guard";
|
||||
import { RouteLocationNormalized, RouteRecordRaw } from "vue-router";
|
||||
import { i18n } from "@/utils/i18n";
|
||||
|
||||
const t = i18n.global.t;
|
||||
|
||||
export enum UserRouteName {
|
||||
REGISTER = "Register",
|
||||
REGISTER_PROFILE = "RegisterProfile",
|
||||
RESEND_CONFIRMATION = "ResendConfirmation",
|
||||
SEND_PASSWORD_RESET = "SendPasswordReset",
|
||||
PASSWORD_RESET = "PasswordReset",
|
||||
EMAIL_VALIDATE = "EMAIL_VALIDATE",
|
||||
VALIDATE = "Validate",
|
||||
LOGIN = "Login",
|
||||
OAUTH_AUTORIZE = "OAUTH_AUTORIZE",
|
||||
OAUTH_LOGIN_DEVICE = "OAUTH_LOGIN_DEVICE",
|
||||
}
|
||||
|
||||
export const userRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/register/user",
|
||||
name: UserRouteName.REGISTER,
|
||||
component: (): Promise<any> => import("@/views/User/RegisterView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Register") as string },
|
||||
},
|
||||
beforeEnter: beforeRegisterGuard,
|
||||
},
|
||||
{
|
||||
path: "/register/profile/:email/:userAlreadyActivated?",
|
||||
name: UserRouteName.REGISTER_PROFILE,
|
||||
component: (): Promise<any> => import("@/views/Account/RegisterView.vue"),
|
||||
// We can only pass string values through params, therefore
|
||||
props: (route: RouteLocationNormalized): Record<string, unknown> => ({
|
||||
email: route.params.email,
|
||||
userAlreadyActivated: route.params.userAlreadyActivated === "true",
|
||||
}),
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Register") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/resend-instructions/:email?",
|
||||
name: UserRouteName.RESEND_CONFIRMATION,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/User/ResendConfirmation.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
announcer: {
|
||||
message: (): string => t("Resent confirmation email") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/password-reset/send/:email?",
|
||||
name: UserRouteName.SEND_PASSWORD_RESET,
|
||||
component: (): Promise<any> => import("@/views/User/SendPasswordReset.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
announcer: {
|
||||
message: (): string => t("Send password reset") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/password-reset/:token",
|
||||
name: UserRouteName.PASSWORD_RESET,
|
||||
component: (): Promise<any> => import("@/views/User/PasswordReset.vue"),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
announcer: { message: (): string => t("Password reset") as string },
|
||||
},
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/validate/email/:token",
|
||||
name: UserRouteName.EMAIL_VALIDATE,
|
||||
component: (): Promise<any> => import("@/views/User/EmailValidate.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
announcer: { message: (): string => t("Email validate") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/validate/:token",
|
||||
name: UserRouteName.VALIDATE,
|
||||
component: (): Promise<any> => import("@/views/User/ValidateUser.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
announcer: {
|
||||
message: (): string => t("Validating account") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
name: UserRouteName.LOGIN,
|
||||
component: (): Promise<any> => import("@/views/User/LoginView.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
requiredAuth: false,
|
||||
announcer: { message: (): string => t("Login") as string },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/oauth/autorize_approve",
|
||||
name: UserRouteName.OAUTH_AUTORIZE,
|
||||
component: (): Promise<any> => import("@/views/OAuth/AuthorizeView.vue"),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Authorize application") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/login/device",
|
||||
name: UserRouteName.OAUTH_LOGIN_DEVICE,
|
||||
component: (): Promise<any> =>
|
||||
import("@/views/OAuth/DeviceActivationView.vue"),
|
||||
meta: {
|
||||
requiredAuth: true,
|
||||
announcer: {
|
||||
message: (): string => t("Device activation") as string,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user