pleroma-fe boots once again

This commit is contained in:
Henry Jameson 2025-01-30 21:56:07 +02:00
parent 58e18d48df
commit c926ed7ac1
26 changed files with 1763 additions and 2245 deletions

View file

@ -38,6 +38,7 @@
"pako": "^2.1.0", "pako": "^2.1.0",
"parse-link-header": "2.0.0", "parse-link-header": "2.0.0",
"phoenix": "1.7.18", "phoenix": "1.7.18",
"pinia": "^2.0.33",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"qrcode": "1.5.4", "qrcode": "1.5.4",
"querystring-es3": "0.2.1", "querystring-es3": "0.2.1",

View file

@ -62,7 +62,7 @@ export default {
document.getElementById('modal').classList = ['-' + this.layoutType] document.getElementById('modal').classList = ['-' + this.layoutType]
}, },
mounted () { mounted () {
if (this.$store.state.interface.themeApplied) { if (useInterfaceStore().themeApplied) {
this.removeSplash() this.removeSplash()
} }
}, },
@ -71,7 +71,7 @@ export default {
}, },
computed: { computed: {
themeApplied () { themeApplied () {
return this.$store.state.interface.themeApplied return useInterfaceStore().themeApplied
}, },
layoutModalClass () { layoutModalClass () {
return '-' + this.layoutType return '-' + this.layoutType

View file

@ -1,6 +1,6 @@
<template> <template>
<div <div
v-show="$store.state.interface.themeApplied" v-show="themeApplied"
id="app-loaded" id="app-loaded"
:style="bgStyle" :style="bgStyle"
> >

View file

@ -359,7 +359,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
await setConfig({ store }) await setConfig({ store })
try { try {
await store.dispatch('applyTheme').catch((e) => { console.error('Error setting theme', e) }) await useInterfaceStore().applyTheme().catch((e) => { console.error('Error setting theme', e) })
} catch (e) { } catch (e) {
window.splashError(e) window.splashError(e)
return Promise.reject(e) return Promise.reject(e)

View file

@ -3,8 +3,10 @@ import Status from '../status/status.vue'
import ThreadTree from '../thread_tree/thread_tree.vue' import ThreadTree from '../thread_tree/thread_tree.vue'
import { WSConnectionStatus } from '../../services/api/api.service.js' import { WSConnectionStatus } from '../../services/api/api.service.js'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue' import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue'
import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue'
import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -350,8 +352,10 @@ const conversation = {
}, },
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState({ ...mapState({
mobileLayout: state => state.interface.layoutType === 'mobile',
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus mastoUserSocketStatus: state => state.api.mastoUserSocketStatus
}),
...mapPiniaState(useInterfaceStore, {
mobileLayout: store => store.layoutType === 'mobile'
}) })
}, },
components: { components: {

View file

@ -1,4 +1,6 @@
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import { useAnnouncementsStore } from '../../stores/announcements'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -33,7 +35,10 @@ const ExtraNotifications = {
currentUser () { currentUser () {
return this.$store.state.users.currentUser return this.$store.state.users.currentUser
}, },
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount', 'followRequestCount', 'mergedConfig']) ...mapGetters(['unreadChatCount', 'followRequestCount', 'mergedConfig']),
...mapPiniaState(useAnnouncementsStore, {
unreadAnnouncementCount: 'unreadAnnouncementCount'
})
}, },
methods: { methods: {
openNotificationSettings () { openNotificationSettings () {

View file

@ -1,6 +1,7 @@
import Select from '../select/select.vue' import Select from '../select/select.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
import Popover from 'src/components/popover/popover.vue' import Popover from 'src/components/popover/popover.vue'
import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -50,10 +51,10 @@ export default {
return typeof this.modelValue !== 'undefined' return typeof this.modelValue !== 'undefined'
}, },
localFontsList () { localFontsList () {
return this.$store.state.interface.localFonts return useInterfaceStore().localFonts
}, },
localFontsSize () { localFontsSize () {
return this.$store.state.interface.localFonts?.length return useInterfaceStore().localFonts?.length
} }
} }
} }

View file

@ -7,6 +7,7 @@ import { filterNavigation } from 'src/components/navigation/filter.js'
import NavigationEntry from 'src/components/navigation/navigation_entry.vue' import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import NavigationPins from 'src/components/navigation/navigation_pins.vue' import NavigationPins from 'src/components/navigation/navigation_pins.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
import { useAnnouncementsStore } from 'src/stores/announcements'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -24,7 +25,6 @@ import {
faBullhorn, faBullhorn,
faFilePen faFilePen
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useAnnouncementsStore } from '../../stores/announcements'
library.add( library.add(
faUsers, faUsers,

View file

@ -17,7 +17,8 @@ import {
faStream, faStream,
faList faList
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useListsStore } from '../../stores/lists' import { useListsStore } from 'src/stores/lists'
import { useAnnouncementsStore } from 'src/stores/announcements'
library.add( library.add(
faUsers, faUsers,
@ -48,6 +49,9 @@ const NavPanel = {
...mapPiniaState(useListsStore, { ...mapPiniaState(useListsStore, {
lists: getListEntries lists: getListEntries
}), }),
...mapPiniaState(useAnnouncementsStore, {
supportsAnnouncements: store => store.supportsAnnouncements
}),
...mapState({ ...mapState({
bookmarks: getBookmarkFolderEntries, bookmarks: getBookmarkFolderEntries,
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
@ -55,7 +59,6 @@ const NavPanel = {
privateMode: state => state.instance.private, privateMode: state => state.instance.private,
federating: state => state.instance.federating, federating: state => state.instance.federating,
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
supportsAnnouncements: state => state.announcements.supportsAnnouncements,
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
}), }),
pinnedList () { pinnedList () {

View file

@ -7,6 +7,7 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue' import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { cloneDeep, isEqual } from 'lodash' import { cloneDeep, isEqual } from 'lodash'
import { mapState as mapPiniaState } from 'pinia'
import { import {
newImporter, newImporter,
newExporter newExporter
@ -167,24 +168,15 @@ const SettingsModal = {
} }
}, },
computed: { computed: {
currentSaveStateNotice () { ...mapPiniaState(useInterfaceStore, {
return useInterfaceStore().settings.currentSaveStateNotice temporaryChangesTimeoutId: store => store.layoutType === 'mobile',
}, currentSaveStateNotice: store => store.settings.currentSaveStateNotice,
modalActivated () { modalActivated: store => store.settingsModalState !== 'hidden',
return useInterfaceStore().settingsModalState !== 'hidden' modalMode: store => store.settingsModalMode,
}, modalOpenedOnceUser: store => store.settingsModalLoadedUser,
modalMode () { modalOpenedOnceAdmin: store => store.settingsModalLoadedAdmin,
return useInterfaceStore().settingsModalMode modalPeeked: store => store.settingsModalState === 'minimized'
}, }),
modalOpenedOnceUser () {
return useInterfaceStore().settingsModalLoadedUser
},
modalOpenedOnceAdmin () {
return useInterfaceStore().settingsModalLoadedAdmin
},
modalPeeked () {
return useInterfaceStore().settingsModalState === 'minimized'
},
expertLevel: { expertLevel: {
get () { get () {
return this.$store.state.config.expertLevel > 0 return this.$store.state.config.expertLevel > 0

View file

@ -158,7 +158,7 @@
</div> </div>
<teleport to="#modal"> <teleport to="#modal">
<ConfirmModal <ConfirmModal
v-if="$store.state.interface.temporaryChangesTimeoutId" v-if="temporaryChangesTimeoutId"
:title="$t('settings.confirm_new_setting')" :title="$t('settings.confirm_new_setting')"
:cancel-text="$t('settings.revert')" :cancel-text="$t('settings.revert')"
:confirm-text="$t('settings.confirm')" :confirm-text="$t('settings.confirm')"

View file

@ -4,6 +4,7 @@ import InstanceTab from './admin_tabs/instance_tab.vue'
import LimitsTab from './admin_tabs/limits_tab.vue' import LimitsTab from './admin_tabs/limits_tab.vue'
import FrontendsTab from './admin_tabs/frontends_tab.vue' import FrontendsTab from './admin_tabs/frontends_tab.vue'
import EmojiTab from './admin_tabs/emoji_tab.vue' import EmojiTab from './admin_tabs/emoji_tab.vue'
import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -45,10 +46,10 @@ const SettingsModalAdminContent = {
return !!this.$store.state.users.currentUser return !!this.$store.state.users.currentUser
}, },
open () { open () {
return this.$store.state.interface.settingsModalState !== 'hidden' return useInterfaceStore().settingsModalState !== 'hidden'
}, },
bodyLock () { bodyLock () {
return this.$store.state.interface.settingsModalState === 'visible' return useInterfaceStore().settingsModalState === 'visible'
}, },
adminDbLoaded () { adminDbLoaded () {
return this.$store.state.adminSettings.loaded return this.$store.state.adminSettings.loaded
@ -67,7 +68,7 @@ const SettingsModalAdminContent = {
}, },
methods: { methods: {
onOpen () { onOpen () {
const targetTab = this.$store.state.interface.settingsModalTargetTab const targetTab = useInterfaceStore().settingsModalTargetTab
// We're being told to open in specific tab // We're being told to open in specific tab
if (targetTab) { if (targetTab) {
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => { const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {

View file

@ -25,7 +25,7 @@ import {
faInfo, faInfo,
faWindowRestore faWindowRestore
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface' import { useInterfaceStore } from 'src/stores/interface'
library.add( library.add(
faWrench, faWrench,
@ -70,7 +70,7 @@ const SettingsModalContent = {
return this.$store.state.config.expertLevel return this.$store.state.config.expertLevel
}, },
isMobileLayout () { isMobileLayout () {
return this.$store.state.interface.layoutType === 'mobile' return useInterfaceStore().layoutType === 'mobile'
} }
}, },
methods: { methods: {

View file

@ -7,7 +7,7 @@ import PaletteEditor from 'src/components/palette_editor/palette_editor.vue'
import FontControl from 'src/components/font_control/font_control.vue' import FontControl from 'src/components/font_control/font_control.vue'
import { normalizeThemeData } from 'src/modules/interface' import { useInterfaceStore, normalizeThemeData } from 'src/stores/interface'
import { newImporter } from 'src/services/export_import/export_import.js' import { newImporter } from 'src/services/export_import/export_import.js'
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
@ -131,7 +131,7 @@ const AppearanceTab = {
})) }))
}) })
this.userPalette = this.$store.state.interface.paletteDataUsed || {} this.userPalette = useInterfaceStore().paletteDataUsed || {}
updateIndex('palette').then(bundledPalettes => { updateIndex('palette').then(bundledPalettes => {
bundledPalettes.forEach(([key, palettePromise]) => palettePromise.then(v => { bundledPalettes.forEach(([key, palettePromise]) => palettePromise.then(v => {
@ -187,10 +187,10 @@ const AppearanceTab = {
}, },
computed: { computed: {
switchInProgress () { switchInProgress () {
return this.$store.state.interface.themeChangeInProgress return useInterfaceStore().themeChangeInProgress
}, },
paletteDataUsed () { paletteDataUsed () {
return this.$store.state.interface.paletteDataUsed return useInterfaceStore().paletteDataUsed
}, },
availableStyles () { availableStyles () {
return [ return [
@ -205,7 +205,7 @@ const AppearanceTab = {
] ]
}, },
stylePalettes () { stylePalettes () {
const ruleset = this.$store.state.interface.styleDataUsed || [] const ruleset = useInterfaceStore().styleDataUsed || []
if (!ruleset && ruleset.length === 0) return if (!ruleset && ruleset.length === 0) return
const meta = ruleset.find(x => x.component === '@meta') const meta = ruleset.find(x => x.component === '@meta')
const result = ruleset.filter(x => x.component.startsWith('@palette')) const result = ruleset.filter(x => x.component.startsWith('@palette'))
@ -273,7 +273,7 @@ const AppearanceTab = {
} }
}, },
customThemeVersion () { customThemeVersion () {
const { themeVersion } = this.$store.state.interface const { themeVersion } = useInterfaceStore()
return themeVersion return themeVersion
}, },
isCustomThemeUsed () { isCustomThemeUsed () {

View file

@ -1,5 +1,6 @@
import { ref, reactive, computed, watch, watchEffect, provide, getCurrentInstance } from 'vue' import { ref, reactive, computed, watch, watchEffect, provide, getCurrentInstance } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { useInterfaceStore } from 'src/stores/interface'
import { get, set, unset, throttle } from 'lodash' import { get, set, unset, throttle } from 'lodash'
import Select from 'src/components/select/select.vue' import Select from 'src/components/select/select.vue'
@ -81,9 +82,10 @@ export default {
setup (props, context) { setup (props, context) {
const exports = {} const exports = {}
const store = useStore() const store = useStore()
const interfaceStore = useInterfaceStore()
// All rules that are made by editor // All rules that are made by editor
const allEditedRules = ref(store.state.interface.styleDataUsed || {}) const allEditedRules = ref(interfaceStore.styleDataUsed || {})
const styleDataUsed = computed(() => store.state.interface.styleDataUsed) const styleDataUsed = computed(() => interfaceStore.styleDataUsed)
watch([styleDataUsed], (value) => { watch([styleDataUsed], (value) => {
onImport(store.state.interface.styleDataUsed) onImport(store.state.interface.styleDataUsed)

View file

@ -297,7 +297,7 @@ export default {
} }
}, },
themeDataUsed () { themeDataUsed () {
return this.$store.state.interface.themeDataUsed return useInterfaceStore().themeDataUsed
}, },
shadowsAvailable () { shadowsAvailable () {
return Object.keys(DEFAULT_SHADOWS).sort() return Object.keys(DEFAULT_SHADOWS).sort()

View file

@ -1,4 +1,5 @@
import StillImage from '../still-image/still-image.vue' import StillImage from '../still-image/still-image.vue'
import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
@ -22,7 +23,7 @@ const UserAvatar = {
return { return {
showPlaceholder: false, showPlaceholder: false,
defaultAvatar: `${this.$store.state.instance.server + this.$store.state.instance.defaultAvatar}`, defaultAvatar: `${this.$store.state.instance.server + this.$store.state.instance.defaultAvatar}`,
betterShadow: this.$store.state.interface.browserSupport.cssFilter betterShadow: useInterfaceStore().browserSupport.cssFilter
} }
}, },
components: { components: {

View file

@ -14,18 +14,11 @@ import configModule from './modules/config.js'
import profileConfigModule from './modules/profileConfig.js' import profileConfigModule from './modules/profileConfig.js'
import serverSideStorageModule from './modules/serverSideStorage.js' import serverSideStorageModule from './modules/serverSideStorage.js'
import adminSettingsModule from './modules/adminSettings.js' import adminSettingsModule from './modules/adminSettings.js'
import shoutModule from './modules/shout.js'
import oauthModule from './modules/oauth.js' import oauthModule from './modules/oauth.js'
import authFlowModule from './modules/auth_flow.js' import authFlowModule from './modules/auth_flow.js'
import oauthTokensModule from './modules/oauth_tokens.js' import oauthTokensModule from './modules/oauth_tokens.js'
import reportsModule from './modules/reports.js'
import pollsModule from './modules/polls.js'
import postStatusModule from './modules/postStatus.js'
import editStatusModule from './modules/editStatus.js'
import statusHistoryModule from './modules/statusHistory.js'
import draftsModule from './modules/drafts.js' import draftsModule from './modules/drafts.js'
import chatsModule from './modules/chats.js' import chatsModule from './modules/chats.js'
import announcementsModule from './modules/announcements.js'
import bookmarkFoldersModule from './modules/bookmark_folders.js' import bookmarkFoldersModule from './modules/bookmark_folders.js'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
@ -101,6 +94,7 @@ const persistedStateOptions = {
instance: instanceModule, instance: instanceModule,
// TODO refactor users/statuses modules, they depend on each other // TODO refactor users/statuses modules, they depend on each other
users: usersModule, users: usersModule,
lists: listsModule,
statuses: statusesModule, statuses: statusesModule,
notifications: notificationsModule, notifications: notificationsModule,
api: apiModule, api: apiModule,
@ -122,10 +116,11 @@ const persistedStateOptions = {
strict: false // Socket modifies itself, let's ignore this for now. strict: false // Socket modifies itself, let's ignore this for now.
// strict: process.env.NODE_ENV !== 'production' // strict: process.env.NODE_ENV !== 'production'
}) })
window.vuex = store
if (storageError) { if (storageError) {
// Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete.
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
} }
// Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete.
return await afterStoreSetup({ pinia, store, storageError, i18n }) return await afterStoreSetup({ pinia, store, storageError, i18n })
} catch (e) { } catch (e) {
splashError(i18n, e) splashError(i18n, e)

View file

@ -6,6 +6,8 @@ import localeService from '../services/locale/locale.service.js'
import { useI18nStore } from '../stores/i18n.js' import { useI18nStore } from '../stores/i18n.js'
import { useInterfaceStore } from '../stores/interface.js' import { useInterfaceStore } from '../stores/interface.js'
import { defaultState } from './default_config_state.js'
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
const APPEARANCE_SETTINGS_KEYS = new Set([ const APPEARANCE_SETTINGS_KEYS = new Set([
'sidebarColumnWidth', 'sidebarColumnWidth',
@ -19,8 +21,6 @@ const APPEARANCE_SETTINGS_KEYS = new Set([
'emojiReactionsScale' 'emojiReactionsScale'
]) ])
const browserLocale = (window.navigator.language || 'en').split('-')[0]
/* TODO this is a bit messy. /* TODO this is a bit messy.
* We need to declare settings with their types and also deal with * We need to declare settings with their types and also deal with
* instance-default settings in some way, hopefully try to avoid copy-pasta * instance-default settings in some way, hopefully try to avoid copy-pasta
@ -36,168 +36,7 @@ export const multiChoiceProperties = [
'unsavedPostAction' // save | discard | confirm 'unsavedPostAction' // save | discard | confirm
] ]
export const defaultState = { console.log('TEST', defaultState)
expertLevel: 0, // used to track which settings to show and hide
// Theme stuff
theme: undefined, // Very old theme store, stores preset name, still in use
// V1
colors: {}, // VERY old theme store, just colors of V1, probably not even used anymore
// V2
customTheme: undefined, // "snapshot", previously was used as actual theme store for V2 so it's still used in case of PleromaFE downgrade event.
customThemeSource: undefined, // "source", stores original theme data
// V3
style: null,
styleCustomData: null,
palette: null,
paletteCustomData: null,
themeDebug: false, // debug mode that uses computed backgrounds instead of real ones to debug contrast functions
forceThemeRecompilation: false, // flag that forces recompilation on boot even if cache exists
theme3hacks: { // Hacks, user overrides that are independent of theme used
underlay: 'none',
fonts: {
interface: undefined,
input: undefined,
post: undefined,
monospace: undefined
}
},
hideISP: false,
hideInstanceWallpaper: false,
hideShoutbox: false,
// bad name: actually hides posts of muted USERS
hideMutedPosts: undefined, // instance default
hideMutedThreads: undefined, // instance default
hideWordFilteredPosts: undefined, // instance default
muteBotStatuses: undefined, // instance default
muteSensitiveStatuses: undefined, // instance default
collapseMessageWithSubject: undefined, // instance default
padEmoji: true,
hideAttachments: false,
hideAttachmentsInConv: false,
hideScrobbles: false,
hideScrobblesAfter: '2d',
maxThumbnails: 16,
hideNsfw: true,
preloadImage: true,
loopVideo: true,
loopVideoSilentOnly: true,
streaming: false,
emojiReactionsOnTimeline: true,
alwaysShowNewPostButton: false,
autohideFloatingPostButton: false,
pauseOnUnfocused: true,
stopGifs: true,
replyVisibility: 'all',
thirdColumnMode: 'notifications',
notificationVisibility: {
follows: true,
mentions: true,
statuses: true,
likes: true,
repeats: true,
moves: true,
emojiReactions: true,
followRequest: true,
reports: true,
chatMention: true,
polls: true
},
notificationNative: {
follows: true,
mentions: true,
statuses: true,
likes: false,
repeats: false,
moves: false,
emojiReactions: false,
followRequest: true,
reports: true,
chatMention: true,
polls: true
},
webPushNotifications: false,
webPushAlwaysShowNotifications: false,
muteWords: [],
highlight: {},
interfaceLanguage: browserLocale,
hideScopeNotice: false,
useStreamingApi: false,
sidebarRight: undefined, // instance default
scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default
alwaysShowSubjectInput: undefined, // instance default
postContentType: undefined, // instance default
minimalScopesMode: undefined, // instance default
// This hides statuses filtered via a word filter
hideFilteredStatuses: undefined, // instance default
modalOnRepeat: undefined, // instance default
modalOnUnfollow: undefined, // instance default
modalOnBlock: undefined, // instance default
modalOnMute: undefined, // instance default
modalOnMuteConversation: undefined, // instance default
modalOnMuteDomain: undefined, // instance default
modalOnDelete: undefined, // instance default
modalOnLogout: undefined, // instance default
modalOnApproveFollow: undefined, // instance default
modalOnDenyFollow: undefined, // instance default
modalOnRemoveUserFromFollowers: undefined, // instance default
modalMobileCenter: undefined,
playVideosInModal: false,
useOneClickNsfw: false,
useContainFit: true,
disableStickyHeaders: false,
showScrollbars: false,
userPopoverAvatarAction: 'open',
userPopoverOverlay: false,
sidebarColumnWidth: '25rem',
contentColumnWidth: '45rem',
notifsColumnWidth: '25rem',
emojiReactionsScale: undefined,
textSize: undefined, // instance default
emojiSize: undefined, // instance default
navbarSize: undefined, // instance default
panelHeaderSize: undefined, // instance default
forcedRoundness: undefined, // instance default
navbarColumnStretch: false,
greentext: undefined, // instance default
useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default
mentionLinkShowTooltip: undefined, // instance default
mentionLinkShowAvatar: undefined, // instance default
mentionLinkFadeDomain: undefined, // instance default
mentionLinkShowYous: undefined, // instance default
mentionLinkBoldenYou: undefined, // instance default
hidePostStats: undefined, // instance default
hideBotIndication: undefined, // instance default
hideUserStats: undefined, // instance default
virtualScrolling: undefined, // instance default
sensitiveByDefault: undefined, // instance default
conversationDisplay: undefined, // instance default
conversationTreeAdvanced: undefined, // instance default
conversationOtherRepliesButton: undefined, // instance default
conversationTreeFadeAncestors: undefined, // instance default
showExtraNotifications: undefined, // instance default
showExtraNotificationsTip: undefined, // instance default
showChatsInExtraNotifications: undefined, // instance default
showAnnouncementsInExtraNotifications: undefined, // instance default
showFollowRequestsInExtraNotifications: undefined, // instance default
maxDepthInThread: undefined, // instance default
autocompleteSelect: undefined, // instance default
closingDrawerMarksAsSeen: undefined, // instance default
unseenAtTop: undefined, // instance default
ignoreInactionableSeen: undefined, // instance default
unsavedPostAction: undefined, // instance default
autoSaveDraft: undefined, // instance default
useAbsoluteTimeFormat: undefined, // instance default
absoluteTimeFormatMinAge: undefined, // instance default
absoluteTime12h: undefined, // instance default
imageCompression: true
}
// caching the instance default properties // caching the instance default properties
export const instanceDefaultProperties = Object.entries(defaultState) export const instanceDefaultProperties = Object.entries(defaultState)
@ -261,7 +100,7 @@ const config = {
commit('setHighlight', { user, color, type }) commit('setHighlight', { user, color, type })
}, },
setOptionTemporarily ({ commit, dispatch, state, rootState }, { name, value }) { setOptionTemporarily ({ commit, dispatch, state, rootState }, { name, value }) {
if (rootState.interface.temporaryChangesTimeoutId !== null) { if (useInterfaceStore().temporaryChangesTimeoutId !== null) {
console.warn('Can\'t track more than one temporary change') console.warn('Can\'t track more than one temporary change')
return return
} }

View file

@ -0,0 +1,164 @@
const browserLocale = (window.navigator.language || 'en').split('-')[0]
export const defaultState = {
expertLevel: 0, // used to track which settings to show and hide
// Theme stuff
theme: undefined, // Very old theme store, stores preset name, still in use
// V1
colors: {}, // VERY old theme store, just colors of V1, probably not even used anymore
// V2
customTheme: undefined, // "snapshot", previously was used as actual theme store for V2 so it's still used in case of PleromaFE downgrade event.
customThemeSource: undefined, // "source", stores original theme data
// V3
style: null,
styleCustomData: null,
palette: null,
paletteCustomData: null,
themeDebug: false, // debug mode that uses computed backgrounds instead of real ones to debug contrast functions
forceThemeRecompilation: false, // flag that forces recompilation on boot even if cache exists
theme3hacks: { // Hacks, user overrides that are independent of theme used
underlay: 'none',
fonts: {
interface: undefined,
input: undefined,
post: undefined,
monospace: undefined
}
},
hideISP: false,
hideInstanceWallpaper: false,
hideShoutbox: false,
// bad name: actually hides posts of muted USERS
hideMutedPosts: undefined, // instance default
hideMutedThreads: undefined, // instance default
hideWordFilteredPosts: undefined, // instance default
muteBotStatuses: undefined, // instance default
muteSensitiveStatuses: undefined, // instance default
collapseMessageWithSubject: undefined, // instance default
padEmoji: true,
hideAttachments: false,
hideAttachmentsInConv: false,
hideScrobbles: false,
hideScrobblesAfter: '2d',
maxThumbnails: 16,
hideNsfw: true,
preloadImage: true,
loopVideo: true,
loopVideoSilentOnly: true,
streaming: false,
emojiReactionsOnTimeline: true,
alwaysShowNewPostButton: false,
autohideFloatingPostButton: false,
pauseOnUnfocused: true,
stopGifs: true,
replyVisibility: 'all',
thirdColumnMode: 'notifications',
notificationVisibility: {
follows: true,
mentions: true,
statuses: true,
likes: true,
repeats: true,
moves: true,
emojiReactions: true,
followRequest: true,
reports: true,
chatMention: true,
polls: true
},
notificationNative: {
follows: true,
mentions: true,
statuses: true,
likes: false,
repeats: false,
moves: false,
emojiReactions: false,
followRequest: true,
reports: true,
chatMention: true,
polls: true
},
webPushNotifications: false,
webPushAlwaysShowNotifications: false,
muteWords: [],
highlight: {},
interfaceLanguage: browserLocale,
hideScopeNotice: false,
useStreamingApi: false,
sidebarRight: undefined, // instance default
scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default
alwaysShowSubjectInput: undefined, // instance default
postContentType: undefined, // instance default
minimalScopesMode: undefined, // instance default
// This hides statuses filtered via a word filter
hideFilteredStatuses: undefined, // instance default
modalOnRepeat: undefined, // instance default
modalOnUnfollow: undefined, // instance default
modalOnBlock: undefined, // instance default
modalOnMute: undefined, // instance default
modalOnMuteConversation: undefined, // instance default
modalOnMuteDomain: undefined, // instance default
modalOnDelete: undefined, // instance default
modalOnLogout: undefined, // instance default
modalOnApproveFollow: undefined, // instance default
modalOnDenyFollow: undefined, // instance default
modalOnRemoveUserFromFollowers: undefined, // instance default
modalMobileCenter: undefined,
playVideosInModal: false,
useOneClickNsfw: false,
useContainFit: true,
disableStickyHeaders: false,
showScrollbars: false,
userPopoverAvatarAction: 'open',
userPopoverOverlay: false,
sidebarColumnWidth: '25rem',
contentColumnWidth: '45rem',
notifsColumnWidth: '25rem',
emojiReactionsScale: undefined,
textSize: undefined, // instance default
emojiSize: undefined, // instance default
navbarSize: undefined, // instance default
panelHeaderSize: undefined, // instance default
forcedRoundness: undefined, // instance default
navbarColumnStretch: false,
greentext: undefined, // instance default
useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default
mentionLinkShowTooltip: undefined, // instance default
mentionLinkShowAvatar: undefined, // instance default
mentionLinkFadeDomain: undefined, // instance default
mentionLinkShowYous: undefined, // instance default
mentionLinkBoldenYou: undefined, // instance default
hidePostStats: undefined, // instance default
hideBotIndication: undefined, // instance default
hideUserStats: undefined, // instance default
virtualScrolling: undefined, // instance default
sensitiveByDefault: undefined, // instance default
conversationDisplay: undefined, // instance default
conversationTreeAdvanced: undefined, // instance default
conversationOtherRepliesButton: undefined, // instance default
conversationTreeFadeAncestors: undefined, // instance default
showExtraNotifications: undefined, // instance default
showExtraNotificationsTip: undefined, // instance default
showChatsInExtraNotifications: undefined, // instance default
showAnnouncementsInExtraNotifications: undefined, // instance default
showFollowRequestsInExtraNotifications: undefined, // instance default
maxDepthInThread: undefined, // instance default
autocompleteSelect: undefined, // instance default
closingDrawerMarksAsSeen: undefined, // instance default
unseenAtTop: undefined, // instance default
ignoreInactionableSeen: undefined, // instance default
unsavedPostAction: undefined, // instance default
autoSaveDraft: undefined, // instance default
useAbsoluteTimeFormat: undefined, // instance default
absoluteTimeFormatMinAge: undefined, // instance default
absoluteTime12h: undefined, // instance default
imageCompression: true
}

View file

@ -11,6 +11,8 @@ import {
closeAllDesktopNotifications closeAllDesktopNotifications
} from '../services/desktop_notification_utils/desktop_notification_utils.js' } from '../services/desktop_notification_utils/desktop_notification_utils.js'
import { useReportsStore } from '../stores/reports.js'
const emptyNotifications = () => ({ const emptyNotifications = () => ({
desktopNotificationSilence: true, desktopNotificationSilence: true,
maxId: 0, maxId: 0,
@ -94,7 +96,7 @@ export const notifications = {
validNotifications.forEach(notification => { validNotifications.forEach(notification => {
if (notification.type === 'pleroma:report') { if (notification.type === 'pleroma:report') {
dispatch('addReport', notification.report) useReportsStore().addReport(notification.report)
} }
if (notification.type === 'pleroma:emoji_reaction') { if (notification.type === 'pleroma:emoji_reaction') {

View file

@ -13,7 +13,6 @@ import {
omitBy omitBy
} from 'lodash' } from 'lodash'
import apiService from '../services/api/api.service.js' import apiService from '../services/api/api.service.js'
import { useReportsStore } from '../stores/reports.js'
const emptyTl = (userId = 0) => ({ const emptyTl = (userId = 0) => ({
statuses: [], statuses: [],

View file

@ -1,6 +1,7 @@
import { muteWordHits } from '../status_parser/status_parser.js' import { muteWordHits } from '../status_parser/status_parser.js'
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js' import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
import { useI18nStore } from '../../stores/i18n.js' import { useI18nStore } from '../../stores/i18n.js'
import { useAnnouncementsStore } from 'src/stores/announcements'
import FaviconService from 'src/services/favicon_service/favicon_service.js' import FaviconService from 'src/services/favicon_service/favicon_service.js'
@ -169,7 +170,7 @@ export const countExtraNotifications = (store) => {
return [ return [
mergedConfig.showChatsInExtraNotifications ? rootGetters.unreadChatCount : 0, mergedConfig.showChatsInExtraNotifications ? rootGetters.unreadChatCount : 0,
mergedConfig.showAnnouncementsInExtraNotifications ? rootGetters.unreadAnnouncementCount : 0, mergedConfig.showAnnouncementsInExtraNotifications ? useAnnouncementsStore().unreadAnnouncementCount : 0,
mergedConfig.showFollowRequestsInExtraNotifications ? rootGetters.followRequestCount : 0 mergedConfig.showFollowRequestsInExtraNotifications ? rootGetters.followRequestCount : 0
].reduce((a, c) => a + c, 0) ].reduce((a, c) => a + c, 0)
} }

View file

@ -1,10 +1,12 @@
import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js' import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js'
import { getCssRules } from '../theme_data/css_utils.js' import { getCssRules } from '../theme_data/css_utils.js'
import { defaultState } from '../../modules/config.js' import { defaultState } from 'src/modules/default_config_state.js'
import { chunk } from 'lodash' import { chunk } from 'lodash'
import pako from 'pako' import pako from 'pako'
import localforage from 'localforage' import localforage from 'localforage'
console.log('CONFIG', defaultState)
// On platforms where this is not supported, it will return undefined // On platforms where this is not supported, it will return undefined
// Otherwise it will return an array // Otherwise it will return an array
const supportsAdoptedStyleSheets = !!document.adoptedStyleSheets const supportsAdoptedStyleSheets = !!document.adoptedStyleSheets
@ -212,6 +214,7 @@ const extractStyleConfig = ({
return result return result
} }
console.log(defaultState)
const defaultStyleConfig = extractStyleConfig(defaultState) const defaultStyleConfig = extractStyleConfig(defaultState)
export const applyConfig = (input, i18n) => { export const applyConfig = (input, i18n) => {

View file

@ -1,10 +1,31 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { CURRENT_VERSION, generatePreset } from 'src/services/theme_data/theme_data.service.js'
import { getResourcesIndex, applyTheme, tryLoadCache } from '../services/style_setter/style_setter.js'
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
import { deserialize } from '../services/theme_data/iss_deserializer.js'
export const useInterfaceStore = defineStore('interface', { export const useInterfaceStore = defineStore('interface', {
state: () => ({ state: () => ({
localFonts: null,
themeApplied: false,
themeChangeInProgress: false,
themeVersion: 'v3',
styleNameUsed: null,
styleDataUsed: null,
useStylePalette: false, // hack for applying styles from appearance tab
paletteNameUsed: null,
paletteDataUsed: null,
themeNameUsed: null,
themeDataUsed: null,
temporaryChangesTimeoutId: null, // used for temporary options that revert after a timeout
temporaryChangesConfirm: () => {}, // used for applying temporary options
temporaryChangesRevert: () => {}, // used for reverting temporary options
settingsModalState: 'hidden', settingsModalState: 'hidden',
settingsModalLoaded: false, settingsModalLoadedUser: false,
settingsModalLoadedAdmin: false,
settingsModalTargetTab: null, settingsModalTargetTab: null,
settingsModalMode: 'user',
settings: { settings: {
currentSaveStateNotice: null, currentSaveStateNotice: null,
noticeClearTimeout: null, noticeClearTimeout: null,
@ -46,10 +67,17 @@ export const useInterfaceStore = defineStore('interface', {
closeSettingsModal () { closeSettingsModal () {
this.settingsModalState = 'hidden' this.settingsModalState = 'hidden'
}, },
openSettingsModal () { openSettingsModal (value) {
this.settingsModalMode = value
this.settingsModalState = 'visible' this.settingsModalState = 'visible'
if (!this.settingsModalLoaded) { if (value === 'user') {
this.settingsModalLoaded = true if (!this.settingsModalLoadedUser) {
this.settingsModalLoadedUser = true
}
} else if (value === 'admin') {
if (!this.settingsModalLoadedAdmin) {
this.settingsModalLoadedAdmin = true
}
} }
}, },
togglePeekSettingsModal () { togglePeekSettingsModal () {
@ -121,6 +149,500 @@ export const useInterfaceStore = defineStore('interface', {
}, },
setLastTimeline (value) { setLastTimeline (value) {
this.lastTimeline = value this.lastTimeline = value
},
async fetchPalettesIndex () {
try {
const value = await getResourcesIndex('/static/palettes/index.json')
window.vuex.commit('setInstanceOption', { name: 'palettesIndex', value })
return value
} catch (e) {
console.error('Could not fetch palettes index', e)
window.vuex.commit('setInstanceOption', { name: 'palettesIndex', value: { _error: e } })
return Promise.resolve({})
}
},
setPalette (value) {
this.resetThemeV3Palette()
this.resetThemeV2()
window.vuex.commit('setOption', { name: 'palette', value })
this.applyTheme({ recompile: true })
},
setPaletteCustom (value) {
this.resetThemeV3Palette()
this.resetThemeV2()
window.vuex.commit('setOption', { name: 'paletteCustomData', value })
this.applyTheme({ recompile: true })
},
async fetchStylesIndex () {
try {
const value = await getResourcesIndex(
'/static/styles/index.json',
deserialize
)
window.vuex.commit('setInstanceOption', { name: 'stylesIndex', value })
return value
} catch (e) {
console.error('Could not fetch styles index', e)
window.vuex.commit('setInstanceOption', { name: 'stylesIndex', value: { _error: e } })
return Promise.resolve({})
}
},
setStyle (value) {
this.resetThemeV3()
this.resetThemeV2()
this.resetThemeV3Palette()
window.vuex.commit('setOption', { name: 'style', value })
this.useStylePalette = true
this.applyTheme({ recompile: true }).then(() => {
this.useStylePalette = false
})
},
setStyleCustom (value) {
this.resetThemeV3()
this.resetThemeV2()
this.resetThemeV3Palette()
window.vuex.commit('setOption', { name: 'styleCustomData', value })
this.useStylePalette = true
this.applyTheme({ recompile: true }).then(() => {
this.useStylePalette = false
})
},
async fetchThemesIndex () {
try {
const value = await getResourcesIndex('/static/styles.json')
window.vuex.commit('setInstanceOption', { name: 'themesIndex', value })
return value
} catch (e) {
console.error('Could not fetch themes index', e)
window.vuex.commit('setInstanceOption', { name: 'themesIndex', value: { _error: e } })
return Promise.resolve({})
}
},
setTheme (value) {
this.resetThemeV3()
this.resetThemeV3Palette()
this.resetThemeV2()
window.vuex.commit('setOption', { name: 'theme', value })
this.applyTheme({ recompile: true })
},
setThemeCustom (value) {
this.resetThemeV3()
this.resetThemeV3Palette()
this.resetThemeV2()
window.vuex.commit('setOption', { name: 'customTheme', value })
window.vuex.commit('setOption', { name: 'customThemeSource', value })
this.applyTheme({ recompile: true })
},
resetThemeV3 () {
window.vuex.commit('setOption', { name: 'style', value: null })
window.vuex.commit('setOption', { name: 'styleCustomData', value: null })
},
resetThemeV3Palette () {
window.vuex.commit('setOption', { name: 'palette', value: null })
window.vuex.commit('setOption', { name: 'paletteCustomData', value: null })
},
resetThemeV2 () {
window.vuex.commit('setOption', { name: 'theme', value: null })
window.vuex.commit('setOption', { name: 'customTheme', value: null })
window.vuex.commit('setOption', { name: 'customThemeSource', value: null })
},
async getThemeData () {
const getData = async (resource, index, customData, name) => {
const capitalizedResource = resource[0].toUpperCase() + resource.slice(1)
const result = {}
if (customData) {
result.nameUsed = 'custom' // custom data overrides name
result.dataUsed = customData
} else {
result.nameUsed = name
if (result.nameUsed == null) {
result.dataUsed = null
return result
}
let fetchFunc = index[result.nameUsed]
// Fallbacks
if (!fetchFunc) {
if (resource === 'style' || resource === 'palette') {
return result
}
const newName = Object.keys(index)[0]
fetchFunc = index[newName]
console.warn(`${capitalizedResource} with id '${this.styleNameUsed}' not found, trying back to '${newName}'`)
if (!fetchFunc) {
console.warn(`${capitalizedResource} doesn't have a fallback, defaulting to stock.`)
fetchFunc = () => Promise.resolve(null)
}
}
result.dataUsed = await fetchFunc()
}
return result
}
const {
style: instanceStyleName,
palette: instancePaletteName
} = window.vuex.state.instance
let {
theme: instanceThemeV2Name,
themesIndex,
stylesIndex,
palettesIndex
} = window.vuex.state.instance
const {
style: userStyleName,
styleCustomData: userStyleCustomData,
palette: userPaletteName,
paletteCustomData: userPaletteCustomData
} = window.vuex.state.config
let {
theme: userThemeV2Name,
customTheme: userThemeV2Snapshot,
customThemeSource: userThemeV2Source
} = window.vuex.state.config
let majorVersionUsed
console.debug(
`User V3 palette: ${userPaletteName}, style: ${userStyleName} , custom: ${!!userStyleCustomData}`
)
console.debug(
`User V2 name: ${userThemeV2Name}, source: ${!!userThemeV2Source}, snapshot: ${!!userThemeV2Snapshot}`
)
console.debug(`Instance V3 palette: ${instancePaletteName}, style: ${instanceStyleName}`)
console.debug('Instance V2 theme: ' + instanceThemeV2Name)
if (userPaletteName || userPaletteCustomData ||
userStyleName || userStyleCustomData ||
(
// User V2 overrides instance V3
(instancePaletteName ||
instanceStyleName) &&
instanceThemeV2Name == null &&
userThemeV2Name == null
)
) {
// Palette and/or style overrides V2 themes
instanceThemeV2Name = null
userThemeV2Name = null
userThemeV2Source = null
userThemeV2Snapshot = null
majorVersionUsed = 'v3'
} else if (
(userThemeV2Name ||
userThemeV2Snapshot ||
userThemeV2Source ||
instanceThemeV2Name)
) {
majorVersionUsed = 'v2'
} else {
// if all fails fallback to v3
majorVersionUsed = 'v3'
}
if (majorVersionUsed === 'v3') {
const result = await Promise.all([
this.fetchPalettesIndex(),
this.fetchStylesIndex()
])
palettesIndex = result[0]
stylesIndex = result[1]
} else {
// Promise.all just to be uniform with v3
const result = await Promise.all([
this.fetchThemesIndex()
])
themesIndex = result[0]
}
this.themeVersion = majorVersionUsed
console.debug('Version used', majorVersionUsed)
if (majorVersionUsed === 'v3') {
this.themeDataUsed = null
this.themeNameUsed = null
const style = await getData(
'style',
stylesIndex,
userStyleCustomData,
userStyleName || instanceStyleName
)
this.styleNameUsed = style.nameUsed
this.styleDataUsed = style.dataUsed
let firstStylePaletteName = null
style
.dataUsed
?.filter(x => x.component === '@palette')
.map(x => {
const cleanDirectives = Object.fromEntries(
Object
.entries(x.directives)
.filter(([k, v]) => k)
)
return { name: x.variant, ...cleanDirectives }
})
.forEach(palette => {
const key = 'style.' + palette.name.toLowerCase().replace(/ /g, '_')
if (!firstStylePaletteName) firstStylePaletteName = key
palettesIndex[key] = () => Promise.resolve(palette)
})
const palette = await getData(
'palette',
palettesIndex,
userPaletteCustomData,
this.useStylePalette ? firstStylePaletteName : (userPaletteName || instancePaletteName)
)
if (this.useStylePalette) {
window.vuex.commit('setOption', { name: 'palette', value: firstStylePaletteName })
}
this.paletteNameUsed = palette.nameUsed
this.paletteDataUsed = palette.dataUsed
if (this.paletteDataUsed) {
this.paletteDataUsed.link = this.paletteDataUsed.link || this.paletteDataUsed.accent
this.paletteDataUsed.accent = this.paletteDataUsed.accent || this.paletteDataUsed.link
}
if (Array.isArray(this.paletteDataUsed)) {
const [
name,
bg,
fg,
text,
link,
cRed = '#FF0000',
cGreen = '#00FF00',
cBlue = '#0000FF',
cOrange = '#E3FF00'
] = palette.dataUsed
this.paletteDataUsed = {
name,
bg,
fg,
text,
link,
accent: link,
cRed,
cBlue,
cGreen,
cOrange
}
}
console.debug('Palette data used', palette.dataUsed)
} else {
this.styleNameUsed = null
this.styleDataUsed = null
this.paletteNameUsed = null
this.paletteDataUsed = null
const theme = await getData(
'theme',
themesIndex,
userThemeV2Source || userThemeV2Snapshot,
userThemeV2Name || instanceThemeV2Name
)
this.themeNameUsed = theme.nameUsed
this.themeDataUsed = theme.dataUsed
}
},
async setThemeApplied () {
this.themeApplied = true
},
async applyTheme (
{ recompile = false } = {}
) {
const {
forceThemeRecompilation,
themeDebug,
theme3hacks
} = window.vuex.state.config
this.themeChangeInProgress = true
// If we're not not forced to recompile try using
// cache (tryLoadCache return true if load successful)
const forceRecompile = forceThemeRecompilation || recompile
if (!forceRecompile && !themeDebug && await tryLoadCache()) {
this.themeChangeInProgress = false
return this.setThemeApplied()
}
window.splashUpdate('splash.theme')
await this.getThemeData()
try {
const paletteIss = (() => {
if (!this.paletteDataUsed) return null
const result = {
component: 'Root',
directives: {}
}
Object
.entries(this.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 = this.themeDataUsed && convertTheme2To3(normalizeThemeData(this.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
} }
} }
}) })
const rulesetArray = [
theme2ruleset,
this.styleDataUsed,
paletteIss,
hacks
].filter(x => x)
return applyTheme(
rulesetArray.flat(),
() => this.setThemeApplied(),
() => {
this.themeChangeInProgress = false
},
themeDebug
)
} catch (e) {
window.splashError(e)
}
}
}
})
export const normalizeThemeData = (input) => {
let themeData, themeSource
if (input.themeFileVerison === 1) {
// this might not be even used at all, some leftover of unimplemented code in V2 editor
return generatePreset(input).theme
} else if (
Object.prototype.hasOwnProperty.call(input, '_pleroma_theme_version') ||
Object.prototype.hasOwnProperty.call(input, 'source') ||
Object.prototype.hasOwnProperty.call(input, 'theme')
) {
// We got passed a full theme file
themeData = input.theme
themeSource = input.source
} else if (
Object.prototype.hasOwnProperty.call(input, 'themeEngineVersion') ||
Object.prototype.hasOwnProperty.call(input, 'colors')
) {
// We got passed a source/snapshot
themeData = input
themeSource = input
}
// New theme presets don't have 'theme' property, they use 'source'
let out // shout, shout let it all out
if (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION) {
// There are some themes in wild that have completely broken source
out = { ...(themeData || {}), ...themeSource }
} else {
out = themeData
}
// generatePreset here basically creates/updates "snapshot",
// while also fixing the 2.2 -> 2.3 colors/shadows/etc
return generatePreset(out).theme
}

3021
yarn.lock

File diff suppressed because it is too large Load diff