diff --git a/index.html b/index.html index 474c0244..6588a287 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,101 @@ + @@ -11,95 +106,23 @@ -
- -
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+ + (。>﹏<)
-
-
-
-
- - (。>﹏<)
diff --git a/src/App.scss b/src/App.scss index 408ba402..5e7d3656 100644 --- a/src/App.scss +++ b/src/App.scss @@ -919,7 +919,6 @@ option { pointer-events: none; transition: opacity 2s; opacity: 1; - z-index: 9999999999999999999999999999; &.hidden { opacity: 0; @@ -938,13 +937,80 @@ option { } } + #mascot-container { + perspective: 60em; + perspective-origin: 0 -15em; + transform-style: preserve-3d; + } + #throbber { - animation-duration: 2s; + animation-duration: 3s; animation-name: bounce; animation-iteration-count: infinite; animation-direction: normal; transform-origin: bottom center; + --defaultY: 0; + + &.dead { + animation-name: dead; + animation-duration: 3s; + // animation-iteration-count: 1; + animation-iteration-count: 1; + transform: rotateX(90deg) rotateY(0) rotateZ(-45deg); + } + + @keyframes dead { + 0% { + transform: rotateX(0) rotateY(0) rotateZ(0); + } + + 5% { + transform: rotateX(0) rotateY(0) rotateZ(1deg); + } + + 10% { + transform: rotateX(0) rotateY(0) rotateZ(-2deg); + } + + 15% { + transform: rotateX(0) rotateY(0) rotateZ(3deg); + } + + 20% { + transform: rotateX(0) rotateY(0) rotateZ(0); + } + + 25% { + transform: rotateX(0) rotateY(0) rotateZ(0); + } + + 30% { + transform: rotateX(10deg) rotateY(0) rotateZ(0); + } + + 35% { + transform: rotateX(-10deg) rotateY(0) rotateZ(0); + } + + 40% { + transform: rotateX(10deg) rotateY(0) rotateZ(0); + } + + 45% { + transform: rotateX(-10deg) rotateY(0) rotateZ(0); + } + + 50% { + transform: rotateX(10deg) rotateY(0) rotateZ(0); + } + + 100% { + transform: rotateX(90deg) rotateY(0) rotateZ(-45deg); + transition-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); /* easeInQuint */ + } + } + @keyframes bounce { 0% { scale: 1 1; @@ -955,24 +1021,28 @@ option { 10% { scale: 1.2 0.8; translate: 0 0; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-out; } 30% { scale: 0.9 1.1; translate: 0 -40%; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-in; } 40% { scale: 1.1 0.9; translate: 0 -50%; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-in; } 45% { scale: 0.9 1.1; translate: 0 -45%; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-in; } @@ -985,30 +1055,35 @@ option { 55% { scale: 0.985 1.025; translate: 0 -35%; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-in; } 60% { scale: 1.0125 0.9985; translate: 0 -30%; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-in; } 80% { scale: 1.0063 0.9938; translate: 0 -10%; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-in-ou; } 90% { scale: 1.2 0.8; translate: 0 0; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-out; } 100% { scale: 1 1; translate: 0 0; + transform: rotateZ(var(--defaultZ)); animation-timing-function: ease-out; } } diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 6691ff3e..356b09fa 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -327,11 +327,7 @@ const setConfig = async ({ store }) => { const checkOAuthToken = async ({ store }) => { if (store.getters.getUserToken()) { - try { - await store.dispatch('loginUser', store.getters.getUserToken()) - } catch (e) { - console.error(e) - } + return store.dispatch('loginUser', store.getters.getUserToken()) } return Promise.resolve() } @@ -349,10 +345,15 @@ 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 }) - await store.dispatch('setTheme') - document.querySelector('#status').textContent = i18n.global.t('splash.theme') + try { + await store.dispatch('setTheme').catch((e) => { console.log(e) }) + } catch (e) { + return Promise.reject(e) + } + applyConfig(store.state.config, i18n.global) // Now we can try getting the server settings and logging in @@ -363,7 +364,7 @@ const afterStoreSetup = async ({ store, i18n }) => { getInstancePanel({ store }), getNodeInfo({ store }), getInstanceConfig({ store }) - ]) + ]).catch(e => Promise.reject(e)) // Start fetching things that don't need to block the UI store.dispatch('fetchMutes') diff --git a/src/i18n/en.json b/src/i18n/en.json index 0d4609b5..8d6a9bfe 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1406,11 +1406,12 @@ "loading": "Loading...", "theme": "Applying theme, please wait warmly...", "instance": "Getting instance info...", - "splines": "Reticulating splines...", - "almost": "Almost there!", - "fun_1": "Drink more water!", + "settings": "Applying settings...", + "almost": "Reticulating splines...", + "fun_1": "Drink more water", "fun_2": "Take it easy!", - "fun_3": "Suya..", - "fun_4": "#cofe" + "fun_3": "Suya...", + "fun_4": "My Pleroma machine is full power!", + "error": "Something went wrong" } } diff --git a/src/main.js b/src/main.js index 580c5005..40347e6d 100644 --- a/src/main.js +++ b/src/main.js @@ -48,6 +48,16 @@ const i18n = createI18n({ messages.setLanguage(i18n.global, currentLocale) +const splashError = (i18n, e) => { + document.querySelector('#mascot').src = (Math.floor(Math.random() * 2) > 0) + ? '/static/pleromatan_orz_fox.png' + : '/static/pleromatan_orz.png' + document.querySelector('#mascot').classList.add('orz') + document.querySelector('#throbber').classList.add('dead') + document.querySelector('#status').textContent = i18n.global.t('splash.error') + console.error('PleromaFE failed to initialize: ', e) +} + const persistedStateOptions = { paths: [ 'serverSideStorage.cache', @@ -58,57 +68,61 @@ const persistedStateOptions = { }; (async () => { - let storageError = false - const plugins = [pushNotifications] try { - const persistedState = await createPersistedState(persistedStateOptions) - plugins.push(persistedState) - } catch (e) { - console.error(e) - storageError = true - } - document.querySelector('#status').removeAttribute('class') - document.querySelector('#status').textContent = i18n.global.t('splash.loading') - const store = createStore({ - modules: { - i18n: { - getters: { - i18n: () => i18n.global - } + let storageError + const plugins = [pushNotifications] + try { + const persistedState = await createPersistedState(persistedStateOptions) + plugins.push(persistedState) + } catch (e) { + console.error('Storage error', e) + storageError = e + } + document.querySelector('#status').removeAttribute('class') + document.querySelector('#status').textContent = i18n.global.t('splash.loading') + const store = createStore({ + modules: { + i18n: { + getters: { + i18n: () => i18n.global + } + }, + interface: interfaceModule, + instance: instanceModule, + // TODO refactor users/statuses modules, they depend on each other + users: usersModule, + statuses: statusesModule, + notifications: notificationsModule, + lists: listsModule, + api: apiModule, + config: configModule, + profileConfig: profileConfigModule, + serverSideStorage: serverSideStorageModule, + adminSettings: adminSettingsModule, + shout: shoutModule, + oauth: oauthModule, + authFlow: authFlowModule, + mediaViewer: mediaViewerModule, + oauthTokens: oauthTokensModule, + reports: reportsModule, + polls: pollsModule, + postStatus: postStatusModule, + editStatus: editStatusModule, + statusHistory: statusHistoryModule, + chats: chatsModule, + announcements: announcementsModule }, - interface: interfaceModule, - instance: instanceModule, - // TODO refactor users/statuses modules, they depend on each other - users: usersModule, - statuses: statusesModule, - notifications: notificationsModule, - lists: listsModule, - api: apiModule, - config: configModule, - profileConfig: profileConfigModule, - serverSideStorage: serverSideStorageModule, - adminSettings: adminSettingsModule, - shout: shoutModule, - oauth: oauthModule, - authFlow: authFlowModule, - mediaViewer: mediaViewerModule, - oauthTokens: oauthTokensModule, - reports: reportsModule, - polls: pollsModule, - postStatus: postStatusModule, - editStatus: editStatusModule, - statusHistory: statusHistoryModule, - chats: chatsModule, - announcements: announcementsModule - }, - plugins, - strict: false // Socket modifies itself, let's ignore this for now. - // strict: process.env.NODE_ENV !== 'production' - }) - if (storageError) { - store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) + plugins, + strict: false // Socket modifies itself, let's ignore this for now. + // strict: process.env.NODE_ENV !== 'production' + }) + if (storageError) { + store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) + } + return await afterStoreSetup({ store, i18n }) + } catch (e) { + splashError(i18n, e) } - afterStoreSetup({ store, i18n }) })() // These are inlined by webpack's DefinePlugin diff --git a/src/modules/interface.js b/src/modules/interface.js index 3f180cf5..68b51015 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -230,27 +230,27 @@ const interfaceMod = { const forceRecompile = forceThemeRecompilation || recompile - let promise = null + let result = null if (themeData) { - promise = Promise.resolve(normalizeThemeData(themeData)) + result = normalizeThemeData(themeData) } else if (themeName) { - promise = getPreset(themeName).then(themeData => normalizeThemeData(themeData)) + result = normalizeThemeData(getPreset(themeName)) + .then(themeData => normalizeThemeData(themeData)) } else if (userThemeSource || userThemeSnapshot) { - promise = Promise.resolve(normalizeThemeData({ + result = normalizeThemeData({ _pleroma_theme_version: 2, theme: userThemeSnapshot, source: userThemeSource - })) - } else if (actualThemeName && actualThemeName !== 'custom') { - promise = getPreset(actualThemeName).then(themeData => { - const realThemeData = normalizeThemeData(themeData) - if (actualThemeName === instanceThemeName) { - // This sole line is the reason why this whole block is above the recompilation check - commit('setInstanceOption', { name: 'themeData', value: { theme: realThemeData } }) - } - return realThemeData }) + } else if (actualThemeName && actualThemeName !== 'custom') { + const themeData = actualThemeName + const realThemeData = normalizeThemeData(themeData) + if (actualThemeName === instanceThemeName) { + // This sole line is the reason why this whole block is above the recompilation check + commit('setInstanceOption', { name: 'themeData', value: { theme: realThemeData } }) + } + result = realThemeData } else { throw new Error('Cannot load any theme!') } @@ -259,95 +259,91 @@ const interfaceMod = { // cache (tryLoadCache return true if load successful) if (!forceRecompile && !themeDebug && tryLoadCache()) { dispatch('setThemeApplied') - return + return Promise.resolve() } - promise - .then(realThemeData => { - const theme2ruleset = convertTheme2To3(realThemeData) + const realThemeData = result + const theme2ruleset = convertTheme2To3(realThemeData) - if (saveData) { - commit('setOption', { name: 'theme', value: themeName || actualThemeName }) - commit('setOption', { name: 'customTheme', value: realThemeData }) - commit('setOption', { name: 'customThemeSource', value: realThemeData }) + if (saveData) { + commit('setOption', { name: 'theme', value: themeName || actualThemeName }) + commit('setOption', { name: 'customTheme', value: realThemeData }) + commit('setOption', { name: 'customThemeSource', value: realThemeData }) + } + 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 } - 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: {} } - 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 + if (value === 'opaque') { + newRule.directives.opacity = 1 + newRule.directives.background = '--wallpaper' } + if (value === 'transparent') { + newRule.directives.opacity = 0 + } + hacks.push(newRule) } - }) + break + } + } + }) - const ruleset = [ - ...theme2ruleset, - ...hacks - ] + const ruleset = [ + ...theme2ruleset, + ...hacks + ] - applyTheme( - ruleset, - () => dispatch('setThemeApplied'), - themeDebug - ) - }) - - return promise + applyTheme( + ruleset, + () => dispatch('setThemeApplied'), + themeDebug + ) } } } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 2b3f88a8..c1603f39 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -43,16 +43,16 @@ const adoptStyleSheets = (styles) => { // is nothing to do here. } -export const generateTheme = async (inputRuleset, callbacks, debug) => { +export const generateTheme = (inputRuleset, callbacks, debug) => { const { onNewRule = (rule, isLazy) => {}, onLazyFinished = () => {}, onEagerFinished = () => {} } = callbacks - // Assuming that "worst case scenario background" is panel background since it's the most likely one const themes3 = init({ inputRuleset, + // Assuming that "worst case scenario background" is panel background since it's the most likely one ultimateBackgroundColor: inputRuleset[0].directives['--bg'].split('|')[1].trim(), debug }) @@ -146,11 +146,11 @@ export const tryLoadCache = () => { } } -export const applyTheme = async (input, onFinish = (data) => {}, debug) => { +export const applyTheme = (input, onFinish = (data) => {}, debug) => { const eagerStyles = createStyleSheet(EAGER_STYLE_ID) const lazyStyles = createStyleSheet(LAZY_STYLE_ID) - const { lazyProcessFunc } = await generateTheme( + const { lazyProcessFunc } = generateTheme( input, { onNewRule (rule, isLazy) { @@ -185,8 +185,6 @@ export const applyTheme = async (input, onFinish = (data) => {}, debug) => { ) setTimeout(lazyProcessFunc, 0) - - return Promise.resolve() } const extractStyleConfig = ({ diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index cf58da11..056b78f7 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -196,6 +196,11 @@ export const init = ({ return rule }) + const i = 4 + if (2 + 2 === i) { + throw new Error('test') + } + const ruleset = rulesetUnsorted .map((data, index) => ({ data, index })) .sort(({ data: a, index: ai }, { data: b, index: bi }) => { diff --git a/static/pleromatan_orz.png b/static/pleromatan_orz.png new file mode 100644 index 00000000..aa54411f Binary files /dev/null and b/static/pleromatan_orz.png differ diff --git a/static/pleromatan_orz_fox.png b/static/pleromatan_orz_fox.png new file mode 100644 index 00000000..936e42f1 Binary files /dev/null and b/static/pleromatan_orz_fox.png differ