chore: 共通してるロジックをいくつか移動

This commit is contained in:
anatawa12 2025-08-13 01:21:03 +09:00
parent 0ec4f87ef8
commit d97547da05
No known key found for this signature in database
GPG Key ID: 9CA909848B8E4EA6
4 changed files with 104 additions and 125 deletions

View File

@ -5,22 +5,24 @@
'use strict';
import { addStyle, bootloaderLocales, detectLanguage } from '@@/js/bootloader';
interface Window {
CLIENT_ENTRY: string | undefined;
}
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
(async () => {
window.onerror = (e) => {
console.error(e);
window.onerror = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED');
};
window.onunhandledrejection = (e) => {
console.error(e);
window.onunhandledrejection = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED_IN_PROMISE');
};
let forceError = localStorage.getItem('forceError');
const forceError = localStorage.getItem('forceError');
if (forceError != null) {
renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.');
return;
@ -35,33 +37,13 @@ interface Window {
document.documentElement.classList.add('noborder');
}
//#region Detect language & fetch translations
const supportedLangs = _LANG_IDS_;
/** @type { string } */
let lang = localStorage.getItem('lang');
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback
if (lang == null) lang = 'en-US';
}
}
// for https://github.com/misskey-dev/misskey/issues/10202
if (lang == null || lang.toString == null || lang.toString() === 'null') {
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
//#endregion
const lang = detectLanguage();
//#region Script
async function importAppScript() {
await import(`/embed_vite/${(window.CLIENT_ENTRY ?? 'src/boot.ts').replace('scripts', lang)}`)
.catch(async e => {
console.error(e);
.catch(async error => {
console.error(error);
renderError('APP_IMPORT');
});
}
@ -76,44 +58,19 @@ interface Window {
}
//#endregion
async function addStyle(styleText) {
let css = document.createElement('style');
css.appendChild(document.createTextNode(styleText));
document.head.appendChild(css);
}
async function renderError(code) {
async function renderError(code: string, _details?) {
// Cannot set property 'innerHTML' of null を回避
if (document.readyState === 'loading') {
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
let messages = null;
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
}
if (!messages) messages = {};
const title = messages?.title || 'Failed to initialize Misskey';
const reload = messages?.reload || 'Reload';
const messages = bootloaderLocales();
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
<div class="message">${title}</div>
<div class="message">${messages.title}</div>
<div class="submessage">Error Code: ${code}</div>
<button onclick="location.reload(!0)">
<div>${reload}</div>
<div>${messages.reload}</div>
</button>`;
addStyle(`
#misskey_app,

View File

@ -7,6 +7,7 @@
type FIXME = any;
declare const _LANGS_: string[][];
declare const _LANG_IDS_: string[];
declare const _VERSION_: string;
declare const _ENV_: string;
declare const _DEV_: boolean;

View File

@ -0,0 +1,74 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
// common portion of bootloader for frontend and frontend-embed
import type { Locale } from '../../../locales/index.js';
export function detectLanguage(): string {
const supportedLangs = _LANG_IDS_;
let lang: string | null | undefined = localStorage.getItem('lang');
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback
lang ??= 'en-US';
}
}
// for https://github.com/misskey-dev/misskey/issues/10202
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (lang == null || lang.toString == null || lang.toString() === 'null') {
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
return lang;
}
type BootLoaderLocales = Locale['_bootErrors'] & Pick<Locale, 'reload'>;
export function bootloaderLocales(): BootLoaderLocales {
let messages: Partial<BootLoaderLocales> | null = null;
const bootloaderLocalesJson = localStorage.getItem('bootloaderLocales');
if (bootloaderLocalesJson) {
messages = JSON.parse(bootloaderLocalesJson);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
}
return Object.assign({
title: 'Failed to initialize Misskey',
solution: 'The following actions may solve the problem.',
solution1: 'Update your os and browser',
solution2: 'Disable an adblocker',
solution3: 'Clear the browser cache',
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
otherOption: 'Other options',
otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool',
otherOption4: 'Start Misskey in safe mode',
reload: 'Reload',
}, messages) as BootLoaderLocales;
}
export function addStyle(styleText: string) {
const styleElement = document.createElement('style');
styleElement.appendChild(document.createTextNode(styleText));
document.head.appendChild(styleElement);
}

View File

@ -3,8 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint no-restricted-globals: off */
'use strict';
import { addStyle, bootloaderLocales, detectLanguage } from '@@/js/bootloader';
interface Window {
CLIENT_ENTRY: string | undefined;
}
@ -13,42 +17,22 @@ interface Window {
(async () => {
const CLIENT_ENTRY = window.CLIENT_ENTRY;
window.onerror = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED', e);
window.onerror = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED', error);
};
window.onunhandledrejection = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
window.onunhandledrejection = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED_IN_PROMISE', error);
};
let forceError = localStorage.getItem('forceError');
const forceError = localStorage.getItem('forceError');
if (forceError != null) {
renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.');
return;
}
//#region Detect language
const supportedLangs = _LANG_IDS_;
/** @type { string } */
let lang = localStorage.getItem('lang');
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback
if (lang == null) lang = 'en-US';
}
}
// for https://github.com/misskey-dev/misskey/issues/10202
if (lang == null || lang.toString == null || lang.toString() === 'null') {
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
//#endregion
const lang = detectLanguage();
//#region Script
async function importAppScript() {
@ -84,7 +68,7 @@ interface Window {
if (!isSafeMode) {
const theme = localStorage.getItem('theme');
if (theme) {
for (const [k, v] of Object.entries(JSON.parse(theme))) {
for (const [k, v] of Object.entries(JSON.parse(theme) as Record<string, string>)) {
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
// HTMLの theme-color 適用
@ -125,50 +109,13 @@ interface Window {
}
}
async function addStyle(styleText) {
let css = document.createElement('style');
css.appendChild(document.createTextNode(styleText));
document.head.appendChild(css);
}
async function renderError(code, details) {
// Cannot set property 'innerHTML' of null を回避
if (document.readyState === 'loading') {
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
let messages = null;
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
}
if (!messages) messages = {};
messages = Object.assign({
title: 'Failed to initialize Misskey',
solution: 'The following actions may solve the problem.',
solution1: 'Update your os and browser',
solution2: 'Disable an adblocker',
solution3: 'Clear the browser cache',
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
otherOption: 'Other options',
otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool',
otherOption4: 'Start Misskey in safe mode',
reload: 'Reload',
}, messages);
const messages = bootloaderLocales();
const safeModeUrl = new URL(window.location.href);
safeModeUrl.searchParams.set('safemode', 'true');
@ -220,7 +167,7 @@ interface Window {
<br>
<div id="errors"></div>
`;
errorsElement = document.getElementById('errors');
errorsElement = document.getElementById('errors')!;
}
const detailsElement = document.createElement('details');
detailsElement.id = 'errorInfo';