diff --git a/changelog.d/splashfix.skip b/changelog.d/splashfix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/index.html b/index.html index 461f5acc1..66eec0777 100644 --- a/index.html +++ b/index.html @@ -70,6 +70,8 @@ "P P . . ." "P P . E E" "P P . E E"; + + --logoChunkSize: calc(2em * 0.5 * var(--scale)) } .chunk { @@ -99,6 +101,27 @@ text-align: center; } + #statusError { + display: none; + margin-top: 1em; + font-size: calc(1vw + 1vh + 1vmin); + line-height: 2; + width: 100%; + text-align: center; + } + + #statusStack { + display: none; + margin-top: 1em; + font-size: calc((1vw + 1vh + 1vmin) / 2.5); + width: calc(100vw - 5em); + padding: 1em; + text-overflow: ellipsis; + overflow-x: hidden; + text-align: left; + line-height: 2; + } + @media (prefers-reduced-motion) { #throbber { animation: none !important; @@ -133,6 +156,8 @@ + +

       
     
     
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index b13049897..65a151785 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -349,12 +349,11 @@ const afterStoreSetup = async ({ store, i18n }) => {
   const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
   store.dispatch('setInstanceOption', { name: 'server', value: server })
 
-  document.querySelector('#status').textContent = i18n.global.t('splash.settings')
   await setConfig({ store })
-  document.querySelector('#status').textContent = i18n.global.t('splash.theme')
   try {
     await store.dispatch('applyTheme').catch((e) => { console.error('Error setting theme', e) })
   } catch (e) {
+    window.splashError(e)
     return Promise.reject(e)
   }
 
@@ -362,7 +361,6 @@ const afterStoreSetup = async ({ store, i18n }) => {
 
   // Now we can try getting the server settings and logging in
   // Most of these are preloaded into the index.html so blocking is minimized
-  document.querySelector('#status').textContent = i18n.global.t('splash.instance')
   await Promise.all([
     checkOAuthToken({ store }),
     getInstancePanel({ store }),
@@ -409,7 +407,6 @@ const afterStoreSetup = async ({ store, i18n }) => {
 
   // remove after vue 3.3
   app.config.unwrapInjectedRef = true
-  document.querySelector('#status').textContent = i18n.global.t('splash.almost')
 
   app.mount('#app')
   return app
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 7febbf9f0..5ec6b731d 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1494,9 +1494,6 @@
   "splash": {
     "loading": "Loading...",
     "theme": "Applying theme, please wait warmly...",
-    "instance": "Getting instance info...",
-    "settings": "Applying settings...",
-    "almost": "Reticulating splines...",
     "fun_1": "Drink more water",
     "fun_2": "Take it easy!",
     "fun_3": "Suya...",
diff --git a/src/main.js b/src/main.js
index bcfc80b0e..eee463cc7 100644
--- a/src/main.js
+++ b/src/main.js
@@ -68,6 +68,17 @@ const persistedStateOptions = {
     throbber.classList.add('dead')
     document.querySelector('#status').textContent = i18n.global.t('splash.error')
     console.error('PleromaFE failed to initialize: ', e)
+    document.querySelector('#statusError').textContent = e
+    document.querySelector('#statusStack').textContent = e.stack
+    document.querySelector('#statusError').style = 'display: block'
+    document.querySelector('#statusStack').style = 'display: block'
+  }
+
+  window.splashError = e => splashError(i18n, e)
+  window.splashUpdate = key => {
+    if (document.querySelector('#status')) {
+      document.querySelector('#status').textContent = i18n.global.t(key)
+    }
   }
 
   try {
diff --git a/src/modules/interface.js b/src/modules/interface.js
index 442b482a0..cd06e429e 100644
--- a/src/modules/interface.js
+++ b/src/modules/interface.js
@@ -563,112 +563,118 @@ const interfaceMod = {
       if (!forceRecompile && !themeDebug && await tryLoadCache()) {
         return commit('setThemeApplied')
       }
+      window.splashUpdate('splash.theme')
       await dispatch('getThemeData')
 
-      const paletteIss = (() => {
-        if (!state.paletteDataUsed) return null
-        const result = {
-          component: 'Root',
-          directives: {}
-        }
+      try {
+        const paletteIss = (() => {
+          if (!state.paletteDataUsed) return null
+          const result = {
+            component: 'Root',
+            directives: {}
+          }
 
-        Object
-          .entries(state.paletteDataUsed)
-          .filter(([k]) => k !== 'name')
-          .forEach(([k, v]) => {
-            let issRootDirectiveName
-            switch (k) {
-              case 'background':
-                issRootDirectiveName = 'bg'
-                break
-              case 'foreground':
-                issRootDirectiveName = 'fg'
-                break
-              default:
-                issRootDirectiveName = k
-            }
-            result.directives['--' + issRootDirectiveName] = 'color | ' + v
-          })
-        return result
-      })()
-
-      const theme2ruleset = state.themeDataUsed && convertTheme2To3(normalizeThemeData(state.themeDataUsed))
-      const hacks = []
-
-      Object.entries(theme3hacks).forEach(([key, value]) => {
-        switch (key) {
-          case 'fonts': {
-            Object.entries(theme3hacks.fonts).forEach(([fontKey, font]) => {
-              if (!font?.family) return
-              switch (fontKey) {
-                case 'interface':
-                  hacks.push({
-                    component: 'Root',
-                    directives: {
-                      '--font': 'generic | ' + font.family
-                    }
-                  })
+          Object
+            .entries(state.paletteDataUsed)
+            .filter(([k]) => k !== 'name')
+            .forEach(([k, v]) => {
+              let issRootDirectiveName
+              switch (k) {
+                case 'background':
+                  issRootDirectiveName = 'bg'
                   break
-                case 'input':
-                  hacks.push({
-                    component: 'Input',
-                    directives: {
-                      '--font': 'generic | ' + font.family
-                    }
-                  })
-                  break
-                case 'post':
-                  hacks.push({
-                    component: 'RichContent',
-                    directives: {
-                      '--font': 'generic | ' + font.family
-                    }
-                  })
-                  break
-                case 'monospace':
-                  hacks.push({
-                    component: 'Root',
-                    directives: {
-                      '--monoFont': 'generic | ' + font.family
-                    }
-                  })
+                case 'foreground':
+                  issRootDirectiveName = 'fg'
                   break
+                default:
+                  issRootDirectiveName = k
               }
+              result.directives['--' + issRootDirectiveName] = 'color | ' + v
             })
-            break
-          }
-          case 'underlay': {
-            if (value !== 'none') {
-              const newRule = {
-                component: 'Underlay',
-                directives: {}
-              }
-              if (value === 'opaque') {
-                newRule.directives.opacity = 1
-                newRule.directives.background = '--wallpaper'
-              }
-              if (value === 'transparent') {
-                newRule.directives.opacity = 0
-              }
-              hacks.push(newRule)
+          return result
+        })()
+
+        const theme2ruleset = state.themeDataUsed && convertTheme2To3(normalizeThemeData(state.themeDataUsed))
+        const hacks = []
+
+        Object.entries(theme3hacks).forEach(([key, value]) => {
+          switch (key) {
+            case 'fonts': {
+              Object.entries(theme3hacks.fonts).forEach(([fontKey, font]) => {
+                if (!font?.family) return
+                switch (fontKey) {
+                  case 'interface':
+                    hacks.push({
+                      component: 'Root',
+                      directives: {
+                        '--font': 'generic | ' + font.family
+                      }
+                    })
+                    break
+                  case 'input':
+                    hacks.push({
+                      component: 'Input',
+                      directives: {
+                        '--font': 'generic | ' + font.family
+                      }
+                    })
+                    break
+                  case 'post':
+                    hacks.push({
+                      component: 'RichContent',
+                      directives: {
+                        '--font': 'generic | ' + font.family
+                      }
+                    })
+                    break
+                  case 'monospace':
+                    hacks.push({
+                      component: 'Root',
+                      directives: {
+                        '--monoFont': 'generic | ' + font.family
+                      }
+                    })
+                    break
+                }
+              })
+              break
+            }
+            case 'underlay': {
+              if (value !== 'none') {
+                const newRule = {
+                  component: 'Underlay',
+                  directives: {}
+                }
+                if (value === 'opaque') {
+                  newRule.directives.opacity = 1
+                  newRule.directives.background = '--wallpaper'
+                }
+                if (value === 'transparent') {
+                  newRule.directives.opacity = 0
+                }
+                hacks.push(newRule)
+              }
+              break
             }
-            break
           }
-        }
-      })
+        })
 
-      const rulesetArray = [
-        theme2ruleset,
-        state.styleDataUsed,
-        paletteIss,
-        hacks
-      ].filter(x => x)
+        const rulesetArray = [
+          theme2ruleset,
+          state.styleDataUsed,
+          paletteIss,
+          hacks
+        ].filter(x => x)
 
-      return applyTheme(
-        rulesetArray.flat(),
-        () => commit('setThemeApplied'),
-        themeDebug
-      )
+        return applyTheme(
+          rulesetArray.flat(),
+          () => commit('setThemeApplied'),
+          () => {},
+          themeDebug
+        )
+      } catch (e) {
+        window.splashError(e)
+      }
     }
   }
 }
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index b5014ff17..3791c6f34 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -118,7 +118,12 @@ export const tryLoadCache = async () => {
   }
 }
 
-export const applyTheme = (input, onFinish = (data) => {}, debug) => {
+export const applyTheme = (
+  input,
+  onEagerFinish = data => {},
+  onFinish = data => {},
+  debug
+) => {
   const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
   const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
 
@@ -148,6 +153,7 @@ export const applyTheme = (input, onFinish = (data) => {}, debug) => {
       },
       onEagerFinished () {
         adoptStyleSheets([eagerStyles])
+        onEagerFinish()
       },
       onLazyFinished () {
         adoptStyleSheets([eagerStyles, lazyStyles])