mirror of
https://github.com/ilyhalight/voice-over-translation.git
synced 2026-10-19 07:43:33 +00:00
refactor: rewrited AboutSection with Solid
This commit is contained in:
parent
bfbfce1cc3
commit
166a66ccd0
27 changed files with 943 additions and 793 deletions
|
|
@ -45,6 +45,7 @@ const config: StorybookConfig = {
|
|||
],
|
||||
define: {
|
||||
DEBUG_MODE: true,
|
||||
GM_info: {},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
# 1.12.0 (WIP)
|
||||
|
||||
- Переработан дизайн блока настроек "Мой аккаунт"
|
||||
- Переработан дизайн блока "О расширении"
|
||||
- Все `Checkbox` в настройках были заменены на `Switch`
|
||||
- Весь UI расширения переведен на SolidJS
|
||||
- Исправлена ошибка из-за которой Debug логи попадали в релизную сборку расширения
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
22
dist/vot-min.user.js
vendored
22
dist/vot-min.user.js
vendored
File diff suppressed because one or more lines are too long
1331
dist/vot.user.js
vendored
1331
dist/vot.user.js
vendored
File diff suppressed because one or more lines are too long
17
src/components/About/AboutItem.scss
Normal file
17
src/components/About/AboutItem.scss
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.vot-about-item {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 0.8em;
|
||||
|
||||
&__label {
|
||||
text-transform: lowercase;
|
||||
color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.68);
|
||||
}
|
||||
|
||||
&__value {
|
||||
text-align: end;
|
||||
color: rgba(var(--vot-primary-rgb, 33, 150, 243), 0.9);
|
||||
}
|
||||
}
|
||||
18
src/components/About/AboutItem.tsx
Normal file
18
src/components/About/AboutItem.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import type { JSX } from "solid-js";
|
||||
|
||||
import "./AboutItem.scss";
|
||||
|
||||
export type AccountInfoProps = {
|
||||
label: string;
|
||||
children: JSX.Element;
|
||||
ref?: (element: HTMLElement) => void;
|
||||
};
|
||||
|
||||
export function AboutItem(props: AccountInfoProps): JSX.Element {
|
||||
return (
|
||||
<vot-block ref={props.ref} class="vot-about-item">
|
||||
<vot-block class="vot-about-item__label">{props.label}</vot-block>
|
||||
<vot-block class="vot-about-item__value">{props.children}</vot-block>
|
||||
</vot-block>
|
||||
);
|
||||
}
|
||||
12
src/components/About/AboutSection.scss
Normal file
12
src/components/About/AboutSection.scss
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
.vot-about-section {
|
||||
--vot-about-sep-color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
|
||||
> .vot-about-item:not(:last-child) {
|
||||
border-bottom: 1px solid var(--vot-about-sep-color);
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
}
|
||||
14
src/components/About/AboutSection.stories.tsx
Normal file
14
src/components/About/AboutSection.stories.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import type { Meta, StoryObj } from "storybook-solidjs-vite";
|
||||
|
||||
import { AboutSection } from "./AboutSection";
|
||||
|
||||
const meta = {
|
||||
component: AboutSection,
|
||||
} satisfies Meta<typeof AboutSection>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const AboutSectionDefault: Story = {
|
||||
args: {},
|
||||
};
|
||||
67
src/components/About/AboutSection.tsx
Normal file
67
src/components/About/AboutSection.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import type { TMInfoScriptMeta } from "@toil/gm-types/types/info/tampermonkey";
|
||||
import type { JSX } from "solid-js";
|
||||
|
||||
import { AboutItem } from "./AboutItem";
|
||||
import "./AboutSection.scss";
|
||||
|
||||
import { localizationProvider } from "../../localization/localizationProvider";
|
||||
import { locale } from "../../stores/locale";
|
||||
import { getEnvironmentInfo } from "../../utils/environment";
|
||||
import { votStorage } from "../../utils/storage";
|
||||
import { OutlinedButton } from "../Button/OutlinedButton";
|
||||
|
||||
export type AccountSectionProps = {
|
||||
ref?: (element: HTMLElement) => void;
|
||||
};
|
||||
|
||||
export function AboutSection(props: AccountSectionProps): JSX.Element {
|
||||
const envInfo = getEnvironmentInfo();
|
||||
const safeGMInfo = typeof GM_info === "undefined" ? undefined : GM_info;
|
||||
|
||||
const scriptVersion = () =>
|
||||
envInfo.scriptVersion === "unknown"
|
||||
? safeGMInfo?.script?.version || localizationProvider.get("notFound")
|
||||
: envInfo.scriptVersion;
|
||||
const buildAuthors = () =>
|
||||
typeof VOT_AUTHORS === "undefined" ? "" : String(VOT_AUTHORS);
|
||||
const scriptAuthors = () =>
|
||||
(safeGMInfo?.script as TMInfoScriptMeta)?.author ||
|
||||
buildAuthors() ||
|
||||
localizationProvider.get("notFound");
|
||||
const loaderInfo = () => envInfo.loader;
|
||||
const browserInfo = () => `${envInfo.browser} (${envInfo.os})`;
|
||||
const localeUpdatedAt = () =>
|
||||
new Date(locale.updatedAt * 1000).toLocaleString();
|
||||
const localeHashValue = () =>
|
||||
locale.hash || localizationProvider.get("notFound");
|
||||
|
||||
return (
|
||||
<vot-block ref={props.ref} class="vot-about-section">
|
||||
<AboutItem label={localizationProvider.get("VOTVersion")}>
|
||||
{scriptVersion()}
|
||||
</AboutItem>
|
||||
<AboutItem label={localizationProvider.get("VOTAuthors")}>
|
||||
{scriptAuthors()}
|
||||
</AboutItem>
|
||||
<AboutItem label={localizationProvider.get("VOTLoader")}>
|
||||
{loaderInfo()}
|
||||
</AboutItem>
|
||||
<AboutItem label={localizationProvider.get("VOTBrowser")}>
|
||||
{browserInfo()}
|
||||
</AboutItem>
|
||||
<AboutItem label={localizationProvider.get("VOTLocaleHash")}>
|
||||
{localeHashValue()}
|
||||
<br />({localizationProvider.get("VOTUpdatedAt")} {localeUpdatedAt})
|
||||
</AboutItem>
|
||||
<OutlinedButton
|
||||
onClick={async () => {
|
||||
await votStorage.set("localeHash", "");
|
||||
await localizationProvider.update(true);
|
||||
globalThis.location.reload();
|
||||
}}
|
||||
>
|
||||
{localizationProvider.get("VOTUpdateLocaleFiles")}
|
||||
</OutlinedButton>
|
||||
</vot-block>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
&__block {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
gap: 0.5em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { JSX } from "solid-js";
|
||||
|
||||
import "./IconButton.scss";
|
||||
import { RawButton, type RawButtonProps } from "./RawButton";
|
||||
|
||||
|
|
|
|||
1
src/components/Button/OutlinedButton.scss
Normal file
1
src/components/Button/OutlinedButton.scss
Normal file
|
|
@ -0,0 +1 @@
|
|||
@use "../../styles/components/buttons/outlined.scss";
|
||||
42
src/components/Button/OutlinedButton.stories.tsx
Normal file
42
src/components/Button/OutlinedButton.stories.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import type { Meta, StoryObj } from "storybook-solidjs-vite";
|
||||
|
||||
import { OutlinedButton } from "./OutlinedButton";
|
||||
|
||||
const meta = {
|
||||
component: OutlinedButton,
|
||||
} satisfies Meta<typeof OutlinedButton>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const OutlinedButtonDefault: Story = {
|
||||
args: {
|
||||
children: "Settings",
|
||||
},
|
||||
};
|
||||
|
||||
export const OutlinedButtonWithOnClick: Story = {
|
||||
args: {
|
||||
children: "Settings",
|
||||
onClick: () => {
|
||||
alert("hi");
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const OutlinedButtonDisabled: Story = {
|
||||
args: {
|
||||
children: "Settings",
|
||||
disabled: true,
|
||||
onClick: () => {
|
||||
alert("you shouldn't see this");
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const OutlinedButtonHidden: Story = {
|
||||
args: {
|
||||
children: "Settings",
|
||||
hidden: true,
|
||||
},
|
||||
};
|
||||
10
src/components/Button/OutlinedButton.tsx
Normal file
10
src/components/Button/OutlinedButton.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { JSX } from "solid-js";
|
||||
|
||||
import "./OutlinedButton.scss";
|
||||
import { RawButton, type RawButtonProps } from "./RawButton";
|
||||
|
||||
export type OutlinedButtonProps = Omit<RawButtonProps, "class">;
|
||||
|
||||
export function OutlinedButton(props: OutlinedButtonProps): JSX.Element {
|
||||
return <RawButton {...props} class="vot-outlined-button" />;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import type { JSX } from "solid-js";
|
||||
|
||||
import "./TextButton.scss";
|
||||
import { RawButton, type RawButtonProps } from "./RawButton";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import type { JSX } from "solid-js";
|
||||
import { AboutSection } from "../About/AboutSection";
|
||||
import { AccountMenu } from "../Account/AccountMenu";
|
||||
|
||||
export function SettingsContent(): JSX.Element {
|
||||
return (
|
||||
<vot-block style="max-width: 450px;display:block;margin:0 auto;">
|
||||
<AccountMenu />
|
||||
<AboutSection />
|
||||
</vot-block>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
@use "global.scss";
|
||||
|
||||
@use "About/AboutItem.scss";
|
||||
@use "About/AboutSection.scss";
|
||||
|
||||
@use "Account/AccountInfo.scss";
|
||||
@use "Account/AccountLogin.scss";
|
||||
@use "Account/AccountLogout.scss";
|
||||
|
|
|
|||
1
src/global.d.ts
vendored
1
src/global.d.ts
vendored
|
|
@ -35,7 +35,6 @@ declare global {
|
|||
// These are injected at runtime by the userscript manager or by our extension
|
||||
// prelude. GM is installed synchronously at document_start before any
|
||||
// content-script code evaluates.
|
||||
const GM_info: any;
|
||||
|
||||
const GM: {
|
||||
// Promise-based GM API (supported by most userscript managers).
|
||||
|
|
|
|||
21
src/stores/locale.ts
Normal file
21
src/stores/locale.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { createStore } from "solid-js/store";
|
||||
|
||||
export type LocaleStore = {
|
||||
updatedAt: number;
|
||||
hash: string;
|
||||
};
|
||||
|
||||
function createInitialState(): LocaleStore {
|
||||
return {
|
||||
updatedAt: 0,
|
||||
hash: "",
|
||||
};
|
||||
}
|
||||
|
||||
export const [locale, setLocale] = createStore<LocaleStore>(
|
||||
createInitialState(),
|
||||
);
|
||||
|
||||
export function resetLocale() {
|
||||
setLocale(createInitialState());
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
.vot-account {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&-container,
|
||||
&-wrapper,
|
||||
&-buttons {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
max-width: 36px;
|
||||
max-height: 36px;
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
overflow: hidden;
|
||||
&-img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,8 @@
|
|||
@include button.vot-button-layer-hover(0.04);
|
||||
@include button.vot-button-layer-active(0.16);
|
||||
|
||||
&[disabled="true"] {
|
||||
&[disabled="true"],
|
||||
&[aria-disabled="true"] {
|
||||
background-color: transparent;
|
||||
color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);
|
||||
cursor: initial;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
@use "components/dialog.scss";
|
||||
@use "components/loader.scss";
|
||||
@use "components/label.scss";
|
||||
@use "components/account.scss";
|
||||
@use "subtitles.scss";
|
||||
|
||||
:root {
|
||||
|
|
@ -58,8 +57,7 @@
|
|||
|
||||
--vot-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.14);
|
||||
--vot-shadow-2:
|
||||
0 2px 4px rgba(0, 0, 0, 0.22),
|
||||
0 12px 32px rgba(0, 0, 0, 0.22);
|
||||
0 2px 4px rgba(0, 0, 0, 0.22), 0 12px 32px rgba(0, 0, 0, 0.22);
|
||||
|
||||
--vot-duration-fast: 120ms;
|
||||
--vot-duration-medium: 200ms;
|
||||
|
|
|
|||
39
src/ui.ts
39
src/ui.ts
|
|
@ -40,14 +40,6 @@ function initKeyboardNavigationMode(): void {
|
|||
// Initialize once at module load.
|
||||
initKeyboardNavigationMode();
|
||||
|
||||
type HeaderLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
||||
|
||||
type InformationElements = {
|
||||
container: HTMLElement;
|
||||
header: HTMLElement;
|
||||
value: HTMLElement;
|
||||
};
|
||||
|
||||
type SubtitleInfoElements = {
|
||||
container: HTMLElement;
|
||||
translatedWith: HTMLElement;
|
||||
|
|
@ -151,33 +143,6 @@ const UI = {
|
|||
return el;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create header element
|
||||
*/
|
||||
createHeader(html: Node | string, level: HeaderLevel = 4): HTMLElement {
|
||||
return UI.createEl(
|
||||
"vot-block",
|
||||
["vot-header", `vot-header-level-${level}`],
|
||||
html,
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create information element
|
||||
*/
|
||||
createInformation(
|
||||
labelHtml: UiTemplate,
|
||||
valueHtml: UiTemplate,
|
||||
): InformationElements {
|
||||
const container = UI.createEl("vot-block", ["vot-info"]);
|
||||
const header = UI.createEl("vot-block");
|
||||
appendTemplate(labelHtml, header);
|
||||
const value = UI.createEl("vot-block");
|
||||
appendTemplate(valueHtml, value);
|
||||
container.append(header, value);
|
||||
return { container, header, value };
|
||||
},
|
||||
|
||||
/**
|
||||
* Create button
|
||||
*/
|
||||
|
|
@ -210,10 +175,6 @@ const UI = {
|
|||
return UI.createEl("vot-block", ["vot-inline-loader"]);
|
||||
},
|
||||
|
||||
createPortal(local: boolean = false): HTMLElement {
|
||||
return UI.createEl("vot-block", [`vot-portal${local ? "-local" : ""}`]);
|
||||
},
|
||||
|
||||
createSubtitleInfo(
|
||||
word: string,
|
||||
desc: string,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
type TMInfoScriptMeta = {
|
||||
author?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
const SETTINGS_EVENT_KEYS: Array<keyof SettingsViewEventMap> = [
|
||||
"click:bugReport",
|
||||
"click:resetSettings",
|
||||
|
|
@ -43,6 +39,7 @@ function createSettingsEvents(): {
|
|||
}
|
||||
|
||||
import { availableLangs } from "@vot.js/shared/consts";
|
||||
import { AboutSection } from "../../components/About/AboutSection";
|
||||
import { AccountMenu } from "../../components/Account/AccountMenu";
|
||||
import { Switch } from "../../components/Control/Switch";
|
||||
import {
|
||||
|
|
@ -60,12 +57,7 @@ import {
|
|||
type LangOverride,
|
||||
localizationProvider,
|
||||
} from "../../localization/localizationProvider";
|
||||
import {
|
||||
account,
|
||||
resetAccount,
|
||||
updateAccount,
|
||||
updateAccountFromStorage,
|
||||
} from "../../stores/account";
|
||||
import { account, resetAccount, updateAccount } from "../../stores/account";
|
||||
import {
|
||||
getGoogleSubtitleFontFamilyName,
|
||||
loadGoogleFontsCatalog,
|
||||
|
|
@ -101,7 +93,6 @@ import type {
|
|||
} from "../../types/views/settings";
|
||||
import ui from "../../ui";
|
||||
import debug from "../../utils/debug";
|
||||
import { getEnvironmentInfo } from "../../utils/environment";
|
||||
import { EventImpl } from "../../utils/eventImpl";
|
||||
import { isProxyOnlyExtension, isSupportGMXhr } from "../../utils/gm";
|
||||
import { votStorage } from "../../utils/storage";
|
||||
|
|
@ -282,6 +273,7 @@ export class SettingsView {
|
|||
buttonPositionTooltip?: Tooltip;
|
||||
menuLanguageSelectLabel?: Label;
|
||||
menuLanguageSelect?: Select<LangOverride>;
|
||||
aboutSection?: MountedComponent<HTMLDivElement>;
|
||||
bugReportButton?: HTMLElement;
|
||||
resetSettingsButton?: HTMLElement;
|
||||
constructor({ globalPortal, data = {}, videoHandler }: SettingsViewProps) {
|
||||
|
|
@ -578,71 +570,6 @@ export class SettingsView {
|
|||
return this.buildSubtitleFontItems(activeFontFamily, matchingGoogleFonts);
|
||||
}
|
||||
|
||||
private buildAboutSectionContent(aboutSection: {
|
||||
content: HTMLElement;
|
||||
}): void {
|
||||
const envInfo = getEnvironmentInfo();
|
||||
const safeGMInfo = typeof GM_info === "undefined" ? undefined : GM_info;
|
||||
const versionInfo = ui.createInformation(
|
||||
`${localizationProvider.get("VOTVersion")}:`,
|
||||
envInfo.scriptVersion === "unknown"
|
||||
? safeGMInfo?.script?.version || localizationProvider.get("notFound")
|
||||
: envInfo.scriptVersion,
|
||||
);
|
||||
const buildAuthors =
|
||||
typeof VOT_AUTHORS === "undefined" ? "" : String(VOT_AUTHORS);
|
||||
const authorsInfo = ui.createInformation(
|
||||
`${localizationProvider.get("VOTAuthors")}:`,
|
||||
(safeGMInfo?.script as TMInfoScriptMeta | undefined)?.author ||
|
||||
buildAuthors ||
|
||||
localizationProvider.get("notFound"),
|
||||
);
|
||||
const loaderInfo = ui.createInformation(
|
||||
`${localizationProvider.get("VOTLoader")}:`,
|
||||
envInfo.loader,
|
||||
);
|
||||
const userBrowserInfo = ui.createInformation(
|
||||
`${localizationProvider.get("VOTBrowser")}:`,
|
||||
`${envInfo.browser} (${envInfo.os})`,
|
||||
);
|
||||
const localeUpdatedAt = new Date(
|
||||
(this.data.localeUpdatedAt ?? 0) * 1000,
|
||||
).toLocaleString();
|
||||
const localeHashValue =
|
||||
this.data.localeHash ?? localizationProvider.get("notFound");
|
||||
// Solid: a thunk building the same two-line fragment. No reactive value is
|
||||
// involved (the strings are computed once per settings render), so a plain
|
||||
const localeInfoValue = () => {
|
||||
const fragment = document.createDocumentFragment();
|
||||
fragment.append(
|
||||
localeHashValue,
|
||||
document.createElement("br"),
|
||||
`(${localizationProvider.get("VOTUpdatedAt")} ${localeUpdatedAt})`,
|
||||
);
|
||||
return fragment;
|
||||
};
|
||||
const localeInfo = ui.createInformation(
|
||||
`${localizationProvider.get("VOTLocaleHash")}:`,
|
||||
localeInfoValue,
|
||||
);
|
||||
const updateLocaleFilesButton = ui.createOutlinedButton(
|
||||
localizationProvider.get("VOTUpdateLocaleFiles"),
|
||||
);
|
||||
updateLocaleFilesButton.addEventListener("click", async () => {
|
||||
await votStorage.set("localeHash", "");
|
||||
await localizationProvider.update(true);
|
||||
globalThis.location.reload();
|
||||
});
|
||||
aboutSection.content.append(
|
||||
versionInfo.container,
|
||||
authorsInfo.container,
|
||||
loaderInfo.container,
|
||||
userBrowserInfo.container,
|
||||
localeInfo.container,
|
||||
updateLocaleFilesButton,
|
||||
);
|
||||
}
|
||||
|
||||
initUI() {
|
||||
if (this.isInitialized()) {
|
||||
throw new Error("[VOT] SettingsView is already initialized");
|
||||
|
|
@ -1150,7 +1077,13 @@ export class SettingsView {
|
|||
this.buttonPositionSelect.container,
|
||||
this.menuLanguageSelect.container,
|
||||
);
|
||||
this.buildAboutSectionContent(aboutSection);
|
||||
this.aboutSection = mountComponent<HTMLDivElement>((rootRef) =>
|
||||
AboutSection({
|
||||
ref: rootRef,
|
||||
}),
|
||||
);
|
||||
|
||||
aboutSection.content.append(this.aboutSection.root);
|
||||
this.dialog.footerContainer.append(
|
||||
this.bugReportButton,
|
||||
this.resetSettingsButton,
|
||||
|
|
@ -1464,6 +1397,7 @@ export class SettingsView {
|
|||
"autoTranslateSwitch",
|
||||
"autoSubtitlesSwitch",
|
||||
"accountMenu",
|
||||
"aboutSection",
|
||||
] satisfies (keyof (typeof SettingsView)["prototype"])[]) {
|
||||
const control = this[key] as MountedComponent<any> | undefined;
|
||||
control?.dispose();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
proxyWorkerHost,
|
||||
} from "../../config/config";
|
||||
import { updateAccountFromStorage } from "../../stores/account";
|
||||
import { setLocale } from "../../stores/locale";
|
||||
import type { LanguageSelectKey } from "../../types/components/select";
|
||||
import debug from "../../utils/debug";
|
||||
import { GM_fetch, isProxyOnlyExtension, isSupportGMXhr } from "../../utils/gm";
|
||||
|
|
@ -103,6 +104,10 @@ export async function init(this: VideoHandler) {
|
|||
}
|
||||
|
||||
await updateAccountFromStorage();
|
||||
setLocale({
|
||||
updatedAt: this.data.localeUpdatedAt,
|
||||
hash: this.data.localeHash,
|
||||
});
|
||||
|
||||
try {
|
||||
if (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue