logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/
commit: 00df9c9c32832feea80d6cd6d66c69fabacfab42
parent 8ee51229090ba323ceacf2cb8c6b50f2b1309560
Author: Henry Jameson <me@hjkos.com>
Date:   Mon, 16 Sep 2024 02:34:02 +0300

initial splashscreen implementation

Diffstat:

Mindex.html94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/App.js10++++++++++
Msrc/App.scss100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/boot/after_store.js6++++--
Msrc/i18n/en.json11+++++++++++
Msrc/main.js2++
Msrc/modules/interface.js12++++++------
Msrc/services/style_setter/style_setter.js17+++++++++++------
Astatic/pleromatan_apology_fox.png2++
9 files changed, 238 insertions(+), 16 deletions(-)

diff --git a/index.html b/index.html @@ -8,9 +8,99 @@ <style id="pleroma-lazy-styles" type="text/css"></style> <!--server-generated-meta--> </head> - <body class="hidden"> + <body style="margin: 0; padding: 0"> <noscript>To use Pleroma, please enable JavaScript.</noscript> - <div id="app"></div> + <!-- putting styles here to avoid having to wait for styles to load up --> + <div id="splash" style=" + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: #0f161e; + font-family: sans-serif; + color: #b9b9ba; + position: absolute; + " + > + <img + style=" + width: 30vh"; + margin-top: 1vh; + margin-bottom: 0.5vh; + " + src="/static/pleromatan_apology_fox.png" + /> + <div + id="throbber" + style=' + --logoChunkSize: 2vh; + display: grid; + margin-top: 2.5vh; + margin-bottom: 0.5vh; + width: 30vw; + grid-template-rows: repeat(8, var(--logoChunkSize)); + grid-template-columns: repeat(5, var(--logoChunkSize)); + grid-template-areas: "P P . L L" + "P P . L L" + "P P . L L" + "P P . L L" + "P P . . ." + "P P . . ." + "P P . E E" + "P P . E E"; + width: auto; + ' + > + <div + style=" + background-color: #e2b188; + grid-area: P; + border-top-left-radius: calc(var(--logoChunkSize) / 2); + box-shadow: 0.1vh 0.1vh 1vh 0 #e2b188; + " + > + </div> + <div + style=" + width: 100%; + height: 100%; + background-color: #e2b188; + grid-area: L; + border-bottom-right-radius: calc(var(--logoChunkSize) / 2); + box-shadow: 0.1vh 0.1vh 1vh 0 #e2b188; + " + > + </div> + <div + style=" + width: 100%; + height: 100%; + background-color: #e2b188; + grid-area: E; + border-bottom-right-radius: calc(var(--logoChunkSize) / 2); + box-shadow: 0.1vh 0.1vh 1vh 0 #e2b188; + " + > + </div> + </div> + <div + id="status" + class="css-ok" + style=" + margin-top: 3.5vh; + height: 4vh; + line-height: 4vh; + font-size: 4vh; + width: 100%; + text-align: center; + " + > + <span class="initial-text">(。>﹏<)</span> + </div> + </div> + <div id="app" class="hidden"></div> <div id="modal"></div> <!-- built files will be auto injected --> <div id="popovers" /> diff --git a/src/App.js b/src/App.js @@ -44,6 +44,13 @@ export default { data: () => ({ mobileActivePanel: 'timeline' }), + watch: { + themeApplied (value) { + document.querySelector('#app').classList.remove('hidden') + document.querySelector('#splash').className = 'hidden' + document.querySelector('#status').textContent = this.$t('splash.fun_' + Math.ceil(Math.random() * 4)) + } + }, created () { // Load the locale from the storage const val = this.$store.getters.mergedConfig.interfaceLanguage @@ -54,6 +61,9 @@ export default { window.removeEventListener('resize', this.updateMobileState) }, computed: { + themeApplied () { + return this.$store.state.interface.themeApplied + }, classes () { return [ { diff --git a/src/App.scss b/src/App.scss @@ -914,3 +914,103 @@ option { color: var(--selectionText); background-color: var(--selectionBackground); } + +#splash { + pointer-events: none; + transition: opacity 2s; + opacity: 1; + z-index: 9999999999999999999999999999; + + &.hidden { + opacity: 0; + } + + #status { + &.css-ok { + &::before { + display: inline-block; + content: "CSS OK"; + } + } + + .initial-text { + display: none; + } + } + + #throbber { + animation-duration: 2s; + animation-name: bounce; + animation-iteration-count: infinite; + animation-direction: normal; + transform-origin: bottom center; + + @keyframes bounce { + 0% { + scale: 1 1; + translate: 0 0; + animation-timing-function: ease-out; + } + + 10% { + scale: 1.2 0.8; + translate: 0 0; + animation-timing-function: ease-out; + } + + 30% { + scale: 0.9 1.1; + translate: 0 -40%; + animation-timing-function: ease-in; + } + + 40% { + scale: 1.1 0.9; + translate: 0 -50%; + animation-timing-function: ease-in; + } + + 45% { + scale: 0.9 1.1; + translate: 0 -45%; + animation-timing-function: ease-in; + } + + 50% { + scale: 1.05 0.95; + translate: 0 -40%; + animation-timing-function: ease-in; + } + + 55% { + scale: 0.985 1.025; + translate: 0 -35%; + animation-timing-function: ease-in; + } + + 60% { + scale: 1.0125 0.9985; + translate: 0 -30%; + animation-timing-function: ease-in; + } + + 80% { + scale: 1.0063 0.9938; + translate: 0 -10%; + animation-timing-function: ease-in-ou; + } + + 90% { + scale: 1.2 0.8; + translate: 0 0; + animation-timing-function: ease-out; + } + + 100% { + scale: 1 1; + translate: 0 0; + animation-timing-function: ease-out; + } + } + } +} diff --git a/src/boot/after_store.js b/src/boot/after_store.js @@ -352,10 +352,12 @@ const afterStoreSetup = async ({ store, i18n }) => { await setConfig({ store }) await store.dispatch('setTheme') - applyConfig(store.state.config) + document.querySelector('#status').textContent = i18n.global.t('splash.theme') + applyConfig(store.state.config, i18n.global) // 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 }), @@ -395,9 +397,9 @@ 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 @@ -1401,5 +1401,16 @@ }, "unicode_domain_indicator": { "tooltip": "This domain contains non-ascii characters." + }, + "splash": { + "loading": "Loading...", + "theme": "Applying theme, please wait warmly...", + "instance": "Getting instance info...", + "splines": "Reticulating splines...", + "almost": "Almost there!", + "fun_1": "Drink more water!", + "fun_2": "Take it easy!", + "fun_3": "Suya..", + "fun_4": "#cofe", } } diff --git a/src/main.js b/src/main.js @@ -67,6 +67,8 @@ const persistedStateOptions = { console.error(e) storageError = true } + document.querySelector('#status').removeAttribute('class') + document.querySelector('#status').textContent = i18n.global.t('splash.loading') const store = createStore({ modules: { i18n: { diff --git a/src/modules/interface.js b/src/modules/interface.js @@ -56,9 +56,6 @@ const interfaceMod = { state.temporaryChangesConfirm = () => {} state.temporaryChangesRevert = () => {} }, - setThemeApplied (state) { - state.themeApplied = true - }, setNotificationPermission (state, permission) { state.notificationPermission = permission }, @@ -120,6 +117,9 @@ const interfaceMod = { setPageTitle ({ rootState }, option = '') { document.title = `${option} ${rootState.instance.name}` }, + setThemeApplied ({ state, rootGetters }) { + state.themeApplied = true + }, settingsSaved ({ commit, dispatch }, { success, error }) { commit('settingsSaved', { success, error }) }, @@ -212,7 +212,7 @@ const interfaceMod = { setLastTimeline ({ commit }, value) { commit('setLastTimeline', value) }, - setTheme ({ commit, rootState }, { themeName, themeData, recompile, saveData } = {}) { + setTheme ({ dispatch, commit, rootState }, { themeName, themeData, recompile, saveData } = {}) { const { theme: instanceThemeName } = rootState.instance @@ -258,7 +258,7 @@ const interfaceMod = { // If we're not not forced to recompile try using // cache (tryLoadCache return true if load successful) if (!forceRecompile && !themeDebug && tryLoadCache()) { - commit('setThemeApplied') + dispatch('setThemeApplied') return } @@ -342,7 +342,7 @@ const interfaceMod = { applyTheme( ruleset, - () => commit('setThemeApplied'), + () => dispatch('setThemeApplied'), themeDebug ) }) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js @@ -169,7 +169,16 @@ export const applyTheme = async (input, onFinish = (data) => {}, debug) => { adoptStyleSheets([eagerStyles, lazyStyles]) const cache = { engineChecksum: getEngineChecksum(), data: [eagerStyles.rules, lazyStyles.rules] } onFinish(cache) - localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache)) + try { + localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache)) + } catch (e) { + localStorage.removeItem('pleroma-fe-theme-cache') + try { + localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache)) + } catch (e) { + console.warn('cannot save cache!', e) + } + } } }, debug @@ -222,7 +231,7 @@ const extractStyleConfig = ({ const defaultStyleConfig = extractStyleConfig(defaultState) -export const applyConfig = (input) => { +export const applyConfig = (input, i18n) => { const config = extractStyleConfig(input) if (config === defaultStyleConfig) { @@ -230,8 +239,6 @@ export const applyConfig = (input) => { } const head = document.head - const body = document.body - body.classList.add('hidden') const rules = Object .entries(config) @@ -252,8 +259,6 @@ export const applyConfig = (input) => { --roundness: var(--forcedRoundness) !important; }`, 'index-max') } - - body.classList.remove('hidden') } export const getThemes = () => { diff --git a/static/pleromatan_apology_fox.png b/static/pleromatan_apology_fox.png @@ -0,0 +1 @@ +/home/bocchi/Repos/Mine/pleroma-fe/src/assets/pleromatan_apology_fox.png +\ No newline at end of file