clutter page unsync -> sync

This commit is contained in:
Henry Jameson 2026-01-21 16:51:08 +02:00
commit 20071d5a11
10 changed files with 144 additions and 62 deletions

View file

@ -1,7 +1,9 @@
import { throttle } from 'lodash' import { throttle } from 'lodash'
import { mapState } from 'pinia'
import { defineAsyncComponent } from 'vue' import { defineAsyncComponent } from 'vue'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
import DesktopNav from './components/desktop_nav/desktop_nav.vue' import DesktopNav from './components/desktop_nav/desktop_nav.vue'
import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue' import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue'
import FeaturesPanel from './components/features_panel/features_panel.vue' import FeaturesPanel from './components/features_panel/features_panel.vue'
@ -184,9 +186,6 @@ export default {
shoutboxPosition() { shoutboxPosition() {
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
}, },
hideShoutbox() {
return this.$store.getters.mergedConfig.hideShoutbox
},
layoutType() { layoutType() {
return useInterfaceStore().layoutType return useInterfaceStore().layoutType
}, },
@ -214,6 +213,9 @@ export default {
return window /* this.$refs.appContentRef */ return window /* this.$refs.appContentRef */
}, },
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState(useServerSideStorageStore, {
hideShoutbox: (store) => store.prefsStorage.simple.hideShoutbox,
}),
}, },
methods: { methods: {
resizeHandler() { resizeHandler() {

View file

@ -8,6 +8,7 @@ import Popover from 'src/components/popover/popover.vue'
import { pollFormToMasto } from 'src/services/poll/poll.service.js' import { pollFormToMasto } from 'src/services/poll/poll.service.js'
import { useInterfaceStore } from 'src/stores/interface.js' import { useInterfaceStore } from 'src/stores/interface.js'
import { useMediaViewerStore } from 'src/stores/media_viewer.js' import { useMediaViewerStore } from 'src/stores/media_viewer.js'
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js' import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
import fileTypeService from '../../services/file_type/file_type.service.js' import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js' import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
@ -299,10 +300,11 @@ const PostStatusForm = {
return this.hasStatusLengthLimit && this.charactersLeft < 0 return this.hasStatusLengthLimit && this.charactersLeft < 0
}, },
minimalScopesMode() { minimalScopesMode() {
return this.$store.state.instance.minimalScopesMode return useServerSideStorageStore().prefsStorage.simple.minimalScopesMode
}, },
alwaysShowSubject() { alwaysShowSubject() {
return this.mergedConfig.alwaysShowSubjectInput return useServerSideStorageStore().prefsStorage.simple
.alwaysShowSubjectInput
}, },
postFormats() { postFormats() {
return this.$store.state.instance.postFormats || [] return this.$store.state.instance.postFormats || []

View file

@ -1,5 +1,6 @@
import { cloneDeep, get, isEqual, set } from 'lodash' import { cloneDeep, get, isEqual, set } from 'lodash'
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
import DraftButtons from './draft_buttons.vue' import DraftButtons from './draft_buttons.vue'
import ModifiedIndicator from './modified_indicator.vue' import ModifiedIndicator from './modified_indicator.vue'
import ProfileSettingIndicator from './profile_setting_indicator.vue' import ProfileSettingIndicator from './profile_setting_indicator.vue'
@ -226,6 +227,8 @@ export default {
}, },
configSource() { configSource() {
switch (this.realSource) { switch (this.realSource) {
case 'server-side':
return useServerSideStorageStore().prefsStorage
case 'profile': case 'profile':
return this.$store.state.profileConfig return this.$store.state.profileConfig
case 'admin': case 'admin':
@ -239,6 +242,39 @@ export default {
return (k, v) => this.$emit('update:modelValue', v) return (k, v) => this.$emit('update:modelValue', v)
} }
switch (this.realSource) { switch (this.realSource) {
case 'server-side': {
return (path, value, operator) => {
const folder = path.split('.')[0]
if (folder === 'collections' || folder === 'objectCollections') {
switch (operator) {
case 'add':
useServerSideStorageStore().addCollectionPreference({
path,
value,
})
useServerSideStorageStore().pushServerSideStorage()
break
case 'remove':
useServerSideStorageStore().removeCollectionPreference({
path,
value,
})
useServerSideStorageStore().pushServerSideStorage()
break
default:
console.error(
`Unknown server-side collection operator ${operator}, ignoring`,
)
break
}
} else if (folder === 'simple') {
useServerSideStorageStore().setPreference({ path, value })
useServerSideStorageStore().pushServerSideStorage()
} else {
console.error(`Unknown server-side folder ${folder}, ignoring`)
}
}
}
case 'profile': case 'profile':
return (k, v) => return (k, v) =>
this.$store.dispatch('setProfileOption', { name: k, value: v }) this.$store.dispatch('setProfileOption', { name: k, value: v })
@ -262,6 +298,11 @@ export default {
switch (this.realSource) { switch (this.realSource) {
case 'profile': case 'profile':
return {} return {}
case 'server-side':
return get(
this.$store.getters.defaultConfig,
this.path.split(/\./g).slice(1),
)
default: default:
return get(this.$store.getters.defaultConfig, this.path) return get(this.$store.getters.defaultConfig, this.path)
} }

View file

@ -1,18 +1,20 @@
import { mapState as mapPiniaState } from 'pinia'
import { mapGetters, mapState } from 'vuex'
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
const SharedComputedObject = () => ({ const SharedComputedObject = () => ({
user() { ...mapPiniaState(useServerSideStorageStore, {
return this.$store.state.users.currentUser serverSide: (store) => store.state.prefsStorage,
}, }),
...mapGetters(['mergedConfig']),
...mapState({
adminConfig: (state) => state.adminSettings.config,
adminDraft: (state) => state.adminSettings.draft,
user: (state) => state.users.currentUser,
}),
expertLevel() { expertLevel() {
return this.$store.getters.mergedConfig.expertLevel > 0 return this.mergedConfig.expertLevel > 0
},
mergedConfig() {
return this.$store.getters.mergedConfig
},
adminConfig() {
return this.$store.state.adminSettings.config
},
adminDraft() {
return this.$store.state.adminSettings.draft
}, },
}) })

View file

@ -4,41 +4,58 @@
<h3>{{ $t('settings.interface') }}</h3> <h3>{{ $t('settings.interface') }}</h3>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<BooleanSetting path="alwaysShowSubjectInput"> <BooleanSetting
source="server-side"
path="simple.alwaysShowSubjectInput"
>
{{ $t('settings.subject_input_always_show') }} {{ $t('settings.subject_input_always_show') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting path="minimalScopesMode"> <BooleanSetting
source="server-side"
path="simple.minimalScopesMode"
>
{{ $t('settings.minimal_scopes_mode') }} {{ $t('settings.minimal_scopes_mode') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting path="hidePostStats"> <BooleanSetting
source="server-side"
path="simple.hidePostStats"
>
{{ $t('settings.hide_post_stats') }} {{ $t('settings.hide_post_stats') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting <BooleanSetting
path="hideUserStats" path="simple.hideUserStats"
source="server-side"
> >
{{ $t('settings.hide_user_stats') }} {{ $t('settings.hide_user_stats') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting path="hideBotIndication"> <BooleanSetting
source="server-side"
path="simple.hideBotIndication"
>
{{ $t('settings.hide_actor_type_indication') }} {{ $t('settings.hide_actor_type_indication') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting path="hideScrobbles"> <BooleanSetting
source="server-side"
path="simple.hideScrobbles"
>
{{ $t('settings.hide_scrobbles') }} {{ $t('settings.hide_scrobbles') }}
</BooleanSetting> </BooleanSetting>
<ul class="setting-list suboptions"> <ul class="setting-list suboptions">
<li> <li>
<UnitSetting <UnitSetting
key="hideScrobblesAfter" key="hideScrobblesAfter"
path="hideScrobblesAfter" source="server-side"
path="simple.hideScrobblesAfter"
:units="['m', 'h', 'd']" :units="['m', 'h', 'd']"
unit-set="time" unit-set="time"
> >
@ -52,30 +69,41 @@
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<IntegerSetting <IntegerSetting
path="maxThumbnails" source="server-side"
path="simple.maxThumbnails"
:min="0" :min="0"
> >
{{ $t('settings.max_thumbnails') }} {{ $t('settings.max_thumbnails') }}
</IntegerSetting> </IntegerSetting>
</li> </li>
<li> <li>
<BooleanSetting path="hideAttachments"> <BooleanSetting
source="server-side"
path="simple.hideAttachments"
>
{{ $t('settings.hide_attachments_in_tl') }} {{ $t('settings.hide_attachments_in_tl') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting path="hideAttachmentsInConv"> <BooleanSetting
source="server-side"
path="simple.hideAttachmentsInConv"
>
{{ $t('settings.hide_attachments_in_convo') }} {{ $t('settings.hide_attachments_in_convo') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>
<BooleanSetting path="userCardHidePersonalMarks"> <BooleanSetting
source="server-side"
path="simple.userCardHidePersonalMarks"
>
{{ $t('settings.user_card_hide_personal_marks') }} {{ $t('settings.user_card_hide_personal_marks') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li v-if="instanceShoutboxPresent"> <li v-if="instanceShoutboxPresent">
<BooleanSetting <BooleanSetting
path="hideShoutbox" source="server-side"
path="simple.hideShoutbox"
> >
{{ $t('settings.hide_shoutbox') }} {{ $t('settings.hide_shoutbox') }}
</BooleanSetting> </BooleanSetting>

View file

@ -1,4 +1,5 @@
import { unescape as ldUnescape, uniqBy } from 'lodash' import { unescape as ldUnescape, uniqBy } from 'lodash'
import { mapState } from 'pinia'
import MentionLink from 'src/components/mention_link/mention_link.vue' import MentionLink from 'src/components/mention_link/mention_link.vue'
import MentionsLine from 'src/components/mentions_line/mentions_line.vue' import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
@ -254,12 +255,7 @@ const Status = {
return !!this.currentUser return !!this.currentUser
}, },
muteFilterHits() { muteFilterHits() {
return muteFilterHits( return muteFilterHits(Object.values(this.muteFilters), this.status)
Object.values(
useServerSideStorageStore().prefsStorage.simple.muteFilters,
),
this.status,
)
}, },
botStatus() { botStatus() {
return this.status.user.actor_type === 'Service' return this.status.user.actor_type === 'Service'
@ -452,9 +448,6 @@ const Status = {
.map((tagObj) => tagObj.name) .map((tagObj) => tagObj.name)
.join(' ') .join(' ')
}, },
hidePostStats() {
return this.mergedConfig.hidePostStats
},
shouldDisplayFavsAndRepeats() { shouldDisplayFavsAndRepeats() {
return ( return (
!this.hidePostStats && !this.hidePostStats &&
@ -511,10 +504,10 @@ const Status = {
return this.quotedStatus && this.displayQuote return this.quotedStatus && this.displayQuote
}, },
scrobblePresent() { scrobblePresent() {
if (this.mergedConfig.hideScrobbles) return false if (this.hideScrobbles) return false
if (!this.status.user?.latestScrobble) return false if (!this.status.user?.latestScrobble) return false
const value = this.mergedConfig.hideScrobblesAfter.match(/\d+/gs)[0] const value = this.hideScrobblesAfter.match(/\d+/gs)[0]
const unit = this.mergedConfig.hideScrobblesAfter.match(/\D+/gs)[0] const unit = this.hideScrobblesAfter.match(/\D+/gs)[0]
let multiplier = 60 * 1000 // minutes is smallest unit let multiplier = 60 * 1000 // minutes is smallest unit
switch (unit) { switch (unit) {
case 'm': case 'm':
@ -536,6 +529,14 @@ const Status = {
scrobble() { scrobble() {
return this.status.user?.latestScrobble return this.status.user?.latestScrobble
}, },
...mapState(useServerSideStorageStore, {
muteFilters: (store) => store.prefsStorage.simple.muteFilters,
hideBotIndicatior: (store) => store.prefsStorage.simple.hideBotIndicator,
hidePostStats: (store) => store.prefsStorage.simple.hidePostStats,
hideScrobbles: (store) => store.prefsStorage.simple.hideScrobbles,
hideScrobblesAfter: (store) =>
store.prefsStorage.simple.hideScrobblesAfter,
}),
}, },
methods: { methods: {
visibilityIcon(visibility) { visibilityIcon(visibility) {

View file

@ -1,7 +1,9 @@
import { mapState as mapPiniaState } from 'pinia'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
import StatusBody from 'src/components/status_body/status_body.vue' import StatusBody from 'src/components/status_body/status_body.vue'
import { useMediaViewerStore } from 'src/stores/media_viewer' import { useMediaViewerStore } from 'src/stores/media_viewer'
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
import Attachment from '../attachment/attachment.vue' import Attachment from '../attachment/attachment.vue'
import Gallery from '../gallery/gallery.vue' import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue' import LinkPreview from '../link-preview/link-preview.vue'
@ -90,8 +92,8 @@ const StatusContent = {
}, },
hideAttachments() { hideAttachments() {
return ( return (
(this.mergedConfig.hideAttachments && !this.inConversation) || (this.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation) (this.hideAttachmentsInConv && this.inConversation)
) )
}, },
nsfwClickthrough() { nsfwClickthrough() {
@ -110,21 +112,24 @@ const StatusContent = {
if (this.compact) { if (this.compact) {
return 'small' return 'small'
} else if ( } else if (
(this.mergedConfig.hideAttachments && !this.inConversation) || (this.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation) || (this.hideAttachmentsInConv && this.inConversation) ||
this.status.attachments.length > this.maxThumbnails this.status.attachments.length > this.maxThumbnails
) { ) {
return 'hide' return 'hide'
} }
return 'normal' return 'normal'
}, },
maxThumbnails() {
return this.mergedConfig.maxThumbnails
},
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState({ ...mapState({
currentUser: (state) => state.users.currentUser, currentUser: (state) => state.users.currentUser,
}), }),
...mapPiniaState(useServerSideStorageStore, {
maxThumbnails: (store) => store.prefsStorage.simple.maxThumbnails,
hideAttachments: (store) => store.prefsStorage.simple.hideAttachments,
hideAttachmentsInConv: (store) =>
store.prefsStorage.simple.hideAttachmentsInConv,
}),
}, },
components: { components: {
Attachment, Attachment,

View file

@ -1,6 +1,7 @@
import isEqual from 'lodash/isEqual' import isEqual from 'lodash/isEqual'
import merge from 'lodash/merge' import merge from 'lodash/merge'
import ldUnescape from 'lodash/unescape' import ldUnescape from 'lodash/unescape'
import { mapState } from 'pinia'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
@ -15,6 +16,7 @@ import { propsToNative } from 'src/services/attributes_helper/attributes_helper.
import localeService from 'src/services/locale/locale.service.js' import localeService from 'src/services/locale/locale.service.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { usePostStatusStore } from 'src/stores/post_status' import { usePostStatusStore } from 'src/stores/post_status'
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
import { useInterfaceStore } from '../../stores/interface' import { useInterfaceStore } from '../../stores/interface'
import { useMediaViewerStore } from '../../stores/media_viewer' import { useMediaViewerStore } from '../../stores/media_viewer'
import AccountActions from '../account_actions/account_actions.vue' import AccountActions from '../account_actions/account_actions.vue'
@ -217,13 +219,11 @@ export default {
}, },
userHighlightType: { userHighlightType: {
get() { get() {
const data = const data = this.mergedConfig.highlight[this.user.screen_name]
this.$store.getters.mergedConfig.highlight[this.user.screen_name]
return (data && data.type) || 'disabled' return (data && data.type) || 'disabled'
}, },
set(type) { set(type) {
const data = const data = this.mergedConfig.highlight[this.user.screen_name]
this.$store.getters.mergedConfig.highlight[this.user.screen_name]
if (type !== 'disabled') { if (type !== 'disabled') {
this.$store.dispatch('setHighlight', { this.$store.dispatch('setHighlight', {
user: this.user.screen_name, user: this.user.screen_name,
@ -237,12 +237,10 @@ export default {
}) })
} }
}, },
...mapGetters(['mergedConfig']),
}, },
userHighlightColor: { userHighlightColor: {
get() { get() {
const data = const data = this.mergedConfig.highlight[this.user.screen_name]
this.$store.getters.mergedConfig.highlight[this.user.screen_name]
return data && data.color return data && data.color
}, },
set(color) { set(color) {
@ -386,6 +384,11 @@ export default {
}) })
}, },
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState(useServerSideStorageStore, {
hideUserStats: (store) => store.prefsStorage.simple.hideUserStats,
userCardHidePersonalMarks: (store) =>
store.prefsStorage.simple.userCardHidePersonalMarks,
}),
}, },
methods: { methods: {
muteUser() { muteUser() {

View file

@ -293,7 +293,7 @@
</div> </div>
</div> </div>
<div <div
v-if="!editable && loggedIn && isOtherUser && (hasNote || !hideBio) && !mergedConfig.userCardHidePersonalMarks" v-if="!editable && loggedIn && isOtherUser && (hasNote || !hideBio) && !userCardHidePersonalMarks"
class="personal-marks" class="personal-marks"
> >
<UserNote <UserNote
@ -505,11 +505,11 @@
class="user-extras" class="user-extras"
> >
<span <span
v-if="!editable && !mergedConfig.hideUserStats" v-if="!editable && !hideUserStats"
class="user-stats" class="user-stats"
> >
<dl <dl
v-if="!mergedConfig.hideUserStats && !hideBio" v-if="!hideUserStats && !hideBio"
class="user-count" class="user-count"
> >
<dd>{{ user.statuses_count }}</dd> <dd>{{ user.statuses_count }}</dd>

View file

@ -318,9 +318,7 @@ const updateProfileJSON = ({ credentials, params }) => {
credentials, credentials,
payload: params, payload: params,
method: 'PATCH', method: 'PATCH',
}) }).then((data) => parseUser(data))
.then((data) => data.json())
.then((data) => parseUser(data))
} }
// Params needed: // Params needed: