diff --git a/src/App.js b/src/App.js index 4fcfaf2c6..19d28f843 100644 --- a/src/App.js +++ b/src/App.js @@ -156,7 +156,7 @@ export default { } } }, - shout() { + shoutJoined() { return useShoutStore().joined }, isChats() { diff --git a/src/App.vue b/src/App.vue index e5e088bc3..f432d8bc4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -60,8 +60,8 @@ /> - - + {{ ' ' }} @@ -99,7 +100,7 @@ override-backend-description override-backend-description-label override-available-options - :options="limitLocalContentOptions" + :options="[...limitLocalContentOptions]" path=":pleroma.:instance.:limit_to_local_content" /> diff --git a/src/components/settings_modal/helpers/attachment_setting.js b/src/components/settings_modal/helpers/attachment_setting.js index 7ba910950..cbcd4abf8 100644 --- a/src/components/settings_modal/helpers/attachment_setting.js +++ b/src/components/settings_modal/helpers/attachment_setting.js @@ -29,7 +29,7 @@ export default { // The "server" part is primarily for local dev, but could be useful for alt-domain or multiuser usage. const url = path.includes('://') ? path : useInstanceStore().server + path return { - mimetype: fileTypeExt(url), + type: fileTypeExt(url), url, } }, diff --git a/src/components/settings_modal/helpers/color_setting.vue b/src/components/settings_modal/helpers/color_setting.vue index 26789ee09..f8f3b9427 100644 --- a/src/components/settings_modal/helpers/color_setting.vue +++ b/src/components/settings_modal/helpers/color_setting.vue @@ -20,6 +20,7 @@ {{ ' ' }} + + + {{ ' ' }} + @@ -29,13 +36,6 @@ :value="realDraftMode ? draft :state" @change="update" > - {{ ' ' }} - - -

diff --git a/src/components/settings_modal/tabs/clutter_tab.vue b/src/components/settings_modal/tabs/clutter_tab.vue index fe75582ed..8ae416ff8 100644 --- a/src/components/settings_modal/tabs/clutter_tab.vue +++ b/src/components/settings_modal/tabs/clutter_tab.vue @@ -62,12 +62,18 @@

  • - + {{ $t('settings.hide_attachments_in_tl') }}
  • - + {{ $t('settings.hide_attachments_in_convo') }}
  • diff --git a/src/components/settings_modal/tabs/composing_tab.js b/src/components/settings_modal/tabs/composing_tab.js index 908582659..b2ddbe54a 100644 --- a/src/components/settings_modal/tabs/composing_tab.js +++ b/src/components/settings_modal/tabs/composing_tab.js @@ -12,6 +12,7 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceStore } from 'src/stores/instance.js' +import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' import localeService from 'src/services/locale/locale.service.js' @@ -104,7 +105,7 @@ const ComposingTab = { }, computed: { postFormats() { - return useInstanceStore().postFormats || [] + return useInstanceCapabilitiesStore().postFormats }, postContentOptions() { return this.postFormats.map((format) => ({ diff --git a/src/components/settings_modal/tabs/composing_tab.vue b/src/components/settings_modal/tabs/composing_tab.vue index 4b6c0bb51..755539096 100644 --- a/src/components/settings_modal/tabs/composing_tab.vue +++ b/src/components/settings_modal/tabs/composing_tab.vue @@ -29,6 +29,7 @@ id="postContentType" path="postContentType" :options="postContentOptions" + :local="true" > {{ $t('settings.default_post_status_content_type') }} @@ -90,6 +91,7 @@
  • {{ $t('settings.image_compression') }} @@ -98,6 +100,7 @@
  • diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 905a2baec..a3cb5be07 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -68,7 +68,6 @@ name="ui" :label="$t('settings.style.fonts.components_inline.interface')" :fallback="{ family: 'sans-serif' }" - :is-local="true" no-inherit="1" @update:model-value="v => updateFont('interface', v)" /> @@ -77,7 +76,6 @@ {{ $t('settings.emoji_reactions_scale') }} diff --git a/src/components/settings_modal/tabs/layout_tab.vue b/src/components/settings_modal/tabs/layout_tab.vue index acae859a0..ca514e0cb 100644 --- a/src/components/settings_modal/tabs/layout_tab.vue +++ b/src/components/settings_modal/tabs/layout_tab.vue @@ -40,6 +40,7 @@
  • @@ -83,12 +84,18 @@
  • - + {{ $t('settings.right_sidebar') }}
  • - + {{ $t('settings.navbar_column_stretch') }}
  • diff --git a/src/components/user_avatar/user_avatar.js b/src/components/user_avatar/user_avatar.js index fdf766f58..d484303c1 100644 --- a/src/components/user_avatar/user_avatar.js +++ b/src/components/user_avatar/user_avatar.js @@ -37,7 +37,7 @@ const UserAvatar = { data() { return { showPlaceholder: false, - defaultAvatar: `${useInstanceStore().server + useInstanceStore().defaultAvatar}`, + defaultAvatar: `${useInstanceStore().server + useInstanceStore().instanceIdentity.defaultAvatar}`, betterShadow: useInterfaceStore().browserSupport.cssFilter, } }, diff --git a/src/modules/api.js b/src/modules/api.js index d6bf27f23..6f4b8b15f 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -345,7 +345,7 @@ const api = { // Set up websocket connection const token = state.wsToken if ( - useInstanceStore().shoutAvailable && + useInstanceCapabilitiesStore().shoutAvailable && typeof token !== 'undefined' && state.socket === null ) { diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index af2f60ac4..fbfaf08a3 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -45,7 +45,6 @@ export const instanceDefaultConfig = { muteSensitiveStatuses: false, collapseMessageWithSubject: false, padEmoji: true, - hideAttachments: false, hideAttachmentsInConv: false, hideScrobbles: false, hideScrobblesAfter: '2d', @@ -95,12 +94,9 @@ export const instanceDefaultConfig = { webPushAlwaysShowNotifications: false, interfaceLanguage: browserLocale, hideScopeNotice: false, - useStreamingApi: false, - sidebarRight: false, scopeCopy: true, subjectLineBehavior: 'email', alwaysShowSubjectInput: true, - postContentType: 'text/plain', minimalScopesMode: false, // This hides statuses filtered via a word filter @@ -133,19 +129,8 @@ export const instanceDefaultConfig = { userPopoverOverlay: false, userCardLeftJustify: false, userCardHidePersonalMarks: false, - sidebarColumnWidth: '25rem', - contentColumnWidth: '45rem', - notifsColumnWidth: '25rem', - themeEditorMinWidth: '0rem', - emojiReactionsScale: 0.5, - textSize: '1rem', - emojiSize: '2.2rem', - navbarSize: '3.5rem', - panelHeaderSize: '3.2rem', forcedRoundness: -1, - navbarColumnStretch: false, greentext: false, - mentionLinkDisplay: 'short', mentionLinkShowTooltip: true, mentionLinkShowAvatar: false, mentionLinkFadeDomain: true, @@ -175,8 +160,27 @@ export const instanceDefaultConfig = { useAbsoluteTimeFormat: false, absoluteTimeFormatMinAge: '0d', absoluteTime12h: '24h', - imageCompression: true, +} + +export const defaultConfigLocal = { + hideAttachments: false, + hideAttachmentsInConv: false, + postContentType: 'text/plain', + sidebarRight: false, + sidebarColumnWidth: '25rem', + contentColumnWidth: '45rem', + notifsColumnWidth: '25rem', + themeEditorMinWidth: '0rem', + emojiReactionsScale: 0.5, + textSize: '1rem', + emojiSize: '2.2rem', + navbarSize: '3.5rem', + panelHeaderSize: '3.2rem', + navbarColumnStretch: false, + mentionLinkDisplay: 'short', alwaysUseJpeg: false, + imageCompression: true, + useStreamingApi: false, } export const makeUndefined = (c) => diff --git a/src/modules/old_default_config_state.js b/src/modules/old_default_config_state.js index 3977a5d89..e53cafe29 100644 --- a/src/modules/old_default_config_state.js +++ b/src/modules/old_default_config_state.js @@ -1,129 +1,187 @@ // this is a snapshot of config keys used prior to sync config. // used to migrate from old config. -export const defaultStateKeys = [ - 'expertLevel', - 'hideISP', - 'hideInstanceWallpaper', - 'hideShoutbox', - 'hideMutedPosts', - 'hideMutedThreads', - 'hideWordFilteredPosts', - 'muteBotStatuses', - 'muteSensitiveStatuses', - 'collapseMessageWithSubject', - 'padEmoji', - 'hideAttachments', - 'hideAttachmentsInConv', - 'hideScrobbles', - 'hideScrobblesAfter', - 'maxThumbnails', - 'hideNsfw', - 'preloadImage', - 'loopVideo', - 'loopVideoSilentOnly', - 'streaming', - 'emojiReactionsOnTimeline', - 'alwaysShowNewPostButton', - 'autohideFloatingPostButton', - 'pauseOnUnfocused', - 'stopGifs', - 'replyVisibility', - 'thirdColumnMode', - 'notificationVisibility', - 'notificationNative', - 'webPushNotifications', - 'webPushAlwaysShowNotifications', - 'interfaceLanguage', - 'hideScopeNotice', - 'useStreamingApi', - 'sidebarRight', - 'scopeCopy', - 'subjectLineBehavior', - 'alwaysShowSubjectInput', - 'postContentType', - 'minimalScopesMode', - 'hideFilteredStatuses', - 'modalOnRepeat', - 'modalOnUnfollow', - 'modalOnBlock', - 'modalOnMute', - 'modalOnMuteConversation', - 'modalOnMuteDomain', - 'modalOnDelete', - 'modalOnLogout', - 'modalOnApproveFollow', - 'modalOnDenyFollow', - 'modalOnRemoveUserFromFollowers', - 'onMuteDefaultAction', - 'onBlockDefaultAction', - 'modalMobileCenter', - 'playVideosInModal', - 'useOneClickNsfw', - 'useContainFit', - 'disableStickyHeaders', - 'showScrollbars', - 'userPopoverAvatarAction', - 'userPopoverOverlay', - 'userCardLeftJustify', - 'userCardHidePersonalMarks', - 'sidebarColumnWidth', - 'contentColumnWidth', - 'notifsColumnWidth', - 'themeEditorMinWidth', - 'emojiReactionsScale', - 'textSize', - 'emojiSize', - 'navbarSize', - 'panelHeaderSize', - 'forcedRoundness', - 'navbarColumnStretch', - 'greentext', - 'mentionLinkDisplay', - 'mentionLinkShowTooltip', - 'mentionLinkShowAvatar', - 'mentionLinkFadeDomain', - 'mentionLinkShowYous', - 'mentionLinkBoldenYou', - 'hidePostStats', - 'hideBotIndication', - 'hideUserStats', - 'virtualScrolling', - 'sensitiveByDefault', - 'conversationDisplay', - 'conversationTreeAdvanced', - 'conversationOtherRepliesButton', - 'conversationTreeFadeAncestors', - 'showExtraNotifications', - 'showExtraNotificationsTip', - 'showChatsInExtraNotifications', - 'showAnnouncementsInExtraNotifications', - 'showFollowRequestsInExtraNotifications', - 'maxDepthInThread', - 'autocompleteSelect', - 'closingDrawerMarksAsSeen', - 'unseenAtTop', - 'ignoreInactionableSeen', - 'unsavedPostAction', - 'autoSaveDraft', - 'useAbsoluteTimeFormat', - 'absoluteTimeFormatMinAge', - 'absoluteTime12h', - 'imageCompression', - 'alwaysUseJpeg', - 'theme', - 'colors', +// commented entries are unsynced stuff +export const defaultConfigSync = { + expertLevel: 0, // used to track which settings to show and hide + hideISP: false, + hideInstanceWallpaper: false, + hideShoutbox: false, + // bad name: actually hides posts of muted USERS + hideMutedPosts: false, + hideMutedThreads: true, + hideWordFilteredPosts: false, + muteBotStatuses: false, + muteSensitiveStatuses: false, + collapseMessageWithSubject: false, + padEmoji: true, + hideScrobbles: false, + hideScrobblesAfter: '2d', + maxThumbnails: 16, + hideNsfw: true, + preloadImage: true, + loopVideo: true, + loopVideoSilentOnly: true, + /// This is not the streaming API configuration, but rather an option + /// for automatically loading new posts into the timeline without + /// the user clicking the Show New button. + 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, + //interfaceLanguage: '', + hideScopeNotice: false, + useStreamingApi: false, + sidebarRight: false, + scopeCopy: true, + subjectLineBehavior: 'email', + alwaysShowSubjectInput: true, + postContentType: 'text/plain', + minimalScopesMode: false, - 'customTheme', - 'customThemeSource', + // This hides statuses filtered via a word filter + hideFilteredStatuses: false, - 'style', - 'styleCustomData', - 'palette', - 'paletteCustomData', - 'themeDebug', - 'forceThemeRecompilation', - 'theme3hacks', - // 'muteWords', // mutes migrated separately - // 'highlight', // highlight migration is done separately -] + // Confirmations + modalOnRepeat: false, + modalOnUnfollow: false, + modalOnBlock: true, + modalOnMute: false, + modalOnMuteConversation: false, + modalOnMuteDomain: true, + modalOnDelete: true, + modalOnLogout: true, + modalOnApproveFollow: false, + modalOnDenyFollow: false, + modalOnRemoveUserFromFollowers: false, + + // Expiry confirmations/default actions + onMuteDefaultAction: 'ask', + onBlockDefaultAction: 'ask', + + modalMobileCenter: false, + playVideosInModal: false, + useOneClickNsfw: false, + useContainFit: true, + disableStickyHeaders: false, + showScrollbars: false, + userPopoverAvatarAction: 'open', + userPopoverOverlay: false, + userCardLeftJustify: false, + userCardHidePersonalMarks: false, + forcedRoundness: -1, + greentext: false, + mentionLinkShowTooltip: true, + mentionLinkShowAvatar: false, + mentionLinkFadeDomain: true, + mentionLinkShowYous: false, + mentionLinkBoldenYou: true, + hidePostStats: false, + hideBotIndication: false, + hideUserStats: false, + virtualScrolling: true, + sensitiveByDefault: false, + conversationDisplay: 'linear', + conversationTreeAdvanced: false, + conversationOtherRepliesButton: 'below', + conversationTreeFadeAncestors: false, + showExtraNotifications: true, + showExtraNotificationsTip: true, + showChatsInExtraNotifications: true, + showAnnouncementsInExtraNotifications: true, + showFollowRequestsInExtraNotifications: true, + maxDepthInThread: 6, + autocompleteSelect: false, + closingDrawerMarksAsSeen: true, + unseenAtTop: false, + ignoreInactionableSeen: false, + unsavedPostAction: 'confirm', + autoSaveDraft: false, + useAbsoluteTimeFormat: false, + absoluteTimeFormatMinAge: '0d', + absoluteTime12h: '24h', + theme3hacks: { + // Hacks, user overrides that are independent of theme used + underlay: 'none', + fonts: { + interface: undefined, + input: undefined, + post: undefined, + monospace: undefined, + }, + }, + // Special processing + // 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 + + // Those are handled outside config now + // muteWords: [], + // highlight: {}, +} + +export const defaultConfigLocal = { + hideAttachments: false, + hideAttachmentsInConv: false, + sidebarColumnWidth: '25rem', + contentColumnWidth: '45rem', + notifsColumnWidth: '25rem', + themeEditorMinWidth: '0rem', + emojiReactionsScale: 0.5, + textSize: '1rem', + emojiSize: '2.2rem', + navbarSize: '3.5rem', + panelHeaderSize: '3.2rem', + navbarColumnStretch: false, + mentionLinkDisplay: 'short', + imageCompression: true, + alwaysUseJpeg: false, + imageCompression: true, + alwaysUseJpeg: false, +} diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index c460b38b0..e43356e8e 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -22,7 +22,7 @@ import { useLocalConfigStore } from 'src/stores/local_config.js' import { storage } from 'src/lib/storage.js' import { defaultState as configDefaultState } from 'src/modules/default_config_state.js' -import { defaultStateKeys } from 'src/modules/old_default_config_state.js' +import { defaultConfigSync } from 'src/modules/old_default_config_state.js' export const VERSION = 2 export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically @@ -38,7 +38,6 @@ export const defaultState = { // storage of flags - stuff that can only be set and incremented flagStorage: { updateCounter: 0, // Counter for most recent update notification seen - configMigration: 0, // Counter for config -> server-side migrations reset: 0, // special flag that can be used to force-reset all data, debug purposes only // special reset codes: // 1000: trim keys to those known by currently running FE @@ -417,14 +416,6 @@ export const _doMigrations = async (data, setPreference) => { console.debug( 'Data has older version, seeing if there any migrations that can be applied', ) - - if (data._version === 1) { - // Migrate old config to sync config - const vuexState = await storage.getItem('vuex-lz') - defaultStateKeys.forEach((key) => { - setPreference({ path: `simple.${key}`, value: vuexState.config[key] }) - }) - } } if (data._version > VERSION) { @@ -623,6 +614,31 @@ export const useSyncConfigStore = defineStore('sync_config', { const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage let dirty = false + console.debug('Migrating from old config') + const vuexState = await storage.getItem('vuex-lz') + const { config } = vuexState + + const migratedEntries = new Set(config._syncMigration ?? []) + console.debug(`Already migrated Values: ${[...migratedEntries].join()}`) + + Object.entries(defaultConfigSync).forEach(([key, value]) => { + const oldValue = config[key] + const defaultValue = value + + const present = oldValue !== undefined + const migrated = migratedEntries.has(key) + const different = !isEqual(oldValue, defaultValue) + + if (present && !migrated && different) { + console.debug(`Migrating config ${key}: ${oldValue}`,) + this.setPreference({ path: `simple.${key}`, oldValue }) + migratedEntries.add(key) + needUpload = true + } + }) + vuexState.config._syncMigration = [...migratedEntries] + storage.setItem('vuex-lz', vuexState) + if (recent === null) { console.debug( `Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`, @@ -641,7 +657,12 @@ export const useSyncConfigStore = defineStore('sync_config', { // discarding timestamps and versions const { _timestamp: _0, _version: _1, ...recentData } = recent const { _timestamp: _2, _version: _3, ...staleData } = stale - dirty = !isEqual(recentData, staleData) + dirty = !isEqual( + // Something wrong happens if we compare both objects directly + // or with cloneDeep() + JSON.parse(JSON.stringify(recentData)), + JSON.parse(JSON.stringify(staleData)), + ) console.debug(`Data ${dirty ? 'needs' : "doesn't need"} merging`) }