web: re-organise frontend and cleanup common code (#3572)
* fix repo in api client Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: re-organise files to match their interface Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: include version in script tags Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * cleanup maybe broken Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * revert rename Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: get rid of Client.ts Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * move more to common Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * more moving Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * format Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * unfuck files that vscode fucked, thanks Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * move more Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * finish moving (maybe) Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * ok more moving Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix more stuff that vs code destroyed Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * get rid "web" prefix for virtual package Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix locales Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * use custom base element Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix css file Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * don't run autoDetectLanguage when importing locale Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix circular dependencies Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: fix build Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
89
web/src/common/ui/config.ts
Normal file
89
web/src/common/ui/config.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { me } from "@goauthentik/common/users";
|
||||
|
||||
import { UserSelf } from "@goauthentik/api";
|
||||
|
||||
export enum UserDisplay {
|
||||
username = "username",
|
||||
name = "name",
|
||||
email = "email",
|
||||
}
|
||||
|
||||
export enum LayoutType {
|
||||
row = "row",
|
||||
column_2 = "2-column",
|
||||
column_3 = "3-column",
|
||||
}
|
||||
|
||||
export interface UIConfig {
|
||||
enabledFeatures: {
|
||||
// API Request drawer in navbar
|
||||
apiDrawer: boolean;
|
||||
// Notification drawer in navbar
|
||||
notificationDrawer: boolean;
|
||||
// Settings in user dropdown
|
||||
settings: boolean;
|
||||
// Application edit in library (only shown when user is superuser)
|
||||
applicationEdit: boolean;
|
||||
// Search bar
|
||||
search: boolean;
|
||||
};
|
||||
navbar: {
|
||||
userDisplay: UserDisplay;
|
||||
};
|
||||
theme: {
|
||||
background: string;
|
||||
cardBackground: string;
|
||||
};
|
||||
pagination: {
|
||||
perPage: number;
|
||||
};
|
||||
layout: {
|
||||
type: LayoutType;
|
||||
};
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export class DefaultUIConfig implements UIConfig {
|
||||
enabledFeatures = {
|
||||
apiDrawer: true,
|
||||
notificationDrawer: true,
|
||||
settings: true,
|
||||
applicationEdit: true,
|
||||
search: true,
|
||||
};
|
||||
layout = {
|
||||
type: LayoutType.row,
|
||||
};
|
||||
navbar = {
|
||||
userDisplay: UserDisplay.username,
|
||||
};
|
||||
theme = {
|
||||
background: "",
|
||||
cardBackground: "",
|
||||
};
|
||||
pagination = {
|
||||
perPage: 20,
|
||||
};
|
||||
locale = "";
|
||||
}
|
||||
|
||||
let globalUiConfig: Promise<UIConfig>;
|
||||
|
||||
export function getConfigForUser(user: UserSelf): UIConfig {
|
||||
const settings = user.settings;
|
||||
let config = new DefaultUIConfig();
|
||||
if (!settings) {
|
||||
return config;
|
||||
}
|
||||
config = Object.assign(new DefaultUIConfig(), settings);
|
||||
return config;
|
||||
}
|
||||
|
||||
export function uiConfig(): Promise<UIConfig> {
|
||||
if (!globalUiConfig) {
|
||||
globalUiConfig = me().then((user) => {
|
||||
return getConfigForUser(user.user);
|
||||
});
|
||||
}
|
||||
return globalUiConfig;
|
||||
}
|
||||
174
web/src/common/ui/locale.ts
Normal file
174
web/src/common/ui/locale.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import { EVENT_LOCALE_CHANGE } from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
|
||||
import { Messages, i18n } from "@lingui/core";
|
||||
import { detect, fromNavigator, fromUrl } from "@lingui/detect-locale";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
interface Locale {
|
||||
locale: Messages;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
plurals: Function;
|
||||
}
|
||||
|
||||
export const LOCALES: {
|
||||
code: string;
|
||||
label: string;
|
||||
locale: () => Promise<Locale>;
|
||||
}[] = [
|
||||
{
|
||||
code: "en",
|
||||
label: t`English`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/en")).messages,
|
||||
plurals: (await import("make-plural/plurals")).en,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "debug",
|
||||
label: t`Debug`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/pseudo-LOCALE")).messages,
|
||||
plurals: (await import("make-plural/plurals")).en,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "fr",
|
||||
label: t`French`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/fr_FR")).messages,
|
||||
plurals: (await import("make-plural/plurals")).fr,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "tr",
|
||||
label: t`Turkish`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/tr")).messages,
|
||||
plurals: (await import("make-plural/plurals")).tr,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "es",
|
||||
label: t`Spanish`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/es")).messages,
|
||||
plurals: (await import("make-plural/plurals")).es,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "pl",
|
||||
label: t`Polish`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/pl")).messages,
|
||||
plurals: (await import("make-plural/plurals")).pl,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "zh_TW",
|
||||
label: t`Taiwanese Mandarin`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/zh_TW")).messages,
|
||||
plurals: (await import("make-plural/plurals")).zh,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "zh-CN",
|
||||
label: t`Chinese (simplified)`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/zh-Hans")).messages,
|
||||
plurals: (await import("make-plural/plurals")).zh,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "zh-HK",
|
||||
label: t`Chinese (traditional)`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/zh-Hant")).messages,
|
||||
plurals: (await import("make-plural/plurals")).zh,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "de",
|
||||
label: t`German`,
|
||||
locale: async () => {
|
||||
return {
|
||||
locale: (await import("@goauthentik/locales/de")).messages,
|
||||
plurals: (await import("make-plural/plurals")).de,
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_FALLBACK = () => "en";
|
||||
|
||||
export function autoDetectLanguage() {
|
||||
const detected =
|
||||
detect(
|
||||
() => {
|
||||
return globalAK()?.locale;
|
||||
},
|
||||
fromUrl("locale"),
|
||||
fromNavigator(),
|
||||
DEFAULT_FALLBACK,
|
||||
) || DEFAULT_FALLBACK();
|
||||
const locales = [detected];
|
||||
// For now we only care about the first locale part
|
||||
if (detected.includes("_")) {
|
||||
locales.push(detected.split("_")[0]);
|
||||
}
|
||||
if (detected.includes("-")) {
|
||||
locales.push(detected.split("-")[0]);
|
||||
}
|
||||
for (const tryLocale of locales) {
|
||||
if (LOCALES.find((locale) => locale.code === tryLocale)) {
|
||||
console.debug(`authentik/locale: Activating detected locale '${tryLocale}'`);
|
||||
activateLocale(tryLocale);
|
||||
return;
|
||||
} else {
|
||||
console.debug(`authentik/locale: No matching locale for ${tryLocale}`);
|
||||
}
|
||||
}
|
||||
console.debug(`authentik/locale: No locale for '${locales}', falling back to en`);
|
||||
activateLocale(DEFAULT_FALLBACK());
|
||||
}
|
||||
export function activateLocale(code: string) {
|
||||
const urlLocale = fromUrl("locale");
|
||||
if (urlLocale !== null && urlLocale !== "") {
|
||||
code = urlLocale;
|
||||
}
|
||||
const locale = LOCALES.find((locale) => locale.code == code);
|
||||
if (!locale) {
|
||||
console.warn(`authentik/locale: failed to find locale for code ${code}`);
|
||||
return;
|
||||
}
|
||||
locale.locale().then((localeData) => {
|
||||
i18n.loadLocaleData(locale.code, { plurals: localeData.plurals });
|
||||
i18n.load(locale.code, localeData.locale);
|
||||
i18n.activate(locale.code);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(EVENT_LOCALE_CHANGE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user