diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index bc7b800d2..396536948 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -7,257 +7,17 @@
* BOOT LOADER
* サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで、以下の役割を持ちます。
* - 翻訳ファイルをフェッチする。
- * - 事前に挿入されたCLIENT_ENTRYを読んで適切なメインスクリプトを読み込む。
+ * - バージョンに基づいて適切なメインスクリプトを読み込む。
* - キャッシュされたコンパイル済みテーマを適用する。
* - クライアントの設定値に基づいて対応するHTMLクラス等を設定する。
- * - もしメインスクリプトの読み込みなどでエラーが発生した場合は、renderErrorでエラーを描画する。
* テーマをこの段階で設定するのは、メインスクリプトが読み込まれる間もテーマを適用したいためです。
+ * 注: webpackは介さないため、このファイルではrequireやimportは使えません。
*/
'use strict';
-var misskey_loader = new Set();
-
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
-function boot() {
- const defaultSolutions = [
- 'Clear the browser cache / ブラウザのキャッシュをクリアする',
- 'Update your os and browser / ブラウザおよびOSを最新バージョンに更新する',
- 'Disable an adblocker / アドブロッカーを無効にする',
- '(Tor Browser) Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する'
- ];
-
- const onErrorStyle = `
- * {
- font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
- }
-
- #misskey_app,
- #splash {
- display: none !important;
- }
-
- body,
- html {
- background-color: #222;
- color: #dfddcc;
- justify-content: center;
- margin: auto;
- padding: 10px;
- text-align: center;
- }
-
- button {
- border-radius: 999px;
- padding: 0px 12px 0px 12px;
- border: none;
- cursor: pointer;
- margin-bottom: 12px;
- }
-
- .button-big {
- background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
- line-height: 50px;
- }
-
- .button-big:hover {
- background: rgb(153, 204, 0);
- }
-
- .button-small {
- background: #444;
- line-height: 40px;
- }
-
- .button-small:hover {
- background: #555;
- }
-
- .button-label-big {
- color: #222;
- font-weight: bold;
- font-size: 1.2em;
- padding: 12px;
- }
-
- .button-label-small {
- color: rgb(153, 204, 0);
- font-size: 16px;
- padding: 12px;
- }
-
- a {
- color: rgb(134, 179, 0);
- text-decoration: none;
- }
-
- p,
- li {
- font-size: 16px;
- }
-
- .icon-warning {
- color: #dec340;
- height: 4rem;
- padding-top: 2rem;
- }
-
- h1 {
- font-size: 1.5em;
- margin: 1em;
- }
-
- summary {
- cursor: pointer;
- }
-
- code {
- font-family: Fira, FiraCode, monospace;
- }
-
- #errors {
- display: flex;
- flex-direction: column;
- align-items: center;
- }
-
- .errorInfo {
- background: #333;
- width: 40rem;
- max-width: 100%;
- border-radius: 10px;
- justify-content: center;
- padding: 1rem;
- margin-bottom: 1rem;
- box-sizing: border-box;
- }
-
- .errorInfo > pre {
- text-wrap: auto;
- text-wrap: balance;
- }
- `;
-
-
- const addStyle = (styleText) => {
- try {
- let css = document.createElement('style');
- css.appendChild(document.createTextNode(styleText));
- document.head.appendChild(css);
- } catch (e) {
- console.error(e);
- }
- }
-
- const renderError = (code, details, solutions = defaultSolutions) => {
- if (document.readyState === 'loading') {
- window.addEventListener('DOMContentLoaded', () => {
- renderError(code, details, solutions);
- });
- try {
- addStyle(onErrorStyle);
- } catch (e) { }
- return;
- }
-
- let errorsElement = document.getElementById('errors');
-
- if (!errorsElement) {
- // エラー描画用のビューになっていない場合は、エラー描画用のビューに切り替える
- document.body.innerHTML = `
-
-
Failed to load
読み込みに失敗しました
-
- The following actions may solve the problem. / 以下を行うと解決する可能性があります。
- ${solutions.map(x => `${x}
`).join('')}
-
- Other options / その他のオプション
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- errorsElement = document.getElementById('errors');
- }
-
- if (typeof details === 'string') {
- const errorEl = document.createElement('div');
- errorEl.classList.add('errorInfo');
-
- const titleCodeElement = document.createElement('code');
- titleCodeElement.textContent = `ERROR CODE: ${code}`;
- errorEl.appendChild(titleCodeElement);
-
- errorEl.appendChild(document.createElement('br'));
-
- const detailsCodeElement = document.createElement('code');
- detailsCodeElement.textContent = details;
- errorEl.appendChild(detailsCodeElement);
-
- errorsElement.appendChild(errorEl);
- } else if (details instanceof Error) {
- const errorEl = document.createElement('details');
- errorEl.classList.add('errorInfo');
-
- const summaryElement = document.createElement('summary');
- const titleCodeElement = document.createElement('code');
- titleCodeElement.textContent = `ERROR CODE: ${code}`;
- summaryElement.appendChild(titleCodeElement);
- errorEl.appendChild(summaryElement);
-
- const detailsPreElement = document.createElement('pre');
- const detailsMessageElement = document.createElement('code');
- detailsMessageElement.textContent = details.message;
- detailsPreElement.appendChild(detailsMessageElement);
- detailsPreElement.appendChild(document.createElement('br'));
- const detailsCodeElement = document.createElement('code');
- detailsCodeElement.textContent = details.stack;
- detailsPreElement.appendChild(detailsCodeElement);
- errorEl.appendChild(detailsPreElement);
-
- errorsElement.appendChild(errorEl);
- } else {
- const errorEl = document.createElement('details');
- errorEl.classList.add('errorInfo');
-
- const summaryElement = document.createElement('summary');
- const titleCodeElement = document.createElement('code');
- titleCodeElement.textContent = `ERROR CODE: ${code}`;
- summaryElement.appendChild(titleCodeElement);
- errorEl.appendChild(summaryElement);
-
- const detailsCodeElement = document.createElement('code');
- detailsCodeElement.textContent = JSON.stringify(details);
- errorEl.appendChild(detailsCodeElement);
-
- errorsElement.appendChild(errorEl);
- }
-
- addStyle(onErrorStyle);
- }
-
+(async () => {
window.onerror = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED', e);
@@ -270,65 +30,63 @@ function boot() {
let forceError = localStorage.getItem('forceError');
if (forceError != null) {
renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
- renderError('FORCED_ERROR', Error('This error is forced by having forceError in local storage.'));
- return;
}
- //#region After DOM loaded
- async function oncontentload() {
- const providedMetaEl = document.getElementById('misskey_meta');
- const meta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null;
- const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0;
- console.log('providedAt', providedAt, 'now', Date.now());
- if (providedAt < Date.now() - 1000 * 60 * 60 * 24) {
- // 古いデータがなぜか提供された場合は、エラーを描画する
- renderError(
- 'META_PROVIDED_AT_TOO_OLD',
- 'This view is too old. Please reload.',
- [
- 'Reload / リロードする',
- 'Clear the browser cache then reload / ブラウザのキャッシュをクリアしてリロードする',
- 'Disable an adblocker / アドブロッカーを無効にする',
- ]
- );
+ //#region Detect language & fetch translations
+ if (!localStorage.hasOwnProperty('locale')) {
+ 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 metaRes = await window.fetch('/api/meta', {
+ method: 'POST',
+ body: JSON.stringify({}),
+ credentials: 'omit',
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ if (metaRes.status !== 200) {
+ renderError('META_FETCH');
+ return;
+ }
+ const meta = await metaRes.json();
+ const v = meta.version;
+ if (v == null) {
+ renderError('META_FETCH_V');
return;
}
- //#region Detect language & fetch translations on first load
- if (!localStorage.hasOwnProperty('locale')) {
- 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 v = meta?.version;
-
- // for https://github.com/misskey-dev/misskey/issues/10202
- if (lang == null || lang.toString == null || lang.toString() === 'null') {
- console.warn('invalid lang value detected!!!', typeof lang, lang);
- lang = 'en-US';
- }
-
- const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
- if (localRes.status === 200) {
- localStorage.setItem('lang', lang);
- localStorage.setItem('locale', await localRes.text());
- localStorage.setItem('localeVersion', v);
- } else {
- renderError('LOCALE_FETCH');
- return;
- }
+ // 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 localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
+ if (localRes.status === 200) {
+ localStorage.setItem('lang', lang);
+ localStorage.setItem('locale', await localRes.text());
+ localStorage.setItem('localeVersion', v);
+ } else {
+ renderError('LOCALE_FETCH');
+ return;
+ }
+ }
+ //#endregion
+
+ //#region Script
+ async function importAppScript() {
await import(`/vite/${CLIENT_ENTRY}`)
.catch(async e => {
console.error(e);
@@ -336,11 +94,12 @@ function boot() {
});
}
+ // タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある
if (document.readyState !== 'loading') {
- misskey_loader.add(oncontentload());
+ importAppScript();
} else {
window.addEventListener('DOMContentLoaded', () => {
- misskey_loader.add(oncontentload());
+ importAppScript();
});
}
//#endregion
@@ -389,6 +148,172 @@ function boot() {
style.innerHTML = customCss;
document.head.appendChild(style);
}
-}
-boot();
+ async function addStyle(styleText) {
+ let css = document.createElement('style');
+ css.appendChild(document.createTextNode(styleText));
+ document.head.appendChild(css);
+ }
+
+ function renderError(code, details) {
+ let errorsElement = document.getElementById('errors');
+
+ if (!errorsElement) {
+ document.body.innerHTML = `
+
+ Failed to load
読み込みに失敗しました
+
+ The following actions may solve the problem. / 以下を行うと解決する可能性があります。
+ Clear the browser cache / ブラウザのキャッシュをクリアする
+ Update your os and browser / ブラウザおよびOSを最新バージョンに更新する
+ Disable an adblocker / アドブロッカーを無効にする
+ (Tor Browser) Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する
+
+ Other options / その他のオプション
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ errorsElement = document.getElementById('errors');
+ }
+ const detailsElement = document.createElement('details');
+ detailsElement.id = 'errorInfo';
+ detailsElement.innerHTML = `
+
+
+ ERROR CODE: ${code}
+
+ ${JSON.stringify(details)}
`;
+ errorsElement.appendChild(detailsElement);
+ addStyle(`
+ * {
+ font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
+ }
+
+ #misskey_app,
+ #splash {
+ display: none !important;
+ }
+
+ body,
+ html {
+ background-color: #222;
+ color: #dfddcc;
+ justify-content: center;
+ margin: auto;
+ padding: 10px;
+ text-align: center;
+ }
+
+ button {
+ border-radius: 999px;
+ padding: 0px 12px 0px 12px;
+ border: none;
+ cursor: pointer;
+ margin-bottom: 12px;
+ }
+
+ .button-big {
+ background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
+ line-height: 50px;
+ }
+
+ .button-big:hover {
+ background: rgb(153, 204, 0);
+ }
+
+ .button-small {
+ background: #444;
+ line-height: 40px;
+ }
+
+ .button-small:hover {
+ background: #555;
+ }
+
+ .button-label-big {
+ color: #222;
+ font-weight: bold;
+ font-size: 1.2em;
+ padding: 12px;
+ }
+
+ .button-label-small {
+ color: rgb(153, 204, 0);
+ font-size: 16px;
+ padding: 12px;
+ }
+
+ a {
+ color: rgb(134, 179, 0);
+ text-decoration: none;
+ }
+
+ p,
+ li {
+ font-size: 16px;
+ }
+
+ .icon-warning {
+ color: #dec340;
+ height: 4rem;
+ padding-top: 2rem;
+ }
+
+ h1 {
+ font-size: 1.5em;
+ margin: 1em;
+ }
+
+ code {
+ font-family: Fira, FiraCode, monospace;
+ }
+
+ #errorInfo {
+ background: #333;
+ margin-bottom: 2rem;
+ padding: 0.5rem 1rem;
+ width: 40rem;
+ border-radius: 10px;
+ justify-content: center;
+ margin: auto;
+ }
+
+ #errorInfo summary {
+ cursor: pointer;
+ }
+
+ #errorInfo summary > * {
+ display: inline;
+ }
+
+ @media screen and (max-width: 500px) {
+ #errorInfo {
+ width: 50%;
+ }
+ `)
+ }
+})();