summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/web/boot.js
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
commit0e4a111f81cceed275d9bec2695f6e401fb654d8 (patch)
tree40874799472fa07416f17b50a398ac33b7771905 /packages/backend/src/server/web/boot.js
parentupdate deps (diff)
downloadmisskey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.gz
misskey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.bz2
misskey-0e4a111f81cceed275d9bec2695f6e401fb654d8.zip
refactoring
Resolve #7779
Diffstat (limited to 'packages/backend/src/server/web/boot.js')
-rw-r--r--packages/backend/src/server/web/boot.js166
1 files changed, 166 insertions, 0 deletions
diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
new file mode 100644
index 0000000000..d4a2529e63
--- /dev/null
+++ b/packages/backend/src/server/web/boot.js
@@ -0,0 +1,166 @@
+/**
+ * BOOT LOADER
+ * サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで、以下の役割を持ちます。
+ * - 翻訳ファイルをフェッチする。
+ * - バージョンに基づいて適切なメインスクリプトを読み込む。
+ * - キャッシュされたコンパイル済みテーマを適用する。
+ * - クライアントの設定値に基づいて対応するHTMLクラス等を設定する。
+ * テーマをこの段階で設定するのは、メインスクリプトが読み込まれる間もテーマを適用したいためです。
+ * 注: webpackは介さないため、このファイルではrequireやimportは使えません。
+ */
+
+'use strict';
+
+// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
+(async () => {
+ window.onerror = (e) => {
+ renderError('SOMETHING_HAPPENED', e.toString());
+ };
+ window.onunhandledrejection = (e) => {
+ renderError('SOMETHING_HAPPENED_IN_PROMISE', e.toString());
+ };
+
+ const v = localStorage.getItem('v') || VERSION;
+
+ //#region Detect language & fetch translations
+ const localeVersion = localStorage.getItem('localeVersion');
+ const localeOutdated = (localeVersion == null || localeVersion !== v);
+
+ if (!localStorage.hasOwnProperty('locale') || localeOutdated) {
+ const supportedLangs = LANGS;
+ 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';
+ }
+ }
+
+ const res = await fetch(`/assets/locales/${lang}.${v}.json`);
+ if (res.status === 200) {
+ localStorage.setItem('lang', lang);
+ localStorage.setItem('locale', await res.text());
+ localStorage.setItem('localeVersion', v);
+ } else if (localeOutdated) {
+ // nop
+ } else {
+ await checkUpdate();
+ renderError('LOCALE_FETCH_FAILED');
+ return;
+ }
+ }
+ //#endregion
+
+ //#region Script
+ const salt = localStorage.getItem('salt')
+ ? `?salt=${localStorage.getItem('salt')}`
+ : '';
+
+ const script = document.createElement('script');
+ script.setAttribute('src', `/assets/app.${v}.js${salt}`);
+ script.setAttribute('async', 'true');
+ script.setAttribute('defer', 'true');
+ script.addEventListener('error', async () => {
+ await checkUpdate();
+ renderError('APP_FETCH_FAILED');
+ });
+ document.head.appendChild(script);
+ //#endregion
+
+ //#region Theme
+ const theme = localStorage.getItem('theme');
+ if (theme) {
+ for (const [k, v] of Object.entries(JSON.parse(theme))) {
+ document.documentElement.style.setProperty(`--${k}`, v.toString());
+
+ // HTMLの theme-color 適用
+ if (k === 'htmlThemeColor') {
+ for (const tag of document.head.children) {
+ if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+ tag.setAttribute('content', v);
+ break;
+ }
+ }
+ }
+ }
+ }
+ //#endregion
+
+ const fontSize = localStorage.getItem('fontSize');
+ if (fontSize) {
+ document.documentElement.classList.add('f-' + fontSize);
+ }
+
+ const useSystemFont = localStorage.getItem('useSystemFont');
+ if (useSystemFont) {
+ document.documentElement.classList.add('useSystemFont');
+ }
+
+ const wallpaper = localStorage.getItem('wallpaper');
+ if (wallpaper) {
+ document.documentElement.style.backgroundImage = `url(${wallpaper})`;
+ }
+
+ const customCss = localStorage.getItem('customCss');
+ if (customCss && customCss.length > 0) {
+ const style = document.createElement('style');
+ style.innerHTML = customCss;
+ document.head.appendChild(style);
+ }
+
+ // eslint-disable-next-line no-inner-declarations
+ function renderError(code, details) {
+ document.documentElement.innerHTML = `
+ <h1>⚠エラーが発生しました</h1>
+ <p>問題が解決しない場合は管理者までお問い合わせください。以下のオプションを試すこともできます:</p>
+ <ul>
+ <li><a href="/cli">簡易クライアント</a>を起動</li>
+ <li><a href="/bios">BIOS</a>で修復を試みる</li>
+ <li><a href="/flush">キャッシュをクリア</a>する</li>
+ </ul>
+ <hr>
+ <code>ERROR CODE: ${code}</code>
+ <details>
+ ${details}
+ </details>
+ `;
+ }
+
+ // eslint-disable-next-line no-inner-declarations
+ async function checkUpdate() {
+ // TODO: サーバーが落ちている場合などのエラーハンドリング
+ const res = await fetch('/api/meta', {
+ method: 'POST',
+ cache: 'no-cache'
+ });
+
+ const meta = await res.json();
+
+ if (meta.version != v) {
+ localStorage.setItem('v', meta.version);
+ refresh();
+ }
+ }
+
+ // eslint-disable-next-line no-inner-declarations
+ function refresh() {
+ // Random
+ localStorage.setItem('salt', Math.random().toString().substr(2, 8));
+
+ // Clear cache (service worker)
+ try {
+ navigator.serviceWorker.controller.postMessage('clear');
+ navigator.serviceWorker.getRegistrations().then(registrations => {
+ registrations.forEach(registration => registration.unregister());
+ });
+ } catch (e) {
+ console.error(e);
+ }
+
+ location.reload();
+ }
+})();