some initial work on moving instance settings to pinia
This commit is contained in:
parent
20071d5a11
commit
9452b3084a
10 changed files with 559 additions and 195 deletions
41
src/App.js
41
src/App.js
|
|
@ -22,6 +22,7 @@ import UserReportingModal from './components/user_reporting_modal/user_reporting
|
|||
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
|
||||
import { getOrCreateServiceWorker } from './services/sw/sw'
|
||||
import { windowHeight, windowWidth } from './services/window_utils/window_utils'
|
||||
import { useInstanceStore } from './stores/instance'
|
||||
import { useInterfaceStore } from './stores/interface'
|
||||
import { useShoutStore } from './stores/shout'
|
||||
|
||||
|
|
@ -135,11 +136,6 @@ export default {
|
|||
userBackground() {
|
||||
return this.currentUser.background_image
|
||||
},
|
||||
instanceBackground() {
|
||||
return this.mergedConfig.hideInstanceWallpaper
|
||||
? null
|
||||
: this.$store.state.instance.background
|
||||
},
|
||||
background() {
|
||||
return this.userBackground || this.instanceBackground
|
||||
},
|
||||
|
|
@ -153,16 +149,6 @@ export default {
|
|||
shout() {
|
||||
return useShoutStore().joined
|
||||
},
|
||||
suggestionsEnabled() {
|
||||
return this.$store.state.instance.suggestionsEnabled
|
||||
},
|
||||
showInstanceSpecificPanel() {
|
||||
return (
|
||||
this.$store.state.instance.showInstanceSpecificPanel &&
|
||||
!this.$store.getters.mergedConfig.hideISP &&
|
||||
this.$store.state.instance.instanceSpecificPanelContent
|
||||
)
|
||||
},
|
||||
isChats() {
|
||||
return this.$route.name === 'chat' || this.$route.name === 'chats'
|
||||
},
|
||||
|
|
@ -177,21 +163,12 @@ export default {
|
|||
this.layoutType === 'mobile'
|
||||
)
|
||||
},
|
||||
showFeaturesPanel() {
|
||||
return this.$store.state.instance.showFeaturesPanel
|
||||
},
|
||||
editingAvailable() {
|
||||
return this.$store.state.instance.editingAvailable
|
||||
},
|
||||
shoutboxPosition() {
|
||||
return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
|
||||
},
|
||||
layoutType() {
|
||||
return useInterfaceStore().layoutType
|
||||
},
|
||||
privateMode() {
|
||||
return this.$store.state.instance.private
|
||||
},
|
||||
reverseLayout() {
|
||||
const { thirdColumnMode, sidebarRight: reverseSetting } =
|
||||
this.$store.getters.mergedConfig
|
||||
|
|
@ -214,8 +191,22 @@ export default {
|
|||
},
|
||||
...mapGetters(['mergedConfig']),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
hideShoutbox: (store) => store.prefsStorage.simple.hideShoutbox,
|
||||
hideShoutbox: (store) => store.mergedConfig.hideShoutbox,
|
||||
}),
|
||||
...mapState(useInstanceStore, {
|
||||
instanceBackground: (store) =>
|
||||
this.mergedConfig.hideInstanceWallpaper ? null : store.background,
|
||||
showInstanceSpecificPanel: (store) =>
|
||||
store.showInstanceSpecificPanel &&
|
||||
!this.$store.getters.mergedConfig.hideISP &&
|
||||
store.instanceSpecificPanelContent,
|
||||
}),
|
||||
...mapState(useInstanceStore, [
|
||||
'editingAvailable',
|
||||
'showFeaturesPanel',
|
||||
'private',
|
||||
'suggestionsEnabled',
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
resizeHandler() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* global process */
|
||||
|
||||
import vClickOutside from 'click-outside-vue3'
|
||||
import { get, set } from 'lodash'
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import VueVirtualScroller from 'vue-virtual-scroller'
|
||||
|
|
@ -22,6 +23,7 @@ import {
|
|||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
||||
import { useI18nStore } from 'src/stores/i18n'
|
||||
import { useInstanceStore } from 'src/stores/instance'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useOAuthStore } from 'src/stores/oauth'
|
||||
import App from '../App.vue'
|
||||
|
|
@ -78,30 +80,30 @@ const getInstanceConfig = async ({ store }) => {
|
|||
const textlimit = data.max_toot_chars
|
||||
const vapidPublicKey = data.pleroma.vapid_public_key
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaExtensionsAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaExtensionsAvailable',
|
||||
value: data.pleroma,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'textlimit',
|
||||
useInstanceStore().set({
|
||||
path: 'textlimit',
|
||||
value: textlimit,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'accountApprovalRequired',
|
||||
useInstanceStore().set({
|
||||
path: 'accountApprovalRequired',
|
||||
value: data.approval_required,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'birthdayRequired',
|
||||
useInstanceStore().set({
|
||||
path: 'birthdayRequired',
|
||||
value: !!data.pleroma?.metadata.birthday_required,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'birthdayMinAge',
|
||||
useInstanceStore().set({
|
||||
path: 'birthdayMinAge',
|
||||
value: data.pleroma?.metadata.birthday_min_age || 0,
|
||||
})
|
||||
|
||||
if (vapidPublicKey) {
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'vapidPublicKey',
|
||||
useInstanceStore().set({
|
||||
path: 'vapidPublicKey',
|
||||
value: vapidPublicKey,
|
||||
})
|
||||
}
|
||||
|
|
@ -156,19 +158,32 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
|||
let config = {}
|
||||
if (overrides.staticConfigPreference && env === 'development') {
|
||||
console.warn('OVERRIDING API CONFIG WITH STATIC CONFIG')
|
||||
config = Object.assign({}, apiConfig, staticConfig)
|
||||
config = { ...apiConfig, ...staticConfig }
|
||||
} else {
|
||||
config = Object.assign({}, staticConfig, apiConfig)
|
||||
config = { ...staticConfig, ...apiConfig }
|
||||
}
|
||||
console.trace(config)
|
||||
|
||||
const copyInstanceOption = (name) => {
|
||||
if (typeof config[name] !== 'undefined') {
|
||||
store.dispatch('setInstanceOption', { name, value: config[name] })
|
||||
const copyInstanceIdentityOption = (path) => {
|
||||
if (get(config, path) !== undefined) {
|
||||
useInstanceStore().set({
|
||||
path: `instanceIdentity.${path}`,
|
||||
value: get(config, path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(staticOrApiConfigDefault).forEach(copyInstanceOption)
|
||||
Object.keys(instanceDefaultConfig).forEach(copyInstanceOption)
|
||||
const copyInstancePrefOption = (path) => {
|
||||
if (get(config, path) !== undefined) {
|
||||
useInstanceStore().set({
|
||||
path: `prefsStorage.${path}`,
|
||||
value: get(config, path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(staticOrApiConfigDefault).forEach(copyInstanceIdentityOption)
|
||||
Object.keys(instanceDefaultConfig).forEach(copyInstancePrefOption)
|
||||
|
||||
useAuthFlowStore().setInitialStrategy(config.loginMethod)
|
||||
}
|
||||
|
|
@ -178,7 +193,7 @@ const getTOS = async ({ store }) => {
|
|||
const res = await window.fetch('/static/terms-of-service.html')
|
||||
if (res.ok) {
|
||||
const html = await res.text()
|
||||
store.dispatch('setInstanceOption', { name: 'tos', value: html })
|
||||
useInstanceStore().set({ path: 'tos', value: html })
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
|
|
@ -192,8 +207,8 @@ const getInstancePanel = async ({ store }) => {
|
|||
const res = await preloadFetch('/instance/panel.html')
|
||||
if (res.ok) {
|
||||
const html = await res.text()
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'instanceSpecificPanelContent',
|
||||
useInstanceStore().set({
|
||||
path: 'instanceSpecificPanelContent',
|
||||
value: html,
|
||||
})
|
||||
} else {
|
||||
|
|
@ -227,7 +242,7 @@ const getStickers = async ({ store }) => {
|
|||
).sort((a, b) => {
|
||||
return a.meta.title.localeCompare(b.meta.title)
|
||||
})
|
||||
store.dispatch('setInstanceOption', { name: 'stickers', value: stickers })
|
||||
useInstanceStore().set({ path: 'stickers', value: stickers })
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
|
|
@ -248,8 +263,8 @@ const getAppSecret = async ({ store }) => {
|
|||
|
||||
const resolveStaffAccounts = ({ store, accounts }) => {
|
||||
const nicknames = accounts.map((uri) => uri.split('/').pop())
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'staffAccounts',
|
||||
useInstanceStore().set({
|
||||
path: 'staffAccounts',
|
||||
value: nicknames,
|
||||
})
|
||||
}
|
||||
|
|
@ -262,160 +277,160 @@ const getNodeInfo = async ({ store }) => {
|
|||
const data = await res.json()
|
||||
const metadata = data.metadata
|
||||
const features = metadata.features
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'name',
|
||||
useInstanceStore().set({
|
||||
path: 'name',
|
||||
value: metadata.nodeName,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'registrationOpen',
|
||||
useInstanceStore().set({
|
||||
path: 'registrationOpen',
|
||||
value: data.openRegistrations,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'mediaProxyAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.mediaProxyAvailable',
|
||||
value: features.includes('media_proxy'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'safeDM',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.safeDM',
|
||||
value: features.includes('safe_dm_mentions'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'shoutAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.shoutAvailable',
|
||||
value: features.includes('chat'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaChatMessagesAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaChatMessagesAvailable',
|
||||
value: features.includes('pleroma_chat_messages'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaCustomEmojiReactionsAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaCustomEmojiReactionsAvailable',
|
||||
value:
|
||||
features.includes('pleroma_custom_emoji_reactions') ||
|
||||
features.includes('custom_emoji_reactions'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaBookmarkFoldersAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaBookmarkFoldersAvailable',
|
||||
value: features.includes('pleroma:bookmark_folders'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'gopherAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.gopherAvailable',
|
||||
value: features.includes('gopher'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pollsAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pollsAvailable',
|
||||
value: features.includes('polls'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'editingAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.editingAvailable',
|
||||
value: features.includes('editing'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pollLimits',
|
||||
useInstanceStore().set({
|
||||
path: 'pollLimits',
|
||||
value: metadata.pollLimits,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'mailerEnabled',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.mailerEnabled',
|
||||
value: metadata.mailerEnabled,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'quotingAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.quotingAvailable',
|
||||
value: features.includes('quote_posting'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'groupActorAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.groupActorAvailable',
|
||||
value: features.includes('pleroma:group_actors'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'blockExpiration',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.blockExpiration',
|
||||
value: features.includes('pleroma:block_expiration'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'localBubbleInstances',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.localBubbleInstances',
|
||||
value: metadata.localBubbleInstances ?? [],
|
||||
})
|
||||
|
||||
const uploadLimits = metadata.uploadLimits
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'uploadlimit',
|
||||
useInstanceStore().set({
|
||||
path: 'uploadlimit',
|
||||
value: parseInt(uploadLimits.general),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'avatarlimit',
|
||||
useInstanceStore().set({
|
||||
path: 'avatarlimit',
|
||||
value: parseInt(uploadLimits.avatar),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'backgroundlimit',
|
||||
useInstanceStore().set({
|
||||
path: 'backgroundlimit',
|
||||
value: parseInt(uploadLimits.background),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'bannerlimit',
|
||||
useInstanceStore().set({
|
||||
path: 'bannerlimit',
|
||||
value: parseInt(uploadLimits.banner),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'fieldsLimits',
|
||||
useInstanceStore().set({
|
||||
path: 'fieldsLimits',
|
||||
value: metadata.fieldsLimits,
|
||||
})
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'restrictedNicknames',
|
||||
useInstanceStore().set({
|
||||
path: 'restrictedNicknames',
|
||||
value: metadata.restrictedNicknames,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'postFormats',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.postFormats',
|
||||
value: metadata.postFormats,
|
||||
})
|
||||
|
||||
const suggestions = metadata.suggestions
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'suggestionsEnabled',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.suggestionsEnabled',
|
||||
value: suggestions.enabled,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'suggestionsWeb',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.suggestionsWeb',
|
||||
value: suggestions.web,
|
||||
})
|
||||
|
||||
const software = data.software
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'backendVersion',
|
||||
useInstanceStore().set({
|
||||
path: 'backendVersion',
|
||||
value: software.version,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'backendRepository',
|
||||
useInstanceStore().set({
|
||||
path: 'backendRepository',
|
||||
value: software.repository,
|
||||
})
|
||||
|
||||
const priv = metadata.private
|
||||
store.dispatch('setInstanceOption', { name: 'private', value: priv })
|
||||
useInstanceStore().set({ path: 'private', value: priv })
|
||||
|
||||
const frontendVersion = window.___pleromafe_commit_hash
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'frontendVersion',
|
||||
useInstanceStore().set({
|
||||
path: 'frontendVersion',
|
||||
value: frontendVersion,
|
||||
})
|
||||
|
||||
const federation = metadata.federation
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'tagPolicyAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.tagPolicyAvailable',
|
||||
value:
|
||||
typeof federation.mrf_policies === 'undefined'
|
||||
? false
|
||||
: metadata.federation.mrf_policies.includes('TagPolicy'),
|
||||
})
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'federationPolicy',
|
||||
useInstanceStore().set({
|
||||
path: 'federationPolicy',
|
||||
value: federation,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'federating',
|
||||
useInstanceStore().set({
|
||||
path: 'federating',
|
||||
value:
|
||||
typeof federation.enabled === 'undefined' ? true : federation.enabled,
|
||||
})
|
||||
|
||||
const accountActivationRequired = metadata.accountActivationRequired
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'accountActivationRequired',
|
||||
useInstanceStore().set({
|
||||
path: 'accountActivationRequired',
|
||||
value: accountActivationRequired,
|
||||
})
|
||||
|
||||
|
|
@ -526,7 +541,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
|||
typeof overrides.target !== 'undefined'
|
||||
? overrides.target
|
||||
: window.location.origin
|
||||
store.dispatch('setInstanceOption', { name: 'server', value: server })
|
||||
useInstanceStore().set({ path: 'server', value: server })
|
||||
|
||||
await setConfig({ store })
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -299,13 +299,6 @@ const PostStatusForm = {
|
|||
isOverLengthLimit() {
|
||||
return this.hasStatusLengthLimit && this.charactersLeft < 0
|
||||
},
|
||||
minimalScopesMode() {
|
||||
return useServerSideStorageStore().prefsStorage.simple.minimalScopesMode
|
||||
},
|
||||
alwaysShowSubject() {
|
||||
return useServerSideStorageStore().prefsStorage.simple
|
||||
.alwaysShowSubjectInput
|
||||
},
|
||||
postFormats() {
|
||||
return this.$store.state.instance.postFormats || []
|
||||
},
|
||||
|
|
@ -412,6 +405,10 @@ const PostStatusForm = {
|
|||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: (store) => store.mobileLayout,
|
||||
}),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
minimalScopesMode: (store) => store.mergedConfig.minimalScopesMode,
|
||||
alwaysShowSubject: (store) => store.mergedConfig.alwaysShowSubjectInput,
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
newStatus: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { cloneDeep, get, isEqual, set } from 'lodash'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import DraftButtons from './draft_buttons.vue'
|
||||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
|
|
@ -228,7 +229,7 @@ export default {
|
|||
configSource() {
|
||||
switch (this.realSource) {
|
||||
case 'server-side':
|
||||
return useServerSideStorageStore().prefsStorage
|
||||
return useServerSideStorageStore().mergedConfig
|
||||
case 'profile':
|
||||
return this.$store.state.profileConfig
|
||||
case 'admin':
|
||||
|
|
@ -243,36 +244,10 @@ export default {
|
|||
}
|
||||
switch (this.realSource) {
|
||||
case 'server-side': {
|
||||
return (path, value, operator) => {
|
||||
const folder = path.split('.')[0]
|
||||
if (folder === 'collections' || folder === 'objectCollections') {
|
||||
switch (operator) {
|
||||
case 'add':
|
||||
useServerSideStorageStore().addCollectionPreference({
|
||||
path,
|
||||
value,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
break
|
||||
case 'remove':
|
||||
useServerSideStorageStore().removeCollectionPreference({
|
||||
path,
|
||||
value,
|
||||
})
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
break
|
||||
default:
|
||||
console.error(
|
||||
`Unknown server-side collection operator ${operator}, ignoring`,
|
||||
)
|
||||
break
|
||||
}
|
||||
} else if (folder === 'simple') {
|
||||
useServerSideStorageStore().setPreference({ path, value })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
} else {
|
||||
console.error(`Unknown server-side folder ${folder}, ignoring`)
|
||||
}
|
||||
return (originalPath, value, operator) => {
|
||||
const path = `simple.${originalPath}`
|
||||
useServerSideStorageStore().setPreference({ path, value })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
}
|
||||
}
|
||||
case 'profile':
|
||||
|
|
@ -299,10 +274,7 @@ export default {
|
|||
case 'profile':
|
||||
return {}
|
||||
case 'server-side':
|
||||
return get(
|
||||
this.$store.getters.defaultConfig,
|
||||
this.path.split(/\./g).slice(1),
|
||||
)
|
||||
return get(useInstanceStore().prefsStorage, this.path)
|
||||
default:
|
||||
return get(this.$store.getters.defaultConfig, this.path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.alwaysShowSubjectInput"
|
||||
path="alwaysShowSubjectInput"
|
||||
>
|
||||
{{ $t('settings.subject_input_always_show') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.minimalScopesMode"
|
||||
path="minimalScopesMode"
|
||||
>
|
||||
{{ $t('settings.minimal_scopes_mode') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -22,14 +22,14 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.hidePostStats"
|
||||
path="hidePostStats"
|
||||
>
|
||||
{{ $t('settings.hide_post_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="simple.hideUserStats"
|
||||
path="hideUserStats"
|
||||
source="server-side"
|
||||
>
|
||||
{{ $t('settings.hide_user_stats') }}
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.hideBotIndication"
|
||||
path="hideBotIndication"
|
||||
>
|
||||
{{ $t('settings.hide_actor_type_indication') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.hideScrobbles"
|
||||
path="hideScrobbles"
|
||||
>
|
||||
{{ $t('settings.hide_scrobbles') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
<UnitSetting
|
||||
key="hideScrobblesAfter"
|
||||
source="server-side"
|
||||
path="simple.hideScrobblesAfter"
|
||||
path="hideScrobblesAfter"
|
||||
:units="['m', 'h', 'd']"
|
||||
unit-set="time"
|
||||
>
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
<li>
|
||||
<IntegerSetting
|
||||
source="server-side"
|
||||
path="simple.maxThumbnails"
|
||||
path="maxThumbnails"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.max_thumbnails') }}
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.hideAttachments"
|
||||
path="hideAttachments"
|
||||
>
|
||||
{{ $t('settings.hide_attachments_in_tl') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.hideAttachmentsInConv"
|
||||
path="hideAttachmentsInConv"
|
||||
>
|
||||
{{ $t('settings.hide_attachments_in_convo') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.userCardHidePersonalMarks"
|
||||
path="userCardHidePersonalMarks"
|
||||
>
|
||||
{{ $t('settings.user_card_hide_personal_marks') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
<li v-if="instanceShoutboxPresent">
|
||||
<BooleanSetting
|
||||
source="server-side"
|
||||
path="simple.hideShoutbox"
|
||||
path="hideShoutbox"
|
||||
>
|
||||
{{ $t('settings.hide_shoutbox') }}
|
||||
</BooleanSetting>
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export default {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
const currentIndex = this.$store.state.instance.themesIndex
|
||||
const currentIndex = this.$store.state.instance.instanceThemesIndex
|
||||
|
||||
let promise
|
||||
if (currentIndex) {
|
||||
|
|
@ -134,8 +134,8 @@ export default {
|
|||
promise = useInterfaceStore().fetchThemesIndex()
|
||||
}
|
||||
|
||||
promise.then((themesIndex) => {
|
||||
Object.values(themesIndex).forEach((themeFunc) => {
|
||||
promise.then((instanceThemesIndex) => {
|
||||
Object.values(instanceThemesIndex).forEach((themeFunc) => {
|
||||
themeFunc().then(
|
||||
(themeData) => themeData && this.availableStyles.push(themeData),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -532,10 +532,9 @@ const Status = {
|
|||
...mapState(useServerSideStorageStore, {
|
||||
muteFilters: (store) => store.prefsStorage.simple.muteFilters,
|
||||
hideBotIndicatior: (store) => store.prefsStorage.simple.hideBotIndicator,
|
||||
hidePostStats: (store) => store.prefsStorage.simple.hidePostStats,
|
||||
hideScrobbles: (store) => store.prefsStorage.simple.hideScrobbles,
|
||||
hideScrobblesAfter: (store) =>
|
||||
store.prefsStorage.simple.hideScrobblesAfter,
|
||||
hidePostStats: (store) => store.mergedConfig.hidePostStats,
|
||||
hideScrobbles: (store) => store.mergedConfig.hideScrobbles,
|
||||
hideScrobblesAfter: (store) => store.mergedConfig.hideScrobblesAfter,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export const instanceDefaultConfig = {
|
|||
sidebarRight: false,
|
||||
scopeCopy: true,
|
||||
subjectLineBehavior: 'email',
|
||||
alwaysShowSubjectInput: true,
|
||||
alwaysShowSubjectInput: false,
|
||||
postContentType: 'text/plain',
|
||||
minimalScopesMode: false,
|
||||
|
||||
|
|
|
|||
387
src/stores/instance.js
Normal file
387
src/stores/instance.js
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
import { get, set } from 'lodash'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { ensureFinalFallback } from '../i18n/languages.js'
|
||||
import { instanceDefaultProperties } from '../modules/config.js'
|
||||
import {
|
||||
instanceDefaultConfig,
|
||||
staticOrApiConfigDefault,
|
||||
} from '../modules/default_config_state.js'
|
||||
import apiService from '../services/api/api.service.js'
|
||||
|
||||
import { annotationsLoader } from 'virtual:pleroma-fe/emoji-annotations'
|
||||
|
||||
const SORTED_EMOJI_GROUP_IDS = [
|
||||
'smileys-and-emotion',
|
||||
'people-and-body',
|
||||
'animals-and-nature',
|
||||
'food-and-drink',
|
||||
'travel-and-places',
|
||||
'activities',
|
||||
'objects',
|
||||
'symbols',
|
||||
'flags',
|
||||
]
|
||||
|
||||
const REGIONAL_INDICATORS = (() => {
|
||||
const start = 0x1f1e6
|
||||
const end = 0x1f1ff
|
||||
const A = 'A'.codePointAt(0)
|
||||
const res = new Array(end - start + 1)
|
||||
for (let i = start; i <= end; ++i) {
|
||||
const letter = String.fromCodePoint(A + i - start)
|
||||
res[i - start] = {
|
||||
replacement: String.fromCodePoint(i),
|
||||
imageUrl: false,
|
||||
displayText: 'regional_indicator_' + letter,
|
||||
displayTextI18n: {
|
||||
key: 'emoji.regional_indicator',
|
||||
args: { letter },
|
||||
},
|
||||
}
|
||||
}
|
||||
return res
|
||||
})()
|
||||
|
||||
const REMOTE_INTERACTION_URL = '/main/ostatus'
|
||||
|
||||
const defaultState = {
|
||||
// Stuff from apiConfig
|
||||
name: 'Pleroma FE',
|
||||
registrationOpen: true,
|
||||
server: 'http://localhost:4040/',
|
||||
textlimit: 5000,
|
||||
bannerlimit: null,
|
||||
avatarlimit: null,
|
||||
backgroundlimit: null,
|
||||
uploadlimit: null,
|
||||
fieldsLimits: null,
|
||||
private: false,
|
||||
federating: true,
|
||||
federationPolicy: null,
|
||||
themesIndex: null,
|
||||
stylesIndex: null,
|
||||
palettesIndex: null,
|
||||
themeData: null, // used for theme editor v2
|
||||
vapidPublicKey: null,
|
||||
|
||||
// Stuff from static/config.json
|
||||
loginMethod: 'password',
|
||||
disableUpdateNotification: false,
|
||||
|
||||
// Instance-wide configurations that should not be changed by individual users
|
||||
instanceIdentity: {
|
||||
...staticOrApiConfigDefault,
|
||||
},
|
||||
|
||||
// Instance admins can override default settings for the whole instance
|
||||
prefsStorage: {
|
||||
...instanceDefaultConfig,
|
||||
},
|
||||
|
||||
// Custom emoji from server
|
||||
customEmoji: [],
|
||||
customEmojiFetched: false,
|
||||
|
||||
// Unicode emoji from bundle
|
||||
emoji: {},
|
||||
emojiFetched: false,
|
||||
unicodeEmojiAnnotations: {},
|
||||
|
||||
// Known domains list for user's domain-muting
|
||||
knownDomains: [],
|
||||
|
||||
// Moderation stuff
|
||||
staffAccounts: [],
|
||||
accountActivationRequired: null,
|
||||
accountApprovalRequired: null,
|
||||
birthdayRequired: false,
|
||||
birthdayMinAge: 0,
|
||||
restrictedNicknames: [],
|
||||
|
||||
// Feature-set, apparently, not everything here is reported...
|
||||
featureSet: {
|
||||
postFormats: [],
|
||||
mailerEnabled: false,
|
||||
safeDM: true,
|
||||
shoutAvailable: false,
|
||||
pleromaExtensionsAvailable: true,
|
||||
pleromaChatMessagesAvailable: false,
|
||||
pleromaCustomEmojiReactionsAvailable: false,
|
||||
pleromaBookmarkFoldersAvailable: false,
|
||||
pleromaPublicFavouritesAvailable: true,
|
||||
statusNotificationTypeAvailable: true,
|
||||
gopherAvailable: false,
|
||||
editingAvailable: false,
|
||||
mediaProxyAvailable: false,
|
||||
suggestionsEnabled: false,
|
||||
suggestionsWeb: '',
|
||||
quotingAvailable: false,
|
||||
groupActorAvailable: false,
|
||||
blockExpiration: false,
|
||||
tagPolicyAvailable: false,
|
||||
pollsAvailable: false,
|
||||
localBubbleInstances: [], // Akkoma
|
||||
},
|
||||
|
||||
// Html stuff
|
||||
instanceSpecificPanelContent: '',
|
||||
tos: '',
|
||||
|
||||
// Version Information
|
||||
backendVersion: '',
|
||||
backendRepository: '',
|
||||
frontendVersion: '',
|
||||
|
||||
pollsAvailable: false,
|
||||
pollLimits: {
|
||||
max_options: 4,
|
||||
max_option_chars: 255,
|
||||
min_expiration: 60,
|
||||
max_expiration: 60 * 60 * 24,
|
||||
},
|
||||
}
|
||||
|
||||
const loadAnnotations = (lang) => {
|
||||
return annotationsLoader[lang]().then((k) => k.default)
|
||||
}
|
||||
|
||||
const injectAnnotations = (emoji, annotations) => {
|
||||
const availableLangs = Object.keys(annotations)
|
||||
|
||||
return {
|
||||
...emoji,
|
||||
annotations: availableLangs.reduce((acc, cur) => {
|
||||
acc[cur] = annotations[cur][emoji.replacement]
|
||||
return acc
|
||||
}, {}),
|
||||
}
|
||||
}
|
||||
|
||||
const injectRegionalIndicators = (groups) => {
|
||||
groups.symbols.push(...REGIONAL_INDICATORS)
|
||||
return groups
|
||||
}
|
||||
|
||||
export const useInstanceStore = defineStore('instance', {
|
||||
state: () => ({ ...defaultState }),
|
||||
getters: {
|
||||
instanceDefaultConfig(state) {
|
||||
return instanceDefaultProperties
|
||||
.map((key) => [key, state[key]])
|
||||
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
||||
},
|
||||
groupedCustomEmojis(state) {
|
||||
const packsOf = (emoji) => {
|
||||
const packs = emoji.tags
|
||||
.filter((k) => k.startsWith('pack:'))
|
||||
.map((k) => {
|
||||
const packName = k.slice(5) // remove 'pack:' prefix
|
||||
return {
|
||||
id: `custom-${packName}`,
|
||||
text: packName,
|
||||
}
|
||||
})
|
||||
|
||||
if (!packs.length) {
|
||||
return [
|
||||
{
|
||||
id: 'unpacked',
|
||||
},
|
||||
]
|
||||
} else {
|
||||
return packs
|
||||
}
|
||||
}
|
||||
|
||||
return this.customEmoji.reduce((res, emoji) => {
|
||||
packsOf(emoji).forEach(({ id: packId, text: packName }) => {
|
||||
if (!res[packId]) {
|
||||
res[packId] = {
|
||||
id: packId,
|
||||
text: packName,
|
||||
image: emoji.imageUrl,
|
||||
emojis: [],
|
||||
}
|
||||
}
|
||||
res[packId].emojis.push(emoji)
|
||||
})
|
||||
return res
|
||||
}, {})
|
||||
},
|
||||
standardEmojiList(state) {
|
||||
return SORTED_EMOJI_GROUP_IDS.map((groupId) =>
|
||||
(this.emoji[groupId] || []).map((k) =>
|
||||
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
||||
),
|
||||
).reduce((a, b) => a.concat(b), [])
|
||||
},
|
||||
standardEmojiGroupList(state) {
|
||||
return SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
|
||||
id: groupId,
|
||||
emojis: (this.emoji[groupId] || []).map((k) =>
|
||||
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
||||
),
|
||||
}))
|
||||
},
|
||||
instanceDomain(state) {
|
||||
return new URL(this.server).hostname
|
||||
},
|
||||
remoteInteractionLink(state) {
|
||||
const server = this.server.endsWith('/')
|
||||
? this.server.slice(0, -1)
|
||||
: this.server
|
||||
const link = server + REMOTE_INTERACTION_URL
|
||||
|
||||
return ({ statusId, nickname }) => {
|
||||
if (statusId) {
|
||||
return `${link}?status_id=${statusId}`
|
||||
} else {
|
||||
return `${link}?nickname=${nickname}`
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
set({ path, value }) {
|
||||
if (get(defaultState, path) === undefined)
|
||||
console.error(`Unknown instance option ${path}, value: ${value}`)
|
||||
set(this, path, value)
|
||||
switch (name) {
|
||||
case 'name':
|
||||
useInterfaceStore().setPageTitle()
|
||||
break
|
||||
case 'shoutAvailable':
|
||||
if (value) {
|
||||
window.vuex.dispatch('initializeSocket')
|
||||
}
|
||||
break
|
||||
}
|
||||
},
|
||||
async getStaticEmoji() {
|
||||
try {
|
||||
// See build/emojis_plugin for more details
|
||||
const values = (await import('/src/assets/emoji.json')).default
|
||||
|
||||
const emoji = Object.keys(values).reduce((res, groupId) => {
|
||||
res[groupId] = values[groupId].map((e) => ({
|
||||
displayText: e.slug,
|
||||
imageUrl: false,
|
||||
replacement: e.emoji,
|
||||
}))
|
||||
return res
|
||||
}, {})
|
||||
this.emoji = injectRegionalIndicators(emoji)
|
||||
} catch (e) {
|
||||
console.warn("Can't load static emoji\n", e)
|
||||
}
|
||||
},
|
||||
|
||||
loadUnicodeEmojiData(language) {
|
||||
const langList = ensureFinalFallback(language)
|
||||
|
||||
return Promise.all(
|
||||
langList.map(async (lang) => {
|
||||
if (!this.unicodeEmojiAnnotations[lang]) {
|
||||
try {
|
||||
const annotations = await loadAnnotations(lang)
|
||||
this.unicodeEmojiAnnotations[lang] = annotations
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Error loading unicode emoji annotations for ${lang}: `,
|
||||
e,
|
||||
)
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
},
|
||||
|
||||
async getCustomEmoji() {
|
||||
try {
|
||||
let res = await window.fetch('/api/v1/pleroma/emoji')
|
||||
if (!res.ok) {
|
||||
res = await window.fetch('/api/pleroma/emoji.json')
|
||||
}
|
||||
if (res.ok) {
|
||||
const result = await res.json()
|
||||
const values = Array.isArray(result)
|
||||
? Object.assign({}, ...result)
|
||||
: result
|
||||
const caseInsensitiveStrCmp = (a, b) => {
|
||||
const la = a.toLowerCase()
|
||||
const lb = b.toLowerCase()
|
||||
return la > lb ? 1 : la < lb ? -1 : 0
|
||||
}
|
||||
const noPackLast = (a, b) => {
|
||||
const aNull = a === ''
|
||||
const bNull = b === ''
|
||||
if (aNull === bNull) {
|
||||
return 0
|
||||
} else if (aNull && !bNull) {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
const byPackThenByName = (a, b) => {
|
||||
const packOf = (emoji) =>
|
||||
(emoji.tags.filter((k) => k.startsWith('pack:'))[0] || '').slice(
|
||||
5,
|
||||
)
|
||||
const packOfA = packOf(a)
|
||||
const packOfB = packOf(b)
|
||||
return (
|
||||
noPackLast(packOfA, packOfB) ||
|
||||
caseInsensitiveStrCmp(packOfA, packOfB) ||
|
||||
caseInsensitiveStrCmp(a.displayText, b.displayText)
|
||||
)
|
||||
}
|
||||
|
||||
const emoji = Object.entries(values)
|
||||
.map(([key, value]) => {
|
||||
const imageUrl = value.image_url
|
||||
return {
|
||||
displayText: key,
|
||||
imageUrl: imageUrl ? this.server + imageUrl : value,
|
||||
tags: imageUrl
|
||||
? value.tags.sort((a, b) => (a > b ? 1 : 0))
|
||||
: ['utf'],
|
||||
replacement: `:${key}: `,
|
||||
}
|
||||
// Technically could use tags but those are kinda useless right now,
|
||||
// should have been "pack" field, that would be more useful
|
||||
})
|
||||
.sort(byPackThenByName)
|
||||
this.customEmoji = emoji
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Can't load custom emojis\n", e)
|
||||
}
|
||||
},
|
||||
fetchEmoji() {
|
||||
if (!this.customEmojiFetched) {
|
||||
this.customEmojiFetched = true
|
||||
window.vuex.dispatch('getCustomEmoji')
|
||||
}
|
||||
if (!this.emojiFetched) {
|
||||
this.emojiFetched = true
|
||||
window.vuex.dispatch('getStaticEmoji')
|
||||
}
|
||||
},
|
||||
|
||||
async getKnownDomains() {
|
||||
try {
|
||||
this.knownDomains = await apiService.fetchKnownDomains({
|
||||
credentials: window.vuex.state.users.currentUser.credentials,
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn("Can't load known domains\n", e)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -20,8 +20,9 @@ import {
|
|||
defaultState as configDefaultState,
|
||||
instanceDefaultConfig,
|
||||
} from 'src/modules/default_config_state'
|
||||
import { useInstanceStore } from 'src/stores/instance'
|
||||
|
||||
export const VERSION = 1
|
||||
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
|
||||
|
|
@ -45,11 +46,7 @@ export const defaultState = {
|
|||
dontShowUpdateNotifs: false,
|
||||
collapseNav: false,
|
||||
muteFilters: {},
|
||||
...{
|
||||
// reverting all the undefined to their initial values
|
||||
...configDefaultState,
|
||||
...instanceDefaultConfig,
|
||||
},
|
||||
...configDefaultState,
|
||||
},
|
||||
collections: {
|
||||
pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'],
|
||||
|
|
@ -377,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
|
||||
|
|
@ -401,8 +398,8 @@ export const _doMigrations = (cache) => {
|
|||
return window._PLEROMA_HOTPATCH.reverseMigrations.call(
|
||||
{},
|
||||
'serverSideStorage',
|
||||
{ from: cache._version, to: VERSION },
|
||||
cache,
|
||||
{ from: data._version, to: VERSION },
|
||||
data,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -580,7 +577,7 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
cache = null
|
||||
}
|
||||
|
||||
cache = _doMigrations(cache)
|
||||
cache = _doMigrations(cache, live)
|
||||
|
||||
let { recent, stale, needUpload } = _getRecentData(cache, live)
|
||||
|
||||
|
|
@ -649,6 +646,12 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
|||
})
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
mergedConfig: (state) => ({
|
||||
...useInstanceStore().prefsStorage,
|
||||
...state.prefsStorage.simple,
|
||||
}),
|
||||
},
|
||||
persist: {
|
||||
afterLoad(state) {
|
||||
return state
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue