Merge branch 'setttingssync' into shigusegubu-themes3
This commit is contained in:
commit
0a7a2f0da4
78 changed files with 917 additions and 476 deletions
|
|
@ -17,7 +17,7 @@
|
|||
"ci-eslint": "yarn exec eslint",
|
||||
"ci-stylelint": "yarn exec stylelint '**/*.scss' '**/*.vue'",
|
||||
"lint": "yarn ci-biome; yarn ci-eslint; yarn ci-stylelint",
|
||||
"lint-fix": "yarn exec eslint --fix; yarn exec stylelint '**/*.scss' '**/*.vue' --fix; biome check --write"
|
||||
"lint-fix": "yarn exec eslint -- --fix; yarn exec stylelint '**/*.scss' '**/*.vue' --fix; biome check --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.4",
|
||||
|
|
|
|||
19
src/App.js
19
src/App.js
|
|
@ -26,6 +26,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useShoutStore } from 'src/stores/shout.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
|
@ -72,7 +73,7 @@ export default {
|
|||
},
|
||||
created() {
|
||||
// Load the locale from the storage
|
||||
const val = this.$store.getters.mergedConfig.interfaceLanguage
|
||||
const val = useSyncConfigStore().mergedConfig.interfaceLanguage
|
||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
document.getElementById('modal').classList = ['-' + this.layoutType]
|
||||
|
||||
|
|
@ -122,7 +123,7 @@ export default {
|
|||
]
|
||||
},
|
||||
navClasses() {
|
||||
const { navbarColumnStretch } = this.$store.getters.mergedConfig
|
||||
const { navbarColumnStretch } = useSyncConfigStore().mergedConfig
|
||||
return [
|
||||
'-' + this.layoutType,
|
||||
...(navbarColumnStretch ? ['-column-stretch'] : []),
|
||||
|
|
@ -157,19 +158,19 @@ export default {
|
|||
if (this.isChats) return false
|
||||
if (this.isListEdit) return false
|
||||
return (
|
||||
this.$store.getters.mergedConfig.alwaysShowNewPostButton ||
|
||||
useSyncConfigStore().mergedConfig.alwaysShowNewPostButton ||
|
||||
this.layoutType === 'mobile'
|
||||
)
|
||||
},
|
||||
shoutboxPosition() {
|
||||
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
|
||||
return useSyncConfigStore().mergedConfig.alwaysShowNewPostButton || false
|
||||
},
|
||||
hideShoutbox() {
|
||||
return this.$store.getters.mergedConfig.hideShoutbox
|
||||
return useSyncConfigStore().mergedConfig.hideShoutbox
|
||||
},
|
||||
reverseLayout() {
|
||||
const { thirdColumnMode, sidebarRight: reverseSetting } =
|
||||
this.$store.getters.mergedConfig
|
||||
useSyncConfigStore().mergedConfig
|
||||
if (this.layoutType !== 'wide') {
|
||||
return reverseSetting
|
||||
} else {
|
||||
|
|
@ -179,10 +180,10 @@ export default {
|
|||
}
|
||||
},
|
||||
noSticky() {
|
||||
return this.$store.getters.mergedConfig.disableStickyHeaders
|
||||
return useSyncConfigStore().mergedConfig.disableStickyHeaders
|
||||
},
|
||||
showScrollbars() {
|
||||
return this.$store.getters.mergedConfig.showScrollbars
|
||||
return useSyncConfigStore().mergedConfig.showScrollbars
|
||||
},
|
||||
scrollParent() {
|
||||
return window /* this.$refs.appContentRef */
|
||||
|
|
@ -190,7 +191,7 @@ export default {
|
|||
showInstanceSpecificPanel() {
|
||||
return (
|
||||
this.instanceSpecificPanelPresent &&
|
||||
!this.$store.getters.mergedConfig.hideISP
|
||||
!useSyncConfigStore().mergedConfig.hideISP
|
||||
)
|
||||
},
|
||||
...mapGetters(['mergedConfig']),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ config.autoAddCss = false
|
|||
import App from '../App.vue'
|
||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
||||
import FaviconService from '../services/favicon_service/favicon_service.js'
|
||||
import { applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import { applyStyleConfig } from '../services/style_setter/style_setter.js'
|
||||
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
|
||||
import {
|
||||
windowHeight,
|
||||
|
|
@ -88,25 +88,25 @@ const getInstanceConfig = async ({ store }) => {
|
|||
data.pleroma,
|
||||
)
|
||||
useInstanceStore().set({
|
||||
name: 'textlimit',
|
||||
path: 'textlimit',
|
||||
value: textlimit,
|
||||
})
|
||||
useInstanceStore().set({
|
||||
name: 'accountApprovalRequired',
|
||||
path: 'accountApprovalRequired',
|
||||
value: data.approval_required,
|
||||
})
|
||||
useInstanceStore().set({
|
||||
name: 'birthdayRequired',
|
||||
path: 'birthdayRequired',
|
||||
value: !!data.pleroma?.metadata.birthday_required,
|
||||
})
|
||||
useInstanceStore().set({
|
||||
name: 'birthdayMinAge',
|
||||
path: 'birthdayMinAge',
|
||||
value: data.pleroma?.metadata.birthday_min_age || 0,
|
||||
})
|
||||
|
||||
if (vapidPublicKey) {
|
||||
useInstanceStore().set({
|
||||
name: 'vapidPublicKey',
|
||||
path: 'vapidPublicKey',
|
||||
value: vapidPublicKey,
|
||||
})
|
||||
}
|
||||
|
|
@ -258,7 +258,7 @@ const getAppSecret = async ({ store }) => {
|
|||
const resolveStaffAccounts = ({ store, accounts }) => {
|
||||
const nicknames = accounts.map((uri) => uri.split('/').pop())
|
||||
useInstanceStore().set({
|
||||
name: 'staffAccounts',
|
||||
path: 'staffAccounts',
|
||||
value: nicknames,
|
||||
})
|
||||
}
|
||||
|
|
@ -384,20 +384,20 @@ const getNodeInfo = async ({ store }) => {
|
|||
|
||||
const software = data.software
|
||||
useInstanceStore().set({
|
||||
name: 'backendVersion',
|
||||
path: 'backendVersion',
|
||||
value: software.version,
|
||||
})
|
||||
useInstanceStore().set({
|
||||
name: 'backendRepository',
|
||||
path: 'backendRepository',
|
||||
value: software.repository,
|
||||
})
|
||||
|
||||
const priv = metadata.private
|
||||
useInstanceStore().set({ name: 'privateMode', value: priv })
|
||||
useInstanceStore().set({ path: 'privateMode', value: priv })
|
||||
|
||||
const frontendVersion = window.___pleromafe_commit_hash
|
||||
useInstanceStore().set({
|
||||
name: 'frontendVersion',
|
||||
path: 'frontendVersion',
|
||||
value: frontendVersion,
|
||||
})
|
||||
|
||||
|
|
@ -547,7 +547,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
|||
return Promise.reject(e)
|
||||
}
|
||||
|
||||
applyConfig(store.state.config, i18n.global)
|
||||
applyStyleConfig(store.state.config, i18n.global)
|
||||
|
||||
// Now we can try getting the server settings and logging in
|
||||
// Most of these are preloaded into the index.html so blocking is minimized
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import StaffPanel from '../staff_panel/staff_panel.vue'
|
|||
import TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_panel.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const About = {
|
||||
components: {
|
||||
|
|
@ -21,7 +22,7 @@ const About = {
|
|||
showInstanceSpecificPanel() {
|
||||
return (
|
||||
useInstanceStore().instanceIdentity.showInstanceSpecificPanel &&
|
||||
!this.$store.getters.mergedConfig.hideISP &&
|
||||
!useSyncConfigStore().mergedConfig.hideISP &&
|
||||
useInstanceStore().instanceIdentity.instanceSpecificPanelContent
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import ProgressButton from '../progress_button/progress_button.vue'
|
|||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useReportsStore } from 'src/stores/reports'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -89,10 +90,10 @@ const AccountActions = {
|
|||
},
|
||||
computed: {
|
||||
shouldConfirmBlock() {
|
||||
return this.$store.getters.mergedConfig.modalOnBlock
|
||||
return useSyncConfigStore().mergedConfig.modalOnBlock
|
||||
},
|
||||
shouldConfirmRemoveUserFromFollowers() {
|
||||
return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers
|
||||
return useSyncConfigStore().mergedConfig.modalOnRemoveUserFromFollowers
|
||||
},
|
||||
...mapState(useInstanceCapabilitiesStore, [
|
||||
'blockExpiration',
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import VideoAttachment from '../video_attachment/video_attachment.vue'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useMediaViewerStore } from 'src/stores/media_viewer'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -58,8 +59,8 @@ const Attachment = {
|
|||
localDescription: this.description || this.attachment.description,
|
||||
nsfwImage:
|
||||
useInstanceStore().instanceIdentity.nsfwCensorImage || nsfwImage,
|
||||
hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw,
|
||||
preloadImage: this.$store.getters.mergedConfig.preloadImage,
|
||||
hideNsfwLocal: useSyncConfigStore().mergedConfig.hideNsfw,
|
||||
preloadImage: useSyncConfigStore().mergedConfig.preloadImage,
|
||||
loading: false,
|
||||
img:
|
||||
fileTypeService.fileType(this.attachment.mimetype) === 'image' &&
|
||||
|
|
@ -93,7 +94,7 @@ const Attachment = {
|
|||
return this.size === 'hide'
|
||||
},
|
||||
useContainFit() {
|
||||
return this.$store.getters.mergedConfig.useContainFit
|
||||
return useSyncConfigStore().mergedConfig.useContainFit
|
||||
},
|
||||
placeholderName() {
|
||||
if (this.attachment.description === '' || !this.attachment.description) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import Select from 'src/components/select/select.vue'
|
||||
import ConfirmModal from './confirm_modal.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default {
|
||||
props: ['type', 'user', 'status'],
|
||||
emits: ['hide', 'show', 'muted'],
|
||||
|
|
@ -43,7 +45,7 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
...mapGetters(['mergedConfig']),
|
||||
...mapState(useSyncConfigStore, ['mergedConfig']),
|
||||
},
|
||||
methods: {
|
||||
optionallyPrompt() {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import Status from '../status/status.vue'
|
|||
import ThreadTree from '../thread_tree/thread_tree.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -81,7 +82,7 @@ const conversation = {
|
|||
// maxDepthInThread = max number of depths that is *visible*
|
||||
// since our depth starts with 0 and "showing" means "showing children"
|
||||
// there is a -2 here
|
||||
const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2
|
||||
const maxDepth = useSyncConfigStore().mergedConfig.maxDepthInThread - 2
|
||||
return maxDepth >= 1 ? maxDepth : 1
|
||||
},
|
||||
streamingEnabled() {
|
||||
|
|
@ -91,22 +92,22 @@ const conversation = {
|
|||
)
|
||||
},
|
||||
displayStyle() {
|
||||
return this.$store.getters.mergedConfig.conversationDisplay
|
||||
return useSyncConfigStore().mergedConfig.conversationDisplay
|
||||
},
|
||||
isTreeView() {
|
||||
return !this.isLinearView
|
||||
},
|
||||
treeViewIsSimple() {
|
||||
return !this.$store.getters.mergedConfig.conversationTreeAdvanced
|
||||
return !useSyncConfigStore().mergedConfig.conversationTreeAdvanced
|
||||
},
|
||||
isLinearView() {
|
||||
return this.displayStyle === 'linear'
|
||||
},
|
||||
shouldFadeAncestors() {
|
||||
return this.$store.getters.mergedConfig.conversationTreeFadeAncestors
|
||||
return useSyncConfigStore().mergedConfig.conversationTreeFadeAncestors
|
||||
},
|
||||
otherRepliesButtonPosition() {
|
||||
return this.$store.getters.mergedConfig.conversationOtherRepliesButton
|
||||
return useSyncConfigStore().mergedConfig.conversationOtherRepliesButton
|
||||
},
|
||||
showOtherRepliesButtonBelowStatus() {
|
||||
return this.otherRepliesButtonPosition === 'below'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -96,7 +97,7 @@ export default {
|
|||
return this.$store.state.users.currentUser
|
||||
},
|
||||
shouldConfirmLogout() {
|
||||
return this.$store.getters.mergedConfig.modalOnLogout
|
||||
return useSyncConfigStore().mergedConfig.modalOnLogout
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const DialogModal = {
|
||||
props: {
|
||||
darkOverlay: {
|
||||
|
|
@ -13,7 +15,7 @@ const DialogModal = {
|
|||
},
|
||||
computed: {
|
||||
mobileCenter() {
|
||||
return this.$store.getters.mergedConfig.modalMobileCenter
|
||||
return useSyncConfigStore().mergedConfig.modalMobileCenter
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import Gallery from 'src/components/gallery/gallery.vue'
|
|||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPollH } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
|
@ -57,7 +59,7 @@ const Draft = {
|
|||
: undefined
|
||||
},
|
||||
localCollapseSubjectDefault() {
|
||||
return this.$store.getters.mergedConfig.collapseMessageWithSubject
|
||||
return useSyncConfigStore().mergedConfig.collapseMessageWithSubject
|
||||
},
|
||||
nsfwClickthrough() {
|
||||
if (!this.draft.nsfw) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import DialogModal from 'src/components/dialog_modal/dialog_modal.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const DraftCloser = {
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -12,10 +14,10 @@ const DraftCloser = {
|
|||
emits: ['save', 'discard'],
|
||||
computed: {
|
||||
action() {
|
||||
if (this.$store.getters.mergedConfig.autoSaveDraft) {
|
||||
if (useSyncConfigStore().mergedConfig.autoSaveDraft) {
|
||||
return 'save'
|
||||
} else {
|
||||
return this.$store.getters.mergedConfig.unsavedPostAction
|
||||
return useSyncConfigStore().mergedConfig.unsavedPostAction
|
||||
}
|
||||
},
|
||||
shouldConfirm() {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@ import { take } from 'lodash'
|
|||
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
|
||||
import { ensureFinalFallback } from '../../i18n/languages.js'
|
||||
import Completion from '../../services/completion/completion.js'
|
||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
|
||||
import EmojiPicker from '../emoji_picker/emoji_picker.vue'
|
||||
import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { ensureFinalFallback } from 'src/i18n/languages.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faSmileBeam } from '@fortawesome/free-regular-svg-icons'
|
||||
|
||||
|
|
@ -131,10 +134,10 @@ const EmojiInput = {
|
|||
},
|
||||
computed: {
|
||||
padEmoji() {
|
||||
return this.$store.getters.mergedConfig.padEmoji
|
||||
return useSyncConfigStore().mergedConfig.padEmoji
|
||||
},
|
||||
defaultCandidateIndex() {
|
||||
return this.$store.getters.mergedConfig.autocompleteSelect ? 0 : -1
|
||||
return useSyncConfigStore().mergedConfig.autocompleteSelect ? 0 : -1
|
||||
},
|
||||
preText() {
|
||||
return this.modelValue.slice(0, this.caret)
|
||||
|
|
@ -163,7 +166,7 @@ const EmojiInput = {
|
|||
},
|
||||
languages() {
|
||||
return ensureFinalFallback(
|
||||
this.$store.getters.mergedConfig.interfaceLanguage,
|
||||
useSyncConfigStore().mergedConfig.interfaceLanguage,
|
||||
)
|
||||
},
|
||||
maybeLocalizedEmojiNamesAndKeywords() {
|
||||
|
|
@ -331,7 +334,6 @@ const EmojiInput = {
|
|||
if (!this.pickerShown) {
|
||||
this.scrollIntoView()
|
||||
this.$refs.picker.showPicker()
|
||||
this.$refs.picker.startEmojiLoad()
|
||||
} else {
|
||||
this.$refs.picker.hidePicker()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
|
||||
/**
|
||||
* suggest - generates a suggestor function to be used by emoji-input
|
||||
* data: object providing source information for specific types of suggestions:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import StillImage from '../still-image/still-image.vue'
|
|||
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -340,7 +341,7 @@ const EmojiPicker = {
|
|||
this.$nextTick(() => {
|
||||
this.updateEmojiSize()
|
||||
})
|
||||
return this.$store.getters.mergedConfig.fontSize
|
||||
return useSyncConfigStore().mergedConfig.fontSize
|
||||
},
|
||||
emojiHeight() {
|
||||
return this.emojiSize
|
||||
|
|
@ -405,7 +406,7 @@ const EmojiPicker = {
|
|||
},
|
||||
languages() {
|
||||
return ensureFinalFallback(
|
||||
this.$store.getters.mergedConfig.interfaceLanguage,
|
||||
useSyncConfigStore().mergedConfig.interfaceLanguage,
|
||||
)
|
||||
},
|
||||
maybeLocalizedEmojiName() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import {
|
|||
requestUnfollow,
|
||||
} from '../../services/follow_manipulate/follow_manipulate'
|
||||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
export default {
|
||||
props: ['relationship', 'user', 'labelFollowing', 'buttonClass'],
|
||||
components: {
|
||||
|
|
@ -16,7 +18,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
shouldConfirmUnfollow() {
|
||||
return this.$store.getters.mergedConfig.modalOnUnfollow
|
||||
return useSyncConfigStore().mergedConfig.modalOnUnfollow
|
||||
},
|
||||
isPressed() {
|
||||
return this.inProgress || this.relationship.following
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { notificationsFromStore } from '../../services/notification_utils/notifi
|
|||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const FollowRequestCard = {
|
||||
props: ['user'],
|
||||
components: {
|
||||
|
|
@ -76,7 +78,7 @@ const FollowRequestCard = {
|
|||
},
|
||||
computed: {
|
||||
mergedConfig() {
|
||||
return this.$store.getters.mergedConfig
|
||||
return useSyncConfigStore().mergedConfig
|
||||
},
|
||||
shouldConfirmApprove() {
|
||||
return this.mergedConfig.modalOnApproveFollow
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const LinkPreview = {
|
||||
name: 'LinkPreview',
|
||||
|
|
@ -24,7 +26,7 @@ const LinkPreview = {
|
|||
hideNsfwConfig() {
|
||||
return this.mergedConfig.hideNsfw
|
||||
},
|
||||
...mapGetters(['mergedConfig']),
|
||||
...mapState(useSyncConfigStore, ['mergedConfig']),
|
||||
},
|
||||
created() {
|
||||
if (this.useImage) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for
|
|||
import statusPosterService from '../../services/status_poster/status_poster.service.js'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch, faUpload } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -33,7 +34,7 @@ const mediaUpload = {
|
|||
}
|
||||
|
||||
// Skip if image compression is disabled
|
||||
if (!this.$store.getters.mergedConfig.imageCompression) {
|
||||
if (!useSyncConfigStore().mergedConfig.imageCompression) {
|
||||
return file
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +79,7 @@ const mediaUpload = {
|
|||
|
||||
// Convert to WebP if supported and alwaysUseJpeg is false, otherwise JPEG
|
||||
const type =
|
||||
!this.$store.getters.mergedConfig.alwaysUseJpeg && supportsWebP
|
||||
!useSyncConfigStore().mergedConfig.alwaysUseJpeg && supportsWebP
|
||||
? 'image/webp'
|
||||
: 'image/jpeg'
|
||||
const extension = type === 'image/webp' ? '.webp' : '.jpg'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import MentionLink from 'src/components/mention_link/mention_link.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export const MENTIONS_LIMIT = 5
|
||||
|
||||
const MentionsLine = {
|
||||
|
|
@ -26,7 +28,7 @@ const MentionsLine = {
|
|||
manyMentions() {
|
||||
return this.extraMentions.length > 0
|
||||
},
|
||||
...mapGetters(['mergedConfig']),
|
||||
...mapState(useSyncConfigStore, ['mergedConfig']),
|
||||
},
|
||||
methods: {
|
||||
toggleShowMore() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import SideDrawer from '../side_drawer/side_drawer.vue'
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -75,15 +75,15 @@ const MobileNav = {
|
|||
return this.$route.name === 'chat'
|
||||
},
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
...mapState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'),
|
||||
}),
|
||||
shouldConfirmLogout() {
|
||||
return this.$store.getters.mergedConfig.modalOnLogout
|
||||
return useSyncConfigStore().mergedConfig.modalOnLogout
|
||||
},
|
||||
closingDrawerMarksAsSeen() {
|
||||
return this.$store.getters.mergedConfig.closingDrawerMarksAsSeen
|
||||
return useSyncConfigStore().mergedConfig.closingDrawerMarksAsSeen
|
||||
},
|
||||
...mapGetters(['unreadChatCount']),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { debounce } from 'lodash'
|
||||
|
||||
import { usePostStatusStore } from 'src/stores/post_status.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPen } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -45,10 +46,10 @@ const MobilePostStatusButton = {
|
|||
)
|
||||
},
|
||||
isPersistent() {
|
||||
return !!this.$store.getters.mergedConfig.alwaysShowNewPostButton
|
||||
return !!useSyncConfigStore().mergedConfig.alwaysShowNewPostButton
|
||||
},
|
||||
autohideFloatingPostButton() {
|
||||
return !!this.$store.getters.mergedConfig.autohideFloatingPostButton
|
||||
return !!useSyncConfigStore().mergedConfig.autohideFloatingPostButton
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
|||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -84,28 +84,28 @@ const NavPanel = {
|
|||
this.editMode = !this.editMode
|
||||
},
|
||||
toggleCollapse() {
|
||||
useServerSideStorageStore().setPreference({
|
||||
useSyncConfigStore().setPreference({
|
||||
path: 'simple.collapseNav',
|
||||
value: !this.collapsed,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
isPinned(item) {
|
||||
return this.pinnedItems.has(item)
|
||||
},
|
||||
togglePin(item) {
|
||||
if (this.isPinned(item)) {
|
||||
useServerSideStorageStore().removeCollectionPreference({
|
||||
useSyncConfigStore().removeCollectionPreference({
|
||||
path: 'collections.pinnedNavItems',
|
||||
value: item,
|
||||
})
|
||||
} else {
|
||||
useServerSideStorageStore().addCollectionPreference({
|
||||
useSyncConfigStore().addCollectionPreference({
|
||||
path: 'collections.pinnedNavItems',
|
||||
value: item,
|
||||
})
|
||||
}
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -122,7 +122,7 @@ const NavPanel = {
|
|||
...mapPiniaState(useInstanceStore, {
|
||||
privateMode: (store) => store.private,
|
||||
}),
|
||||
...mapPiniaState(useServerSideStorageStore, {
|
||||
...mapPiniaState(useSyncConfigStore, {
|
||||
collapsed: (store) => store.prefsStorage.simple.collapseNav,
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { routeTo } from 'src/components/navigation/navigation.js'
|
|||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -23,17 +23,17 @@ const NavigationEntry = {
|
|||
},
|
||||
togglePin(value) {
|
||||
if (this.isPinned(value)) {
|
||||
useServerSideStorageStore().removeCollectionPreference({
|
||||
useSyncConfigStore().removeCollectionPreference({
|
||||
path: 'collections.pinnedNavItems',
|
||||
value,
|
||||
})
|
||||
} else {
|
||||
useServerSideStorageStore().addCollectionPreference({
|
||||
useSyncConfigStore().addCollectionPreference({
|
||||
path: 'collections.pinnedNavItems',
|
||||
value,
|
||||
})
|
||||
}
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -47,7 +47,7 @@ const NavigationEntry = {
|
|||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapPiniaState(useServerSideStorageStore, {
|
||||
...mapPiniaState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useListsStore } from 'src/stores/lists'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -70,7 +70,7 @@ const NavPanel = {
|
|||
...mapPiniaState(useBookmarkFoldersStore, {
|
||||
bookmarks: getBookmarkFolderEntries,
|
||||
}),
|
||||
...mapPiniaState(useServerSideStorageStore, {
|
||||
...mapPiniaState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import UserLink from '../user_link/user_link.vue'
|
|||
import UserPopover from '../user_popover/user_popover.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
|
||||
|
|
@ -181,7 +182,7 @@ const Notification = {
|
|||
return highlightClass(this.notification.from_profile)
|
||||
},
|
||||
userStyle() {
|
||||
const highlight = this.$store.getters.mergedConfig.highlight
|
||||
const highlight = useSyncConfigStore().mergedConfig.highlight
|
||||
const user = this.notification.from_profile
|
||||
return highlightStyle(highlight[user.screen_name])
|
||||
},
|
||||
|
|
@ -209,7 +210,7 @@ const Notification = {
|
|||
return isStatusNotification(this.notification.type)
|
||||
},
|
||||
mergedConfig() {
|
||||
return this.$store.getters.mergedConfig
|
||||
return useSyncConfigStore().mergedConfig
|
||||
},
|
||||
shouldConfirmApprove() {
|
||||
return this.mergedConfig.modalOnApproveFollow
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@
|
|||
<script>
|
||||
import Popover from '../popover/popover.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faFilter } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
|
@ -117,7 +119,7 @@ export default {
|
|||
components: { Popover },
|
||||
computed: {
|
||||
filters() {
|
||||
return this.$store.getters.mergedConfig.notificationVisibility
|
||||
return useSyncConfigStore().mergedConfig.notificationVisibility
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import NotificationFilters from './notification_filters.vue'
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -98,7 +99,7 @@ const Notifications = {
|
|||
return this.unseenNotifications.length
|
||||
},
|
||||
ignoreInactionableSeen() {
|
||||
return this.$store.getters.mergedConfig.ignoreInactionableSeen
|
||||
return useSyncConfigStore().mergedConfig.ignoreInactionableSeen
|
||||
},
|
||||
extraNotificationsCount() {
|
||||
return countExtraNotifications(this.$store)
|
||||
|
|
@ -136,10 +137,10 @@ const Notifications = {
|
|||
)
|
||||
},
|
||||
noSticky() {
|
||||
return this.$store.getters.mergedConfig.disableStickyHeaders
|
||||
return useSyncConfigStore().mergedConfig.disableStickyHeaders
|
||||
},
|
||||
unseenAtTop() {
|
||||
return this.$store.getters.mergedConfig.unseenAtTop
|
||||
return useSyncConfigStore().mergedConfig.unseenAtTop
|
||||
},
|
||||
showExtraNotifications() {
|
||||
return !this.noExtra
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import Timeago from 'components/timeago/timeago.vue'
|
|||
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
|
||||
|
||||
import { usePollsStore } from 'src/stores/polls.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default {
|
||||
name: 'Poll',
|
||||
|
|
@ -48,7 +49,7 @@ export default {
|
|||
return (this.poll && this.poll.expired) || false
|
||||
},
|
||||
expirationLabel() {
|
||||
if (this.$store.getters.mergedConfig.useAbsoluteTimeFormat) {
|
||||
if (useSyncConfigStore().mergedConfig.useAbsoluteTimeFormat) {
|
||||
return this.expired ? 'polls.expired_at' : 'polls.expires_at'
|
||||
} else {
|
||||
return this.expired ? 'polls.expired' : 'polls.expires_in'
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { pollFormToMasto } from 'src/services/poll/poll.service.js'
|
||||
|
||||
|
|
@ -163,7 +164,7 @@ const PostStatusForm = {
|
|||
const preset = this.$route.query.message
|
||||
let statusText = preset || ''
|
||||
|
||||
const { scopeCopy } = this.$store.getters.mergedConfig
|
||||
const { scopeCopy } = useSyncConfigStore().mergedConfig
|
||||
|
||||
const [statusType, refId] = typeAndRefId({
|
||||
replyTo: this.replyTo,
|
||||
|
|
@ -193,7 +194,7 @@ const PostStatusForm = {
|
|||
: this.$store.state.users.currentUser.default_scope
|
||||
|
||||
const { postContentType: contentType, sensitiveByDefault } =
|
||||
this.$store.getters.mergedConfig
|
||||
useSyncConfigStore().mergedConfig
|
||||
|
||||
statusParams = {
|
||||
type: statusType,
|
||||
|
|
@ -324,7 +325,7 @@ const PostStatusForm = {
|
|||
},
|
||||
hideScopeNotice() {
|
||||
return (
|
||||
this.disableNotice || this.$store.getters.mergedConfig.hideScopeNotice
|
||||
this.disableNotice || useSyncConfigStore().mergedConfig.hideScopeNotice
|
||||
)
|
||||
},
|
||||
pollContentError() {
|
||||
|
|
@ -380,7 +381,7 @@ const PostStatusForm = {
|
|||
return this.newStatus.hasPoll
|
||||
},
|
||||
shouldAutoSaveDraft() {
|
||||
return this.$store.getters.mergedConfig.autoSaveDraft
|
||||
return useSyncConfigStore().mergedConfig.autoSaveDraft
|
||||
},
|
||||
autoSaveState() {
|
||||
if (this.saveable) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default {
|
||||
props: ['user', 'relationship'],
|
||||
data() {
|
||||
|
|
@ -20,7 +22,7 @@ export default {
|
|||
}
|
||||
},
|
||||
shouldConfirmRemoveUserFromFollowers() {
|
||||
return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers
|
||||
return useSyncConfigStore().mergedConfig.modalOnRemoveUserFromFollowers
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import DraftButtons from './draft_buttons.vue'
|
|||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
import ProfileSettingIndicator from './profile_setting_indicator.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModifiedIndicator,
|
||||
|
|
@ -235,13 +239,14 @@ export default {
|
|||
case 'admin':
|
||||
return this.$store.state.adminSettings.config
|
||||
default:
|
||||
return this.$store.getters.mergedConfig
|
||||
return useSyncConfigStore().mergedConfig
|
||||
}
|
||||
},
|
||||
configSink() {
|
||||
if (this.path == null) {
|
||||
return (k, v) => this.$emit('update:modelValue', v)
|
||||
}
|
||||
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return (k, v) =>
|
||||
|
|
@ -250,15 +255,37 @@ export default {
|
|||
return (k, v) =>
|
||||
this.$store.dispatch('pushAdminSetting', { path: k, value: v })
|
||||
default:
|
||||
if (this.timedApplyMode) {
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('setOptionTemporarily', {
|
||||
name: k,
|
||||
value: v,
|
||||
})
|
||||
} else {
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('setOption', { name: k, value: v })
|
||||
return (readPath, value) => {
|
||||
const writePath = `simple.${readPath}`
|
||||
|
||||
if (!this.timedApplyMode) {
|
||||
useSyncConfigStore().setPreference({ path: writePath, value })
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
} else {
|
||||
if (useInterfaceStore().temporaryChangesTimeoutId !== null) {
|
||||
console.error("Can't track more than one temporary change")
|
||||
return
|
||||
}
|
||||
|
||||
const oldValue = get(this.configSource, readPath)
|
||||
|
||||
useSyncConfigStore().setPreference({ path: writePath, value })
|
||||
|
||||
const confirm = () => {
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
useInterfaceStore().clearTemporaryChanges()
|
||||
}
|
||||
|
||||
const revert = () => {
|
||||
useSyncConfigStore().setPreference({
|
||||
path: writePath,
|
||||
value: oldValue,
|
||||
})
|
||||
useInterfaceStore().clearTemporaryChanges()
|
||||
}
|
||||
|
||||
useInterfaceStore().setTemporaryChanges({ confirm, revert })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -267,7 +294,7 @@ export default {
|
|||
case 'profile':
|
||||
return {}
|
||||
default:
|
||||
return get(this.$store.getters.defaultConfig, this.path)
|
||||
return get(useInstanceStore().prefsStorage, this.path)
|
||||
}
|
||||
},
|
||||
isProfileSetting() {
|
||||
|
|
@ -318,7 +345,8 @@ export default {
|
|||
},
|
||||
matchesExpertLevel() {
|
||||
const settingExpertLevel = this.expert || 0
|
||||
const userToggleExpert = this.$store.state.config.expertLevel || 0
|
||||
const userToggleExpert =
|
||||
useSyncConfigStore().mergedConfig.expertLevel || 0
|
||||
|
||||
return settingExpertLevel <= userToggleExpert
|
||||
},
|
||||
|
|
@ -344,7 +372,7 @@ export default {
|
|||
this.draft = cloneDeep(this.state)
|
||||
} else {
|
||||
set(
|
||||
this.$store.getters.mergedConfig,
|
||||
useSyncConfigStore().mergedConfig,
|
||||
this.path,
|
||||
cloneDeep(this.defaultState),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const SharedComputedObject = () => ({
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
expertLevel() {
|
||||
return this.$store.getters.mergedConfig.expertLevel > 0
|
||||
},
|
||||
mergedConfig() {
|
||||
return this.$store.getters.mergedConfig
|
||||
},
|
||||
adminConfig() {
|
||||
return this.$store.state.adminSettings.config
|
||||
},
|
||||
adminDraft() {
|
||||
return this.$store.state.adminSettings.draft
|
||||
},
|
||||
...mapPiniaState(useSyncConfigStore, ['mergedConfig']),
|
||||
...mapPiniaState(useSyncConfigStore, {
|
||||
expertLevel: (store) => store.mergedConfig.expertLevel,
|
||||
}),
|
||||
...mapState({
|
||||
adminConfig: (state) => state.adminSettings.config,
|
||||
adminDraft: (state) => state.adminSettings.draft,
|
||||
user: (state) => state.users.currentUser,
|
||||
}),
|
||||
})
|
||||
|
||||
export default SharedComputedObject
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
|
|||
import Popover from '../popover/popover.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import {
|
||||
newExporter,
|
||||
|
|
@ -191,11 +192,11 @@ const SettingsModal = {
|
|||
}),
|
||||
expertLevel: {
|
||||
get() {
|
||||
return this.$store.state.config.expertLevel > 0
|
||||
return useSyncConfigStore().mergedConfig.expertLevel > 0
|
||||
},
|
||||
set(value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'expertLevel',
|
||||
useSyncConfigStore().setPreference({
|
||||
path: 'simple.expertLevel',
|
||||
value: value ? 1 : 0,
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
:key="style.key"
|
||||
:data-theme-key="style.key"
|
||||
class="button-default theme-preview"
|
||||
:class="{ toggled: isThemeActive(style.key), disabled: switchInProgress }"
|
||||
:class="{ toggled: isStyleActive(style.key), disabled: switchInProgress }"
|
||||
:disabled="switchInProgress"
|
||||
@click="style.version === 'v2' ? setTheme(style.key) : setStyle(style.key)"
|
||||
>
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
class="btn button-default palette-entry"
|
||||
:class="{ toggled: isPaletteActive(p.key), disabled: switchInProgress }"
|
||||
:disabled="switchInProgress"
|
||||
@click="() => setPalette(p.key, p)"
|
||||
@click="() => setLocalPalette(p.key, p)"
|
||||
>
|
||||
<div class="palette-label">
|
||||
<label>
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
class="btn button-default palette-entry"
|
||||
:class="{ toggled: isPaletteActive(p.key), disabled: switchInProgress }"
|
||||
:disabled="switchInProgress"
|
||||
@click="() => setPalette(p.key, p)"
|
||||
@click="() => setLocalPalette(p.key, p)"
|
||||
>
|
||||
<div class="palette-label">
|
||||
<label>
|
||||
|
|
@ -143,7 +143,7 @@
|
|||
:compact="true"
|
||||
:apply="true"
|
||||
:disabled="switchInProgress"
|
||||
@apply-palette="data => setPaletteCustom(data)"
|
||||
@apply-palette="data => setLocalPaletteCustom(data)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="customThemeVersion === 'v2'">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import UnitSetting from '../helpers/unit_setting.vue'
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const ClutterTab = {
|
||||
components: {
|
||||
|
|
@ -33,7 +33,7 @@ const ClutterTab = {
|
|||
store.instanceIdentity.showInstanceSpecificPanel &&
|
||||
store.instanceIdentity.instanceSpecificPanelContent,
|
||||
}),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
...mapState(useSyncConfigStore, {
|
||||
muteFilters: (store) =>
|
||||
Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
||||
|
|
@ -89,10 +89,10 @@ const ClutterTab = {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useServerSideStorageStore, [
|
||||
...mapActions(useSyncConfigStore, [
|
||||
'setPreference',
|
||||
'unsetPreference',
|
||||
'pushServerSideStorage',
|
||||
'pushSyncConfig',
|
||||
]),
|
||||
getDatetimeLocal(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
|
|
@ -139,7 +139,7 @@ const ClutterTab = {
|
|||
filter.order = this.muteFilters.length + 2
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
exportFilter(id) {
|
||||
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
||||
|
|
@ -155,19 +155,19 @@ const ClutterTab = {
|
|||
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
deleteFilter(id) {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
purgeExpiredFilters() {
|
||||
this.muteFiltersExpired.forEach(([id]) => {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
updateFilter(id, field, value) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
|
|
@ -193,7 +193,7 @@ const ClutterTab = {
|
|||
path: 'simple.muteFilters.' + id,
|
||||
value: this.muteFiltersDraftObject[id],
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
this.muteFiltersDraftDirty[id] = false
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@
|
|||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="hideUserStats"
|
||||
>
|
||||
<BooleanSetting path="hideUserStats">
|
||||
{{ $t('settings.hide_user_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
import { cacheKey, clearCache, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||
|
|
@ -116,7 +117,7 @@ const ComposingTab = {
|
|||
},
|
||||
language: {
|
||||
get: function () {
|
||||
return this.$store.getters.mergedConfig.interfaceLanguage
|
||||
return useSyncConfigStore().mergedConfig.interfaceLanguage
|
||||
},
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import UnitSetting from '../helpers/unit_setting.vue'
|
|||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import {
|
||||
newExporter,
|
||||
|
|
@ -36,11 +36,11 @@ const FilteringTab = {
|
|||
label: this.$t(`user_card.mute_block_${mode}`),
|
||||
})),
|
||||
muteFiltersDraftObject: cloneDeep(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters,
|
||||
useSyncConfigStore().prefsStorage.simple.muteFilters,
|
||||
),
|
||||
muteFiltersDraftDirty: Object.fromEntries(
|
||||
Object.entries(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters,
|
||||
useSyncConfigStore().prefsStorage.simple.muteFilters,
|
||||
).map(([k]) => [k, false]),
|
||||
),
|
||||
exportedFilter: null,
|
||||
|
|
@ -92,7 +92,7 @@ const FilteringTab = {
|
|||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
...mapState(useSyncConfigStore, {
|
||||
muteFilters: (store) =>
|
||||
Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
||||
|
|
@ -149,10 +149,10 @@ const FilteringTab = {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useServerSideStorageStore, [
|
||||
...mapActions(useSyncConfigStore, [
|
||||
'setPreference',
|
||||
'unsetPreference',
|
||||
'pushServerSideStorage',
|
||||
'pushSyncConfig',
|
||||
]),
|
||||
getDatetimeLocal(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
|
|
@ -199,7 +199,7 @@ const FilteringTab = {
|
|||
filter.order = this.muteFilters.length + 2
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
exportFilter(id) {
|
||||
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
||||
|
|
@ -215,19 +215,19 @@ const FilteringTab = {
|
|||
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
deleteFilter(id) {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
purgeExpiredFilters() {
|
||||
this.muteFiltersExpired.forEach(([id]) => {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
},
|
||||
updateFilter(id, field, value) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
|
|
@ -253,7 +253,7 @@ const FilteringTab = {
|
|||
path: 'simple.muteFilters.' + id,
|
||||
value: this.muteFiltersDraftObject[id],
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
this.pushSyncConfig()
|
||||
this.muteFiltersDraftDirty[id] = false
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ 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'
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ const GeneralTab = {
|
|||
computed: {
|
||||
language: {
|
||||
get: function () {
|
||||
return this.$store.getters.mergedConfig.interfaceLanguage
|
||||
return useSyncConfigStore().mergedConfig.interfaceLanguage
|
||||
},
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', {
|
||||
|
|
@ -48,6 +49,9 @@ const GeneralTab = {
|
|||
},
|
||||
...SharedComputedObject(),
|
||||
...mapState(useInstanceCapabilitiesStore, ['blockExpiration']),
|
||||
...mapState(useSyncConfigStore, {
|
||||
theme3hacks: (store) => store.mergedConfig.theme3hacks,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
updateProfile() {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
</li>
|
||||
<li>
|
||||
<FontControl
|
||||
:model-value="mergedConfig.theme3hacks.fonts.interface"
|
||||
:model-value="theme3hacks.fonts.interface"
|
||||
name="ui"
|
||||
:label="$t('settings.style.fonts.components_inline.interface')"
|
||||
:fallback="{ family: 'sans-serif' }"
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
</li>
|
||||
<li>
|
||||
<FontControl
|
||||
:model-value="mergedConfig.theme3hacks.fonts.input"
|
||||
:model-value="theme3hacks.fonts.input"
|
||||
name="input"
|
||||
:fallback="{ family: 'inherit' }"
|
||||
:label="$t('settings.style.fonts.components_inline.input')"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const GeneralTab = {
|
||||
data() {
|
||||
|
|
@ -32,12 +33,12 @@ const GeneralTab = {
|
|||
'suggestionsEnabled',
|
||||
]),
|
||||
columns() {
|
||||
const mode = this.$store.getters.mergedConfig.thirdColumnMode
|
||||
const mode = useSyncConfigStore().mergedConfig.thirdColumnMode
|
||||
|
||||
const notif = mode === 'none' ? [] : ['notifs']
|
||||
|
||||
if (
|
||||
this.$store.getters.mergedConfig.sidebarRight ||
|
||||
useSyncConfigStore().mergedConfig.sidebarRight ||
|
||||
mode === 'postform'
|
||||
) {
|
||||
return [...notif, 'content', 'sidebar']
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import Preview from './theme_preview.vue'
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import {
|
||||
getContrastRatioLayers,
|
||||
|
|
@ -81,7 +82,7 @@ export default {
|
|||
}),
|
||||
availableStyles: [],
|
||||
selected: '',
|
||||
selectedTheme: this.$store.getters.mergedConfig.theme,
|
||||
selectedTheme: useSyncConfigStore().mergedConfig.theme,
|
||||
themeWarning: undefined,
|
||||
tempImportFile: undefined,
|
||||
engineVersion: 0,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import UserPopover from '../user_popover/user_popover.vue'
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
|
||||
|
|
@ -259,9 +259,7 @@ const Status = {
|
|||
},
|
||||
muteFilterHits() {
|
||||
return muteFilterHits(
|
||||
Object.values(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters,
|
||||
),
|
||||
Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters),
|
||||
this.status,
|
||||
)
|
||||
},
|
||||
|
|
@ -480,7 +478,7 @@ const Status = {
|
|||
return this.$store.state.users.currentUser
|
||||
},
|
||||
mergedConfig() {
|
||||
return this.$store.getters.mergedConfig
|
||||
return useSyncConfigStore().mergedConfig
|
||||
},
|
||||
isSuspendable() {
|
||||
return !this.replying && this.mediaPlaying.length === 0
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import ActionButtonContainer from './action_button_container.vue'
|
||||
import { BUTTONS } from './buttons_definitions.js'
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import genRandomSeed from 'src/services/random_seed/random_seed.service.js'
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ const StatusActionButtons = {
|
|||
ActionButtonContainer,
|
||||
},
|
||||
computed: {
|
||||
...mapState(useServerSideStorageStore, {
|
||||
...mapState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedStatusActions),
|
||||
}),
|
||||
|
|
@ -111,18 +111,18 @@ const StatusActionButtons = {
|
|||
return this.pinnedItems.has(button.name)
|
||||
},
|
||||
unpin(button) {
|
||||
useServerSideStorageStore().removeCollectionPreference({
|
||||
useSyncConfigStore().removeCollectionPreference({
|
||||
path: 'collections.pinnedStatusActions',
|
||||
value: button.name,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
pin(button) {
|
||||
useServerSideStorageStore().addCollectionPreference({
|
||||
useSyncConfigStore().addCollectionPreference({
|
||||
path: 'collections.pinnedStatusActions',
|
||||
value: button.name,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
getComponent(button) {
|
||||
if (!this.$store.state.users.currentUser && button.anonLink) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import LinkPreview from '../link-preview/link-preview.vue'
|
|||
import Poll from '../poll/poll.vue'
|
||||
|
||||
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -74,7 +75,7 @@ const StatusContent = {
|
|||
uncontrolledShowingLongSubject: false,
|
||||
// not as computed because it sets the initial state which will be changed later
|
||||
uncontrolledExpandingSubject:
|
||||
!this.$store.getters.mergedConfig.collapseMessageWithSubject,
|
||||
!useSyncConfigStore().mergedConfig.collapseMessageWithSubject,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
127
src/components/still-image/still-image-emoji-popover.js
Normal file
127
src/components/still-image/still-image-emoji-popover.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import Popover from 'components/popover/popover.vue'
|
||||
import SelectComponent from 'components/select/select.vue'
|
||||
import { assign } from 'lodash'
|
||||
|
||||
import StillImage from './still-image.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
export default {
|
||||
components: { StillImage, Popover, SelectComponent },
|
||||
props: {
|
||||
shortcode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isLocal: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
knownLocalPacks: {},
|
||||
packName: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isUserAdmin() {
|
||||
return this.$store.state.users.currentUser.rights.admin
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
displayError(msg) {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'admin_dash.emoji.error',
|
||||
messageArgs: [msg],
|
||||
level: 'error',
|
||||
})
|
||||
},
|
||||
copyToLocalPack() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.addNewEmojiFile({
|
||||
packName: this.packName,
|
||||
file: this.$attrs.src,
|
||||
shortcode: this.shortcode,
|
||||
filename: '',
|
||||
})
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp.error !== undefined) {
|
||||
this.displayError(resp.error)
|
||||
return
|
||||
}
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'admin_dash.emoji.copied_successfully',
|
||||
messageArgs: [this.shortcode, this.packName],
|
||||
level: 'success',
|
||||
})
|
||||
|
||||
this.$refs.emojiPopover.hidePopover()
|
||||
this.packName = ''
|
||||
})
|
||||
},
|
||||
|
||||
// Copied from emoji_tab.js
|
||||
loadPacksPaginated(listFunction) {
|
||||
const pageSize = 25
|
||||
const allPacks = {}
|
||||
|
||||
return listFunction({
|
||||
instance: useInstanceStore().server,
|
||||
page: 1,
|
||||
pageSize: 0,
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((data) => {
|
||||
if (data.error !== undefined) {
|
||||
return Promise.reject(data.error)
|
||||
}
|
||||
|
||||
let resultingPromise = Promise.resolve({})
|
||||
for (let i = 0; i < Math.ceil(data.count / pageSize); i++) {
|
||||
resultingPromise = resultingPromise
|
||||
.then(() =>
|
||||
listFunction({
|
||||
instance: useInstanceStore().server,
|
||||
page: i,
|
||||
pageSize,
|
||||
}),
|
||||
)
|
||||
.then((data) => data.json())
|
||||
.then((pageData) => {
|
||||
if (pageData.error !== undefined) {
|
||||
return Promise.reject(pageData.error)
|
||||
}
|
||||
|
||||
assign(allPacks, pageData.packs)
|
||||
})
|
||||
}
|
||||
|
||||
return resultingPromise
|
||||
})
|
||||
.then(() => allPacks)
|
||||
.catch((data) => {
|
||||
this.displayError(data)
|
||||
})
|
||||
},
|
||||
fetchEmojiPacksIfAdmin() {
|
||||
if (!this.isUserAdmin) return
|
||||
|
||||
this.loadPacksPaginated(
|
||||
this.$store.state.api.backendInteractor.listEmojiPacks,
|
||||
).then((allPacks) => {
|
||||
// Sort by key
|
||||
const sorted = Object.keys(allPacks)
|
||||
.sort()
|
||||
.reduce((acc, key) => {
|
||||
if (key.length === 0) return acc
|
||||
acc[key] = allPacks[key]
|
||||
return acc
|
||||
}, {})
|
||||
this.knownLocalPacks = sorted
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const StillImage = {
|
||||
props: [
|
||||
'src',
|
||||
|
|
@ -15,7 +17,7 @@ const StillImage = {
|
|||
return {
|
||||
// for lazy loading, see loadLazy()
|
||||
realSrc: this.src,
|
||||
stopGifs: this.$store.getters.mergedConfig.stopGifs,
|
||||
stopGifs: useSyncConfigStore().mergedConfig.stopGifs,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
129
src/components/timeago/timeago.js
Normal file
129
src/components/timeago/timeago.js
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import * as DateUtils from 'src/services/date_utils/date_utils.js'
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
|
||||
export default {
|
||||
name: 'Timeago',
|
||||
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'],
|
||||
data() {
|
||||
return {
|
||||
relativeTimeMs: 0,
|
||||
relativeTime: { key: 'time.now', num: 0 },
|
||||
interval: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
shouldUseAbsoluteTimeFormat() {
|
||||
if (!useSyncConfigStore().mergedConfig.useAbsoluteTimeFormat) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
DateUtils.durationStrToMs(
|
||||
useSyncConfigStore().mergedConfig.absoluteTimeFormatMinAge,
|
||||
) <= this.relativeTimeMs
|
||||
)
|
||||
},
|
||||
time12hFormat() {
|
||||
return useSyncConfigStore().mergedConfig.absoluteTimeFormat12h === '12h'
|
||||
},
|
||||
browserLocale() {
|
||||
return localeService.internalToBrowserLocale(this.$i18n.locale)
|
||||
},
|
||||
timeAsDate() {
|
||||
return typeof this.time === 'string'
|
||||
? new Date(Date.parse(this.time))
|
||||
: this.time
|
||||
},
|
||||
localeDateString() {
|
||||
return this.timeAsDate.toLocaleString(this.browserLocale)
|
||||
},
|
||||
relativeTimeString() {
|
||||
const timeString = this.$i18n.t(
|
||||
this.relativeTime.key,
|
||||
[this.relativeTime.num],
|
||||
this.relativeTime.num,
|
||||
)
|
||||
|
||||
if (
|
||||
typeof this.templateKey === 'string' &&
|
||||
this.relativeTime.key !== 'time.now'
|
||||
) {
|
||||
return this.$i18n.t(this.templateKey, [timeString])
|
||||
}
|
||||
|
||||
return timeString
|
||||
},
|
||||
absoluteTimeString() {
|
||||
if (this.longFormat) {
|
||||
return this.localeDateString
|
||||
}
|
||||
const now = new Date()
|
||||
const formatter = (() => {
|
||||
if (DateUtils.isSameDay(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
minute: 'numeric',
|
||||
hour: 'numeric',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
} else if (DateUtils.isSameMonth(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
} else if (DateUtils.isSameYear(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
} else {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
}
|
||||
})()
|
||||
|
||||
return formatter.format(this.timeAsDate)
|
||||
},
|
||||
relativeOrAbsoluteTimeString() {
|
||||
return this.shouldUseAbsoluteTimeFormat
|
||||
? this.absoluteTimeString
|
||||
: this.relativeTimeString
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
time(newVal, oldVal) {
|
||||
if (oldVal !== newVal) {
|
||||
clearTimeout(this.interval)
|
||||
this.refreshRelativeTimeObject()
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.refreshRelativeTimeObject()
|
||||
},
|
||||
unmounted() {
|
||||
clearTimeout(this.interval)
|
||||
},
|
||||
methods: {
|
||||
refreshRelativeTimeObject() {
|
||||
const nowThreshold =
|
||||
typeof this.nowThreshold === 'number' ? this.nowThreshold : 1
|
||||
this.relativeTimeMs = DateUtils.relativeTimeMs(this.time)
|
||||
this.relativeTime = this.longFormat
|
||||
? DateUtils.relativeTime(this.time, nowThreshold)
|
||||
: DateUtils.relativeTimeShort(this.time, nowThreshold)
|
||||
|
||||
if (this.autoUpdate) {
|
||||
this.interval = setTimeout(
|
||||
this.refreshRelativeTimeObject,
|
||||
1000 * this.autoUpdate,
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -7,132 +7,4 @@
|
|||
</time>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as DateUtils from 'src/services/date_utils/date_utils.js'
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
|
||||
export default {
|
||||
name: 'Timeago',
|
||||
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'],
|
||||
data() {
|
||||
return {
|
||||
relativeTimeMs: 0,
|
||||
relativeTime: { key: 'time.now', num: 0 },
|
||||
interval: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
shouldUseAbsoluteTimeFormat() {
|
||||
if (!this.$store.getters.mergedConfig.useAbsoluteTimeFormat) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
DateUtils.durationStrToMs(
|
||||
this.$store.getters.mergedConfig.absoluteTimeFormatMinAge,
|
||||
) <= this.relativeTimeMs
|
||||
)
|
||||
},
|
||||
time12hFormat() {
|
||||
return this.$store.getters.mergedConfig.absoluteTimeFormat12h === '12h'
|
||||
},
|
||||
browserLocale() {
|
||||
return localeService.internalToBrowserLocale(this.$i18n.locale)
|
||||
},
|
||||
timeAsDate() {
|
||||
return typeof this.time === 'string'
|
||||
? new Date(Date.parse(this.time))
|
||||
: this.time
|
||||
},
|
||||
localeDateString() {
|
||||
return this.timeAsDate.toLocaleString(this.browserLocale)
|
||||
},
|
||||
relativeTimeString() {
|
||||
const timeString = this.$i18n.t(
|
||||
this.relativeTime.key,
|
||||
[this.relativeTime.num],
|
||||
this.relativeTime.num,
|
||||
)
|
||||
|
||||
if (
|
||||
typeof this.templateKey === 'string' &&
|
||||
this.relativeTime.key !== 'time.now'
|
||||
) {
|
||||
return this.$i18n.t(this.templateKey, [timeString])
|
||||
}
|
||||
|
||||
return timeString
|
||||
},
|
||||
absoluteTimeString() {
|
||||
if (this.longFormat) {
|
||||
return this.localeDateString
|
||||
}
|
||||
const now = new Date()
|
||||
const formatter = (() => {
|
||||
if (DateUtils.isSameDay(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
minute: 'numeric',
|
||||
hour: 'numeric',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
} else if (DateUtils.isSameMonth(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
} else if (DateUtils.isSameYear(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
} else {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
hour12: this.time12hFormat,
|
||||
})
|
||||
}
|
||||
})()
|
||||
|
||||
return formatter.format(this.timeAsDate)
|
||||
},
|
||||
relativeOrAbsoluteTimeString() {
|
||||
return this.shouldUseAbsoluteTimeFormat
|
||||
? this.absoluteTimeString
|
||||
: this.relativeTimeString
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
time(newVal, oldVal) {
|
||||
if (oldVal !== newVal) {
|
||||
clearTimeout(this.interval)
|
||||
this.refreshRelativeTimeObject()
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.refreshRelativeTimeObject()
|
||||
},
|
||||
unmounted() {
|
||||
clearTimeout(this.interval)
|
||||
},
|
||||
methods: {
|
||||
refreshRelativeTimeObject() {
|
||||
const nowThreshold =
|
||||
typeof this.nowThreshold === 'number' ? this.nowThreshold : 1
|
||||
this.relativeTimeMs = DateUtils.relativeTimeMs(this.time)
|
||||
this.relativeTime = this.longFormat
|
||||
? DateUtils.relativeTime(this.time, nowThreshold)
|
||||
: DateUtils.relativeTimeShort(this.time, nowThreshold)
|
||||
|
||||
if (this.autoUpdate) {
|
||||
this.interval = setTimeout(
|
||||
this.refreshRelativeTimeObject,
|
||||
1000 * this.autoUpdate,
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<script src="./timeago.js" />
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import Status from '../status/status.vue'
|
|||
import TimelineMenu from '../timeline_menu/timeline_menu.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
||||
|
||||
|
|
@ -125,7 +126,7 @@ const Timeline = {
|
|||
return this.timeline.visibleStatuses.slice(min, max).map((_) => _.id)
|
||||
},
|
||||
virtualScrollingEnabled() {
|
||||
return this.$store.getters.mergedConfig.virtualScrolling
|
||||
return useSyncConfigStore().mergedConfig.virtualScrolling
|
||||
},
|
||||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||
|
|
@ -313,7 +314,7 @@ const Timeline = {
|
|||
},
|
||||
watch: {
|
||||
newStatusCount(count) {
|
||||
if (!this.$store.getters.mergedConfig.streaming) {
|
||||
if (!useSyncConfigStore().mergedConfig.streaming) {
|
||||
return
|
||||
}
|
||||
if (count > 0) {
|
||||
|
|
@ -323,7 +324,9 @@ const Timeline = {
|
|||
if (
|
||||
top < 15 &&
|
||||
!this.paused &&
|
||||
!(this.unfocused && this.$store.getters.mergedConfig.pauseOnUnfocused)
|
||||
!(
|
||||
this.unfocused && useSyncConfigStore().mergedConfig.pauseOnUnfocused
|
||||
)
|
||||
) {
|
||||
this.showNewStatuses()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Modal from 'src/components/modal/modal.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import pleromaTanFoxMask from 'src/assets/pleromatan_apology_fox_mask.png'
|
||||
import pleromaTanMask from 'src/assets/pleromatan_apology_mask.png'
|
||||
|
|
@ -41,9 +41,9 @@ const UpdateNotification = {
|
|||
return (
|
||||
!useInstanceStore().disableUpdateNotification &&
|
||||
this.$store.state.users.currentUser &&
|
||||
useServerSideStorageStore().flagStorage.updateCounter <
|
||||
useSyncConfigStore().flagStorage.updateCounter <
|
||||
CURRENT_UPDATE_COUNTER &&
|
||||
!useServerSideStorageStore().prefsStorage.simple.dontShowUpdateNotifs
|
||||
!useSyncConfigStore().prefsStorage.simple.dontShowUpdateNotifs
|
||||
)
|
||||
},
|
||||
},
|
||||
|
|
@ -53,22 +53,22 @@ const UpdateNotification = {
|
|||
},
|
||||
neverShowAgain() {
|
||||
this.toggleShow()
|
||||
useServerSideStorageStore().setFlag({
|
||||
useSyncConfigStore().setFlag({
|
||||
flag: 'updateCounter',
|
||||
value: CURRENT_UPDATE_COUNTER,
|
||||
})
|
||||
useServerSideStorageStore().setPreference({
|
||||
useSyncConfigStore().setPreference({
|
||||
path: 'simple.dontShowUpdateNotifs',
|
||||
value: true,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
dismiss() {
|
||||
useServerSideStorageStore().setFlag({
|
||||
useSyncConfigStore().setFlag({
|
||||
flag: 'updateCounter',
|
||||
value: CURRENT_UPDATE_COUNTER,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { useEmojiStore } from 'src/stores/emoji.js'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { usePostStatusStore } from 'src/stores/post_status'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { propsToNative } from 'src/services/attributes_helper/attributes_helper.service.js'
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
|
|
@ -223,12 +224,12 @@ export default {
|
|||
userHighlightType: {
|
||||
get() {
|
||||
const data =
|
||||
this.$store.getters.mergedConfig.highlight[this.user.screen_name]
|
||||
useSyncConfigStore().mergedConfig.highlight[this.user.screen_name]
|
||||
return (data && data.type) || 'disabled'
|
||||
},
|
||||
set(type) {
|
||||
const data =
|
||||
this.$store.getters.mergedConfig.highlight[this.user.screen_name]
|
||||
useSyncConfigStore().mergedConfig.highlight[this.user.screen_name]
|
||||
if (type !== 'disabled') {
|
||||
this.$store.dispatch('setHighlight', {
|
||||
user: this.user.screen_name,
|
||||
|
|
@ -247,7 +248,7 @@ export default {
|
|||
userHighlightColor: {
|
||||
get() {
|
||||
const data =
|
||||
this.$store.getters.mergedConfig.highlight[this.user.screen_name]
|
||||
useSyncConfigStore().mergedConfig.highlight[this.user.screen_name]
|
||||
return data && data.color
|
||||
},
|
||||
set(color) {
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!editable && loggedIn && isOtherUser && (hasNote || !hideBio) && !mergedConfig.userCardHidePersonalMarks"
|
||||
v-if="!editable && loggedIn && isOtherUser && (hasNote || !hideBio) && !userCardHidePersonalMarks"
|
||||
class="personal-marks"
|
||||
>
|
||||
<UserNote
|
||||
|
|
@ -357,7 +357,7 @@
|
|||
<RichContent
|
||||
v-if="!hideBio"
|
||||
class="user-card-bio"
|
||||
:class="{ '-justify-left': mergedConfig.userCardLeftJustify }"
|
||||
:class="{ '-justify-left': userCardLeftJustify }"
|
||||
:html="editable ? newBio.replace(/\n/g, '<br>') : user.description_html"
|
||||
:emoji="editable ? emoji : user.emoji"
|
||||
:handle-links="true"
|
||||
|
|
@ -368,7 +368,7 @@
|
|||
v-model="newBio"
|
||||
enable-emoji-picker
|
||||
class="user-card-bio"
|
||||
:class="{ '-justify-left': mergedConfig.userCardLeftJustify }"
|
||||
:class="{ '-justify-left': userCardLeftJustify }"
|
||||
:suggest="emojiUserSuggestor"
|
||||
>
|
||||
<template #default="inputProps">
|
||||
|
|
@ -505,11 +505,11 @@
|
|||
class="user-extras"
|
||||
>
|
||||
<span
|
||||
v-if="!editable && !mergedConfig.hideUserStats"
|
||||
v-if="!editable && !hideUserStats"
|
||||
class="user-stats"
|
||||
>
|
||||
<dl
|
||||
v-if="!mergedConfig.hideUserStats && !hideBio"
|
||||
v-if="!hideUserStats && !hideBio"
|
||||
class="user-count"
|
||||
>
|
||||
<dd>{{ user.statuses_count }}</dd>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import UserCard from '../user_card/user_card.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const UserPopover = {
|
||||
name: 'UserPopover',
|
||||
props: ['userId', 'overlayCenters', 'disabled', 'overlayCentersSelector'],
|
||||
|
|
@ -9,14 +12,11 @@ const UserPopover = {
|
|||
UserCard,
|
||||
Popover: defineAsyncComponent(() => import('../popover/popover.vue')),
|
||||
},
|
||||
computed: {
|
||||
userPopoverAvatarAction() {
|
||||
return this.$store.getters.mergedConfig.userPopoverAvatarAction
|
||||
},
|
||||
userPopoverOverlay() {
|
||||
return this.$store.getters.mergedConfig.userPopoverOverlay
|
||||
},
|
||||
},
|
||||
computed: mapState(useSyncConfigStore, {
|
||||
userPopoverAvatarAction: (state) =>
|
||||
state.mergedConfig.userPopoverAvatarAction,
|
||||
userPopoverOverlay: (state) => state.mergedConfig.userPopoverOverlay,
|
||||
}),
|
||||
}
|
||||
|
||||
export default UserPopover
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
|
|||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { durationStrToMs } from 'src/services/date_utils/date_utils.js'
|
||||
|
||||
const UserTimedFilterModal = {
|
||||
data() {
|
||||
const action = this.isMute
|
||||
? this.$store.getters.mergedConfig.onMuteDefaultAction
|
||||
: this.$store.getters.mergedConfig.onBlockDefaultAction
|
||||
? useSyncConfigStore().mergedConfig.onMuteDefaultAction
|
||||
: useSyncConfigStore().mergedConfig.onBlockDefaultAction
|
||||
const doAsk = action === 'ask'
|
||||
const defaultValues = {}
|
||||
|
||||
|
|
@ -44,9 +46,9 @@ const UserTimedFilterModal = {
|
|||
computed: {
|
||||
shouldConfirm() {
|
||||
if (this.isMute) {
|
||||
return this.$store.getters.mergedConfig.onMuteDefaultAction === 'ask'
|
||||
return useSyncConfigStore().mergedConfig.onMuteDefaultAction === 'ask'
|
||||
} else {
|
||||
return this.$store.getters.mergedConfig.onBlockDefaultAction === 'ask'
|
||||
return useSyncConfigStore().mergedConfig.onBlockDefaultAction === 'ask'
|
||||
}
|
||||
},
|
||||
expiryString() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const VideoAttachment = {
|
||||
props: ['attachment', 'controls'],
|
||||
data() {
|
||||
|
|
@ -9,10 +11,10 @@ const VideoAttachment = {
|
|||
},
|
||||
computed: {
|
||||
loopVideo() {
|
||||
if (this.$store.getters.mergedConfig.loopVideoSilentOnly) {
|
||||
if (useSyncConfigStore().mergedConfig.loopVideoSilentOnly) {
|
||||
return !this.hasAudio
|
||||
}
|
||||
return this.$store.getters.mergedConfig.loopVideo
|
||||
return useSyncConfigStore().mergedConfig.loopVideo
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import apiService from '../../services/api/api.service.js'
|
||||
import FollowCard from '../follow_card/follow_card.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
|
||||
const WhoToFollow = {
|
||||
components: {
|
||||
FollowCard,
|
||||
|
|
|
|||
14
src/lib/language.js
Normal file
14
src/lib/language.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { useI18nStore } from 'src/stores/i18n.js'
|
||||
|
||||
export const piniaLanguagePlugin = ({ store, options }) => {
|
||||
if (store.$id === 'sync_config') {
|
||||
store.$onAction(({ name, args }) => {
|
||||
if (name === 'setPreference') {
|
||||
const { path, value } = args[0]
|
||||
if (path === 'simple.interfaceLanguage') {
|
||||
useI18nStore().setLanguage(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +1,96 @@
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default (store) => {
|
||||
export const piniaPushNotificationsPlugin = ({ store }) => {
|
||||
if (
|
||||
store.$id !== 'sync_config' &&
|
||||
store.$id !== 'instance' &&
|
||||
store.$id !== 'interface'
|
||||
)
|
||||
return
|
||||
|
||||
store.$onAction(({ name: actionName, args }) => {
|
||||
if (
|
||||
store.$id === 'interface' &&
|
||||
actionName !== 'setNotificationPermission' &&
|
||||
actionName !== 'setLoginStatus'
|
||||
)
|
||||
return
|
||||
|
||||
// Initial state
|
||||
let vapidPublicKey = useInstanceStore().vapidPublicKey
|
||||
let enabled = useSyncConfigStore().mergedConfig.webPushNotifications
|
||||
let permissionGranted =
|
||||
useInterfaceStore().notificationPermission === 'granted'
|
||||
let permissionPresent =
|
||||
useInterfaceStore().notificationPermission !== undefined
|
||||
let user = !!window.vuex.state.users.currentUser
|
||||
|
||||
if (store.$id === 'instance') {
|
||||
if (actionName === 'set' && args[0].path === 'vapidPublicKey') {
|
||||
const { value } = args[0]
|
||||
vapidPublicKey = value
|
||||
}
|
||||
}
|
||||
|
||||
if (!vapidPublicKey || !permissionPresent) {
|
||||
return
|
||||
}
|
||||
|
||||
if (store.$id === 'interface') {
|
||||
if (actionName === 'setNotificationPermission') {
|
||||
permissionGranted = args[0] === 'granted'
|
||||
} else if (actionName === 'setLoginStatus') {
|
||||
user = args[0]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else if (store.$id === 'sync_config') {
|
||||
if (
|
||||
actionName === 'setPreference' &&
|
||||
args[0].path === 'simple.webPushNotifications'
|
||||
) {
|
||||
const { value } = args[0]
|
||||
enabled = value
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionGranted && enabled && user) {
|
||||
console.log('piniaReg')
|
||||
return window.vuex.dispatch('registerPushNotifications')
|
||||
} else {
|
||||
console.log('piniaUnreg')
|
||||
return window.vuex.dispatch('unregisterPushNotifications')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const vuexPushNotificationsPlugin = (store) => {
|
||||
store.subscribe((mutation, state) => {
|
||||
// Initial state
|
||||
const vapidPublicKey = useInstanceStore().vapidPublicKey
|
||||
const webPushNotification = state.config.webPushNotifications
|
||||
const permission = useInterfaceStore().notificationPermission === 'granted'
|
||||
const enabled = useSyncConfigStore().mergedConfig.webPushNotifications
|
||||
const permissionGranted =
|
||||
useInterfaceStore().notificationPermission === 'granted'
|
||||
const permissionPresent =
|
||||
useInterfaceStore().notificationPermission !== undefined
|
||||
const user = state.users.currentUser
|
||||
|
||||
const isUserMutation = mutation.type === 'setCurrentUser'
|
||||
const isVapidMutation =
|
||||
mutation.type === 'setInstanceOption' &&
|
||||
mutation.payload.name === 'vapidPublicKey'
|
||||
const isPermMutation =
|
||||
mutation.type === 'setNotificationPermission' &&
|
||||
mutation.payload === 'granted'
|
||||
const isUserConfigMutation =
|
||||
mutation.type === 'setOption' &&
|
||||
mutation.payload.name === 'webPushNotifications'
|
||||
const isVisibilityMutation =
|
||||
mutation.type === 'setOption' &&
|
||||
mutation.payload.name === 'notificationVisibility'
|
||||
if (!permissionPresent || !vapidPublicKey) return
|
||||
|
||||
if (
|
||||
isUserMutation ||
|
||||
isVapidMutation ||
|
||||
isPermMutation ||
|
||||
isUserConfigMutation ||
|
||||
isVisibilityMutation
|
||||
mutation.type === 'setCurrentUser' ||
|
||||
mutation.type === 'clearCurrentUser'
|
||||
) {
|
||||
if (user && vapidPublicKey && permission && webPushNotification) {
|
||||
console.log(!!user, permissionGranted, enabled)
|
||||
if (user && permissionGranted && enabled) {
|
||||
console.log('vuexReg')
|
||||
return store.dispatch('registerPushNotifications')
|
||||
} else if (isUserConfigMutation && !webPushNotification) {
|
||||
} else {
|
||||
console.log('vuexUnreg')
|
||||
return store.dispatch('unregisterPushNotifications')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
src/lib/style.js
Normal file
29
src/lib/style.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { applyStyleConfig } from 'src/services/style_setter/style_setter.js'
|
||||
|
||||
const APPEARANCE_SETTINGS_KEYS = new Set(
|
||||
[
|
||||
'sidebarColumnWidth',
|
||||
'contentColumnWidth',
|
||||
'notifsColumnWidth',
|
||||
'themeEditorMinWidth',
|
||||
'textSize',
|
||||
'navbarSize',
|
||||
'panelHeaderSize',
|
||||
'forcedRoundness',
|
||||
'emojiSize',
|
||||
'emojiReactionsScale',
|
||||
].map((x) => 'simple.' + x),
|
||||
)
|
||||
|
||||
export const piniaStylePlugin = ({ store, options }) => {
|
||||
if (store.$id === 'sync_config') {
|
||||
store.$onAction(({ name, args, after }) => {
|
||||
if (name === 'setPreference') {
|
||||
const { path } = args[0]
|
||||
if (APPEARANCE_SETTINGS_KEYS.has(path)) {
|
||||
after(() => applyStyleConfig(store.mergedConfig))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
15
src/main.js
15
src/main.js
|
|
@ -20,9 +20,15 @@ import messages from './i18n/messages.js'
|
|||
import createPersistedState, {
|
||||
piniaPersistPlugin,
|
||||
} from './lib/persisted_state.js'
|
||||
import pushNotifications from './lib/push_notifications_plugin.js'
|
||||
import {
|
||||
piniaPushNotificationsPlugin,
|
||||
vuexPushNotificationsPlugin,
|
||||
} from './lib/push_notifications_plugin.js'
|
||||
import vuexModules from './modules/index.js'
|
||||
|
||||
import { piniaLanguagePlugin } from 'src/lib/language.js'
|
||||
import { piniaStylePlugin } from 'src/lib/style.js'
|
||||
|
||||
const currentLocale = (window.navigator.language || 'en').split('-')[0]
|
||||
|
||||
const i18n = createI18n({
|
||||
|
|
@ -35,7 +41,7 @@ const i18n = createI18n({
|
|||
messages.setLanguage(i18n.global, currentLocale)
|
||||
|
||||
const persistedStateOptions = {
|
||||
paths: ['serverSideStorage.cache', 'config', 'users.lastLoginName', 'oauth'],
|
||||
paths: ['syncConfig.cache', 'config', 'users.lastLoginName', 'oauth'],
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
|
|
@ -66,9 +72,12 @@ const persistedStateOptions = {
|
|||
|
||||
try {
|
||||
let storageError
|
||||
const plugins = [pushNotifications]
|
||||
const plugins = [vuexPushNotificationsPlugin]
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPersistPlugin())
|
||||
pinia.use(piniaLanguagePlugin)
|
||||
pinia.use(piniaStylePlugin)
|
||||
pinia.use(piniaPushNotificationsPlugin)
|
||||
|
||||
try {
|
||||
const persistedState = await createPersistedState(persistedStateOptions)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { set } from 'lodash'
|
|||
|
||||
import messages from '../i18n/messages'
|
||||
import localeService from '../services/locale/locale.service.js'
|
||||
import { applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import { applyStyleConfig } from '../services/style_setter/style_setter.js'
|
||||
import { defaultState, instanceDefaultConfig } from './default_config_state.js'
|
||||
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
|
|
@ -71,7 +71,7 @@ const config = {
|
|||
mutations: {
|
||||
setOptionTemporarily(state, { name, value }) {
|
||||
set(state, name, value)
|
||||
applyConfig(state)
|
||||
applyStyleConfig(state)
|
||||
},
|
||||
setOption(state, { name, value }) {
|
||||
set(state, name, value)
|
||||
|
|
@ -162,7 +162,7 @@ const config = {
|
|||
} else {
|
||||
commit('setOption', { name, value })
|
||||
if (APPEARANCE_SETTINGS_KEYS.has(name)) {
|
||||
applyConfig(state)
|
||||
applyStyleConfig(state)
|
||||
}
|
||||
if (name.startsWith('theme3hacks')) {
|
||||
dispatch('applyTheme', { recompile: true })
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
||||
const browserLocale = (navigator.language || 'en').split('-')[0]
|
||||
|
||||
/// Instance config entries provided by static config or pleroma api
|
||||
/// Put settings here only if it does not make sense for a normal user
|
||||
/// to override it.
|
||||
export const staticOrApiConfigDefault = {
|
||||
name: 'PleromaFE',
|
||||
theme: 'pleroma-dark',
|
||||
palette: null,
|
||||
style: null,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from '../services/notification_utils/notification_utils.js'
|
||||
|
||||
import { useReportsStore } from 'src/stores/reports.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const emptyNotifications = () => ({
|
||||
desktopNotificationSilence: true,
|
||||
|
|
@ -119,9 +119,7 @@ export const notifications = {
|
|||
|
||||
maybeShowNotification(
|
||||
store,
|
||||
Object.values(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters,
|
||||
),
|
||||
Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters),
|
||||
notification,
|
||||
)
|
||||
} else if (notification.seen) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import { declarations } from 'src/modules/config_declaration'
|
||||
|
||||
|
|
@ -682,7 +682,7 @@ const users = {
|
|||
useInterfaceStore().setLastTimeline('public-timeline')
|
||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||
store.commit('clearServerSideStorage')
|
||||
//useSyncConfigStore().clearSyncConfig()
|
||||
})
|
||||
},
|
||||
loginUser(store, accessToken) {
|
||||
|
|
@ -702,7 +702,7 @@ const users = {
|
|||
user.domainMutes = []
|
||||
commit('setCurrentUser', user)
|
||||
|
||||
useServerSideStorageStore().setServerSideStorage(user)
|
||||
useSyncConfigStore().setSyncConfig(user)
|
||||
commit('addNewUsers', [user])
|
||||
|
||||
useEmojiStore().fetchEmoji()
|
||||
|
|
@ -723,17 +723,16 @@ const users = {
|
|||
/*
|
||||
// Reset wordfilter
|
||||
Object.keys(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters
|
||||
useSyncConfigStore().prefsStorage.simple.muteFilters
|
||||
).forEach(key => {
|
||||
useServerSideStorageStore().unsetPreference({ path: 'simple.muteFilters.' + key, value: null })
|
||||
useSyncConfigStore().unsetPreference({ path: 'simple.muteFilters.' + key, value: null })
|
||||
})
|
||||
|
||||
// Reset flag to 0 to re-run migrations
|
||||
useServerSideStorageStore().setFlag({ flag: 'configMigration', value: 0 })
|
||||
useSyncConfigStore().setFlag({ flag: 'configMigration', value: 0 })
|
||||
/**/
|
||||
|
||||
const { configMigration } =
|
||||
useServerSideStorageStore().flagStorage
|
||||
const { configMigration } = useSyncConfigStore().flagStorage
|
||||
declarations
|
||||
.filter((x) => {
|
||||
return (
|
||||
|
|
@ -744,12 +743,12 @@ const users = {
|
|||
})
|
||||
.toSorted((a, b) => a.configMigration - b.configMigration)
|
||||
.forEach((value) => {
|
||||
value.migration(useServerSideStorageStore(), store.rootState)
|
||||
useServerSideStorageStore().setFlag({
|
||||
value.migration(useSyncConfigStore(), store.rootState)
|
||||
useSyncConfigStore().setFlag({
|
||||
flag: 'configMigration',
|
||||
value: value.migrationNum,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
useSyncConfigStore().pushSyncConfig()
|
||||
})
|
||||
|
||||
if (user.token) {
|
||||
|
|
|
|||
|
|
@ -318,9 +318,7 @@ const updateProfileJSON = ({ credentials, params }) => {
|
|||
credentials,
|
||||
payload: params,
|
||||
method: 'PATCH',
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((data) => parseUser(data))
|
||||
}).then((data) => parseUser(data))
|
||||
}
|
||||
|
||||
// Params needed:
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ const extractStyleConfig = ({
|
|||
|
||||
const defaultStyleConfig = extractStyleConfig(defaultState)
|
||||
|
||||
export const applyConfig = (input) => {
|
||||
export const applyStyleConfig = (input) => {
|
||||
const config = extractStyleConfig(input)
|
||||
|
||||
if (config === defaultStyleConfig) {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function subscribePush(registration, isEnabled, vapidPublicKey) {
|
|||
function unsubscribePush(registration) {
|
||||
return registration.pushManager.getSubscription().then((subscription) => {
|
||||
if (subscription === null) {
|
||||
return
|
||||
return Promise.resolve('No subscription')
|
||||
}
|
||||
return subscription.unsubscribe()
|
||||
})
|
||||
|
|
@ -158,23 +158,23 @@ export function registerPushNotifications(
|
|||
|
||||
export function unregisterPushNotifications(token) {
|
||||
if (isPushSupported()) {
|
||||
Promise.all([
|
||||
deleteSubscriptionFromBackEnd(token),
|
||||
getOrCreateServiceWorker()
|
||||
.then((registration) => {
|
||||
return unsubscribePush(registration).then((result) => [
|
||||
registration,
|
||||
result,
|
||||
])
|
||||
})
|
||||
.then(([, unsubResult]) => {
|
||||
if (!unsubResult) {
|
||||
console.warn("Push subscription cancellation wasn't successful")
|
||||
}
|
||||
}),
|
||||
]).catch((e) =>
|
||||
console.warn(`Failed to disable Web Push Notifications: ${e.message}`),
|
||||
)
|
||||
getOrCreateServiceWorker()
|
||||
.then((registration) => {
|
||||
return unsubscribePush(registration).then((result) => [
|
||||
registration,
|
||||
result,
|
||||
])
|
||||
})
|
||||
.then(([, unsubResult]) => {
|
||||
if (unsubResult === 'No subscription') return
|
||||
if (!unsubResult) {
|
||||
console.warn("Push subscription cancellation wasn't successful")
|
||||
}
|
||||
return deleteSubscriptionFromBackEnd(token)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.warn(`Failed to disable Web Push Notifications: ${e.message}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
import Cookies from 'js-cookie'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import messages from 'src/i18n/messages'
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
|
||||
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
||||
|
||||
export const useI18nStore = defineStore('i18n', {
|
||||
state: () => ({
|
||||
i18n: null,
|
||||
|
|
@ -10,5 +19,16 @@ export const useI18nStore = defineStore('i18n', {
|
|||
i18n: newI18n.global,
|
||||
})
|
||||
},
|
||||
setLanguage(originalValue) {
|
||||
const value =
|
||||
originalValue || useSyncConfigStore().mergedConfig.interfaceLanguage
|
||||
|
||||
messages.setLanguage(this.i18n, value)
|
||||
useEmojiStore().loadUnicodeEmojiData(value)
|
||||
Cookies.set(
|
||||
BACKEND_LANGUAGE_COOKIE_NAME,
|
||||
localeService.internalToBackendLocaleMulti(value),
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@ import { toRaw } from 'vue'
|
|||
|
||||
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
|
||||
|
||||
export const VERSION = 1
|
||||
import { useInstanceStore } from 'src/stores/instance'
|
||||
|
||||
import { defaultState as configDefaultState } from 'src/modules/default_config_state'
|
||||
|
||||
export const VERSION = 2
|
||||
export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically
|
||||
|
||||
export const COMMAND_TRIM_FLAGS = 1000
|
||||
|
|
@ -41,6 +45,7 @@ export const defaultState = {
|
|||
dontShowUpdateNotifs: false,
|
||||
collapseNav: false,
|
||||
muteFilters: {},
|
||||
...configDefaultState,
|
||||
},
|
||||
collections: {
|
||||
pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'],
|
||||
|
|
@ -128,13 +133,13 @@ export const _getRecentData = (cache, live, isTest) => {
|
|||
live._version === cache._version
|
||||
) {
|
||||
console.debug(
|
||||
'Same version/timestamp on both source, source of truth irrelevant',
|
||||
'Same version/timestamp on both sources, source of truth irrelevant',
|
||||
)
|
||||
result.recent = cache
|
||||
result.stale = live
|
||||
} else {
|
||||
console.debug(
|
||||
'Different timestamp, figuring out which one is more recent',
|
||||
'Different timestamp or version, figuring out which one is more recent',
|
||||
)
|
||||
if (live._timestamp < cache._timestamp) {
|
||||
result.recent = cache
|
||||
|
|
@ -208,7 +213,7 @@ const _mergeJournal = (...journals) => {
|
|||
// side effect
|
||||
journal.sort((a, b) => (a.timestamp > b.timestamp ? 1 : -1))
|
||||
|
||||
if (path.startsWith('collections')) {
|
||||
if (path.startsWith('collections') || path.startsWith('objectCollection')) {
|
||||
const lastRemoveIndex = findLastIndex(
|
||||
journal,
|
||||
({ operation }) => operation === 'removeFromCollection',
|
||||
|
|
@ -229,6 +234,7 @@ const _mergeJournal = (...journals) => {
|
|||
return false
|
||||
}
|
||||
if (a.operation === 'addToCollection') {
|
||||
// TODO check how objectCollections behaves here
|
||||
return a.args[0] === b.args[0]
|
||||
}
|
||||
return false
|
||||
|
|
@ -368,21 +374,21 @@ export const _resetFlags = (
|
|||
return result
|
||||
}
|
||||
|
||||
export const _doMigrations = (cache) => {
|
||||
if (!cache) return cache
|
||||
export const _doMigrations = (cache, live) => {
|
||||
const data = cache ?? live
|
||||
|
||||
if (cache._version < VERSION) {
|
||||
if (data._version < VERSION) {
|
||||
console.debug(
|
||||
'Local cached data has older version, seeing if there any migrations that can be applied',
|
||||
'Data has older version, seeing if there any migrations that can be applied',
|
||||
)
|
||||
|
||||
// no migrations right now since we only have one version
|
||||
console.debug('No migrations found')
|
||||
}
|
||||
|
||||
if (cache._version > VERSION) {
|
||||
if (data._version > VERSION) {
|
||||
console.debug(
|
||||
'Local cached data has newer version, seeing if there any reverse migrations that can be applied',
|
||||
'Data has newer version, seeing if there any reverse migrations that can be applied',
|
||||
)
|
||||
|
||||
// no reverse migrations right now but we leave a possibility of loading a hotpatch if need be
|
||||
|
|
@ -391,9 +397,9 @@ export const _doMigrations = (cache) => {
|
|||
console.debug('Found hotpatch migration, applying')
|
||||
return window._PLEROMA_HOTPATCH.reverseMigrations.call(
|
||||
{},
|
||||
'serverSideStorage',
|
||||
{ from: cache._version, to: VERSION },
|
||||
cache,
|
||||
'syncConfigStore',
|
||||
{ from: data._version, to: VERSION },
|
||||
data,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -402,7 +408,7 @@ export const _doMigrations = (cache) => {
|
|||
return cache
|
||||
}
|
||||
|
||||
export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
||||
export const useSyncConfigStore = defineStore('sync_config', {
|
||||
state() {
|
||||
return cloneDeep(defaultState)
|
||||
},
|
||||
|
|
@ -510,19 +516,40 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
`tried to edit internal (starts with _) field '${path}', ignoring.`,
|
||||
)
|
||||
}
|
||||
const collection = new Set(get(this.prefsStorage, path))
|
||||
collection.delete(value)
|
||||
set(this.prefsStorage, path, [...collection])
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{
|
||||
operation: 'removeFromCollection',
|
||||
path,
|
||||
args: [value],
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
]
|
||||
this.dirty = true
|
||||
|
||||
const { _key } = value
|
||||
if (path.startsWith('collection')) {
|
||||
const collection = new Set(get(this.prefsStorage, path))
|
||||
collection.delete(value)
|
||||
set(this.prefsStorage, path, [...collection])
|
||||
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{
|
||||
operation: 'removeFromCollection',
|
||||
path,
|
||||
args: [value],
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
]
|
||||
this.dirty = true
|
||||
} else if (path.startsWith('objectCollection')) {
|
||||
const collection = new Set(get(this.prefsStorage, path + '.index'))
|
||||
collection.delete(_key)
|
||||
set(this.prefsStorage, path + '.index', [...collection])
|
||||
const data = get(this.prefsStorage, path + '.data')
|
||||
delete data[_key]
|
||||
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{
|
||||
operation: 'removeFromCollection',
|
||||
path,
|
||||
args: [{ _key }],
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
reorderCollectionPreference({ path, value, movement }) {
|
||||
if (path.startsWith('_')) {
|
||||
|
|
@ -554,24 +581,23 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
username,
|
||||
)
|
||||
},
|
||||
clearServerSideStorage() {
|
||||
clearSyncConfig() {
|
||||
const blankState = { ...cloneDeep(defaultState) }
|
||||
Object.keys(this).forEach((k) => {
|
||||
this[k] = blankState[k]
|
||||
})
|
||||
},
|
||||
setServerSideStorage(userData) {
|
||||
setSyncConfig(userData) {
|
||||
const live = userData.storage
|
||||
this.raw = live
|
||||
let cache = this.cache
|
||||
if (cache && cache._user !== userData.fqn) {
|
||||
if (cache?._user !== userData.fqn) {
|
||||
console.warn(
|
||||
'Cache belongs to another user! reinitializing local cache!',
|
||||
)
|
||||
cache = null
|
||||
}
|
||||
|
||||
cache = _doMigrations(cache)
|
||||
console.log(cache, live)
|
||||
|
||||
let { recent, stale, needUpload } = _getRecentData(cache, live)
|
||||
|
||||
|
|
@ -589,6 +615,9 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
})
|
||||
}
|
||||
|
||||
recent = recent && _doMigrations(recent)
|
||||
stale = stale && _doMigrations(stale)
|
||||
|
||||
if (!needUpload && recent && stale) {
|
||||
console.debug('Checking if data needs merging...')
|
||||
// discarding timestamps and versions
|
||||
|
|
@ -627,7 +656,7 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
this.flagStorage = this.cache.flagStorage
|
||||
this.prefsStorage = this.cache.prefsStorage
|
||||
},
|
||||
pushServerSideStorage({ force = false } = {}) {
|
||||
pushSyncConfig({ force = false } = {}) {
|
||||
const needPush = this.dirty || force
|
||||
if (!needPush) return
|
||||
this.updateCache({ username: window.vuex.state.users.currentUser.fqn })
|
||||
|
|
@ -635,9 +664,26 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
window.vuex.state.api.backendInteractor
|
||||
.updateProfileJSON({ params })
|
||||
.then((user) => {
|
||||
this.setServerSideStorage(user)
|
||||
this.setSyncConfig(user)
|
||||
this.dirty = false
|
||||
})
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
mergedConfig: (state) => {
|
||||
const instancePrefs = useInstanceStore().prefsStorage
|
||||
const result = Object.fromEntries(
|
||||
Object.entries(state.prefsStorage.simple).map(([k, v]) => [
|
||||
k,
|
||||
v ?? instancePrefs[k],
|
||||
]),
|
||||
)
|
||||
return result
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
afterLoad(state) {
|
||||
return state
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
createTestingPinia()
|
||||
|
||||
createTestingPinia()
|
||||
|
||||
import { createMemoryHistory, createRouter } from 'vue-router'
|
||||
import { createStore } from 'vuex'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { nextTick } from 'vue'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||
import { $t, mountOpts, waitForEvent } from '../../../fixtures/setup_test'
|
||||
|
|
|
|||
|
|
@ -1,23 +1,19 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import vClickOutside from 'click-outside-vue3'
|
||||
import { h } from 'vue'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
createTestingPinia()
|
||||
|
||||
createTestingPinia()
|
||||
|
||||
import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
const generateInput = (value, padEmoji = true) => {
|
||||
const wrapper = shallowMount(EmojiInput, {
|
||||
global: {
|
||||
renderStubDefaultSlot: true,
|
||||
mocks: {
|
||||
$store: {
|
||||
getters: {
|
||||
mergedConfig: {
|
||||
padEmoji,
|
||||
},
|
||||
},
|
||||
},
|
||||
$t: (msg) => msg,
|
||||
},
|
||||
stubs: {
|
||||
|
|
@ -48,6 +44,12 @@ const generateInput = (value, padEmoji = true) => {
|
|||
}
|
||||
|
||||
describe('EmojiInput', () => {
|
||||
beforeEach(() => {
|
||||
const store = useSyncConfigStore(createTestingPinia())
|
||||
store.mergedConfig = {
|
||||
padEmoji: true,
|
||||
}
|
||||
})
|
||||
describe('insertion mechanism', () => {
|
||||
it('inserts string at the end with trailing space', () => {
|
||||
const initialString = 'Testing'
|
||||
|
|
@ -111,6 +113,10 @@ describe('EmojiInput', () => {
|
|||
it('inserts string without any padding if padEmoji setting is set to false', () => {
|
||||
const initialString = 'Eat some spam!'
|
||||
const wrapper = generateInput(initialString, false)
|
||||
const store = useSyncConfigStore(createTestingPinia())
|
||||
store.mergedConfig = {
|
||||
padEmoji: false,
|
||||
}
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length, keepOpen: false })
|
||||
|
|
@ -146,6 +152,10 @@ describe('EmojiInput', () => {
|
|||
it('correctly sets caret after insertion if padEmoji setting is set to false', async () => {
|
||||
const initialString = '1234'
|
||||
const wrapper = generateInput(initialString, false)
|
||||
const store = useSyncConfigStore(createTestingPinia())
|
||||
store.mergedConfig = {
|
||||
padEmoji: false,
|
||||
}
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length })
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
import {
|
||||
defaultState,
|
||||
mutations,
|
||||
prepareStatus,
|
||||
} from '../../../../src/modules/statuses.js'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
createTestingPinia()
|
||||
|
||||
createTestingPinia()
|
||||
|
||||
const makeMockStatus = ({ id, text, type = 'status' }) => {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,21 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import * as NotificationUtils from 'src/services/notification_utils/notification_utils.js'
|
||||
|
||||
describe('NotificationUtils', () => {
|
||||
beforeEach(() => {
|
||||
const store = useSyncConfigStore(createTestingPinia())
|
||||
store.mergedConfig = {
|
||||
notificationVisibility: {
|
||||
likes: true,
|
||||
repeats: true,
|
||||
mentions: false,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('filteredNotificationsFromStore', () => {
|
||||
it('should return sorted notifications with configured types', () => {
|
||||
const store = {
|
||||
|
|
@ -26,13 +41,7 @@ describe('NotificationUtils', () => {
|
|||
},
|
||||
},
|
||||
getters: {
|
||||
mergedConfig: {
|
||||
notificationVisibility: {
|
||||
likes: true,
|
||||
repeats: true,
|
||||
mentions: false,
|
||||
},
|
||||
},
|
||||
mergedConfig: {},
|
||||
},
|
||||
}
|
||||
const expected = [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { HttpResponse, http } from 'msw'
|
||||
import { setActivePinia, createPinia } from 'pinia'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { HttpResponse, http } from 'msw'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
|
||||
import {
|
||||
authApis,
|
||||
|
|
@ -8,8 +8,8 @@ import {
|
|||
testServer,
|
||||
} from '/test/fixtures/mock_api.js'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
||||
const test = injectMswToTest(authApis)
|
||||
|
||||
|
|
@ -61,7 +61,6 @@ describe('oauth store', () => {
|
|||
}),
|
||||
)
|
||||
|
||||
|
||||
const store = useOAuthStore()
|
||||
store.clientId = 'another-id'
|
||||
store.clientSecret = 'another-secret'
|
||||
|
|
@ -183,7 +182,9 @@ describe('oauth store', () => {
|
|||
await expect(store.ensureAppToken()).rejects.toThrowError('Throttled')
|
||||
})
|
||||
|
||||
test('it should throw if we cannot obtain app token', async ({ worker }) => {
|
||||
test('it should throw if we cannot obtain app token', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(
|
||||
http.post(`${testServer}/oauth/token`, () => {
|
||||
return HttpResponse.text('Throttled', { status: 429 })
|
||||
|
|
|
|||
|
|
@ -12,25 +12,25 @@ import {
|
|||
COMMAND_TRIM_FLAGS_AND_RESET,
|
||||
defaultState,
|
||||
newUserFlags,
|
||||
useServerSideStorageStore,
|
||||
useSyncConfigStore,
|
||||
VERSION,
|
||||
} from 'src/stores/serverSideStorage.js'
|
||||
} from 'src/stores/sync_config.js'
|
||||
|
||||
describe('The serverSideStorage module', () => {
|
||||
describe('The SyncConfig module', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
describe('mutations', () => {
|
||||
describe('setServerSideStorage', () => {
|
||||
describe('setSyncConfig', () => {
|
||||
const user = {
|
||||
created_at: new Date('1999-02-09'),
|
||||
storage: {},
|
||||
}
|
||||
|
||||
it('should initialize storage if none present', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
store.setServerSideStorage(store, user)
|
||||
const store = useSyncConfigStore()
|
||||
store.setSyncConfig({ ...user })
|
||||
expect(store.cache._version).to.eql(VERSION)
|
||||
expect(store.cache._timestamp).to.be.a('number')
|
||||
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||
|
|
@ -38,8 +38,8 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
|
||||
it('should initialize storage with proper flags for new users if none present', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
store.setServerSideStorage({ ...user, created_at: new Date() })
|
||||
const store = useSyncConfigStore()
|
||||
store.setSyncConfig({ ...user, created_at: new Date() })
|
||||
expect(store.cache._version).to.eql(VERSION)
|
||||
expect(store.cache._timestamp).to.be.a('number')
|
||||
expect(store.cache.flagStorage).to.eql(newUserFlags)
|
||||
|
|
@ -47,14 +47,14 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
|
||||
it('should merge flags even if remote timestamp is older', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.cache = {
|
||||
_timestamp: Date.now(),
|
||||
_version: VERSION,
|
||||
...cloneDeep(defaultState),
|
||||
}
|
||||
|
||||
store.setServerSideStorage({
|
||||
store.setSyncConfig({
|
||||
...user,
|
||||
storage: {
|
||||
_timestamp: 123,
|
||||
|
|
@ -76,10 +76,10 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
|
||||
it('should reset local timestamp to remote if contents are the same', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.cache = null
|
||||
|
||||
store.setServerSideStorage({
|
||||
store.setSyncConfig({
|
||||
...user,
|
||||
storage: {
|
||||
_timestamp: 123,
|
||||
|
|
@ -95,9 +95,9 @@ describe('The serverSideStorage module', () => {
|
|||
expect(store.cache.flagStorage.updateCounter).to.eql(999)
|
||||
})
|
||||
|
||||
it('should remote version if local missing', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
store.setServerSideStorage(store, user)
|
||||
it('should use remote version if local missing', () => {
|
||||
const store = useSyncConfigStore()
|
||||
store.setSyncConfig(store, user)
|
||||
expect(store.cache._version).to.eql(VERSION)
|
||||
expect(store.cache._timestamp).to.be.a('number')
|
||||
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||
|
|
@ -105,7 +105,7 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
describe('setPreference', () => {
|
||||
it('should set preference and update journal log accordingly', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||
expect(store.prefsStorage.simple.testing).to.eql(1)
|
||||
expect(store.prefsStorage._journal.length).to.eql(1)
|
||||
|
|
@ -119,18 +119,34 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
|
||||
it('should keep journal to a minimum', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||
store.setPreference({ path: 'simple.testing', value: 2 })
|
||||
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||
store.addCollectionPreference({
|
||||
path: 'objectCollections.testing',
|
||||
value: { _key: 'a', foo: 1 },
|
||||
})
|
||||
expect(store.prefsStorage.objectCollections.testing).to.eql({
|
||||
data: { a: { _key: 'a', foo: 1 } },
|
||||
index: ['a'],
|
||||
})
|
||||
store.removeCollectionPreference({
|
||||
path: 'collections.testing',
|
||||
value: 2,
|
||||
})
|
||||
store.removeCollectionPreference({
|
||||
path: 'objectCollections.testing',
|
||||
value: { _key: 'a' },
|
||||
})
|
||||
store.updateCache({ username: 'test' })
|
||||
expect(store.prefsStorage.simple.testing).to.eql(2)
|
||||
expect(store.prefsStorage.collections.testing).to.eql([])
|
||||
expect(store.prefsStorage._journal.length).to.eql(2)
|
||||
expect(store.prefsStorage.objectCollections.testing).to.eql({
|
||||
data: {},
|
||||
index: [],
|
||||
})
|
||||
expect(store.prefsStorage._journal.length).to.eql(3)
|
||||
expect(store.prefsStorage._journal[0]).to.eql({
|
||||
path: 'simple.testing',
|
||||
operation: 'set',
|
||||
|
|
@ -145,22 +161,41 @@ describe('The serverSideStorage module', () => {
|
|||
// should have A timestamp, we don't really care what it is
|
||||
timestamp: store.prefsStorage._journal[1].timestamp,
|
||||
})
|
||||
expect(store.prefsStorage._journal[2]).to.eql({
|
||||
path: 'objectCollections.testing',
|
||||
operation: 'removeFromCollection',
|
||||
args: [{ _key: 'a' }],
|
||||
// should have A timestamp, we don't really care what it is
|
||||
timestamp: store.prefsStorage._journal[2].timestamp,
|
||||
})
|
||||
})
|
||||
|
||||
it('should remove duplicate entries from journal', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||
store.addCollectionPreference({
|
||||
path: 'objectCollections.testing',
|
||||
value: { _key: 'a', foo: 1 },
|
||||
})
|
||||
store.addCollectionPreference({
|
||||
path: 'objectCollections.testing',
|
||||
value: { _key: 'a', foo: 1 },
|
||||
})
|
||||
store.updateCache({ username: 'test' })
|
||||
expect(store.prefsStorage.simple.testing).to.eql(1)
|
||||
expect(store.prefsStorage.collections.testing).to.eql([2])
|
||||
expect(store.prefsStorage._journal.length).to.eql(2)
|
||||
expect(store.prefsStorage.objectCollections.testing).to.eql({
|
||||
data: { a: { _key: 'a', foo: 1 } },
|
||||
index: ['a'],
|
||||
})
|
||||
expect(store.prefsStorage._journal.length).to.eql(4)
|
||||
})
|
||||
|
||||
it('should remove depth = 3 set/unset entries from journal', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
||||
store.unsetPreference({ path: 'simple.object.foo' })
|
||||
store.updateCache(store, { username: 'test' })
|
||||
|
|
@ -169,7 +204,7 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
|
||||
it('should not allow unsetting depth <= 2', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
||||
expect(() => store.unsetPreference({ path: 'simple' })).to.throw()
|
||||
expect(() =>
|
||||
|
|
@ -178,7 +213,7 @@ describe('The serverSideStorage module', () => {
|
|||
})
|
||||
|
||||
it('should not allow (un)setting depth > 3', () => {
|
||||
const store = useServerSideStorageStore()
|
||||
const store = useSyncConfigStore()
|
||||
store.setPreference({ path: 'simple.object', value: {} })
|
||||
expect(() =>
|
||||
store.setPreference({ path: 'simple.object.lv3', value: 1 }),
|
||||
Loading…
Add table
Add a link
Reference in a new issue