Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F84165557
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
70 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js
index 2e6992cb9a..078cc5ffd6 100644
--- a/src/components/settings_modal/helpers/boolean_setting.js
+++ b/src/components/settings_modal/helpers/boolean_setting.js
@@ -1,56 +1,13 @@
-import { get, set } from 'lodash'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import ModifiedIndicator from './modified_indicator.vue'
-import ServerSideIndicator from './server_side_indicator.vue'
+import ProfileSettingIndicator from './profile_setting_indicator.vue'
+import Setting from './setting.js'
+
export default {
components: {
Checkbox,
ModifiedIndicator,
- ServerSideIndicator
+ ProfileSettingIndicator
},
- props: [
- 'path',
- 'disabled',
- 'expert'
- ],
- computed: {
- pathDefault () {
- const [firstSegment, ...rest] = this.path.split('.')
- return [firstSegment + 'DefaultValue', ...rest].join('.')
- },
- state () {
- const value = get(this.$parent, this.path)
- if (value === undefined) {
- return this.defaultState
- } else {
- return value
- }
- },
- defaultState () {
- return get(this.$parent, this.pathDefault)
- },
- isServerSide () {
- return this.path.startsWith('serverSide_')
- },
- isChanged () {
- return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
- },
- matchesExpertLevel () {
- return (this.expert || 0) <= this.$parent.expertLevel
- }
- },
- methods: {
- update (e) {
- const [firstSegment, ...rest] = this.path.split('.')
- set(this.$parent, this.path, e)
- // Updating nested properties does not trigger update on its parent.
- // probably still not as reliable, but works for depth=1 at least
- if (rest.length > 0) {
- set(this.$parent, firstSegment, { ...get(this.$parent, firstSegment) })
- }
- },
- reset () {
- set(this.$parent, this.path, this.defaultState)
- }
- }
+ ...Setting
}
diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index 4114296691..bc014bc92e 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -1,27 +1,27 @@
<template>
<label
v-if="matchesExpertLevel"
class="BooleanSetting"
>
<Checkbox
:model-value="state"
- :disabled="disabled"
+ :disabled="shouldBeDisabled"
@update:modelValue="update"
>
<span
v-if="!!$slots.default"
class="label"
>
<slot />
</span>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
- <ServerSideIndicator :server-side="isServerSide" />
+ <ProfileSettingIndicator :is-profile="isProfileTied" />
</Checkbox>
</label>
</template>
<script src="./boolean_setting.js"></script>
diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js
index 3da559fe22..8aa5f54b52 100644
--- a/src/components/settings_modal/helpers/choice_setting.js
+++ b/src/components/settings_modal/helpers/choice_setting.js
@@ -1,51 +1,20 @@
-import { get, set } from 'lodash'
import Select from 'src/components/select/select.vue'
import ModifiedIndicator from './modified_indicator.vue'
-import ServerSideIndicator from './server_side_indicator.vue'
+import ProfileSettingIndicator from './profile_setting_indicator.vue'
+import Setting from './setting.js'
+
export default {
components: {
Select,
ModifiedIndicator,
- ServerSideIndicator
+ ProfileSettingIndicator
},
- props: [
- 'path',
- 'disabled',
- 'options',
- 'expert'
- ],
- computed: {
- pathDefault () {
- const [firstSegment, ...rest] = this.path.split('.')
- return [firstSegment + 'DefaultValue', ...rest].join('.')
- },
- state () {
- const value = get(this.$parent, this.path)
- if (value === undefined) {
- return this.defaultState
- } else {
- return value
- }
- },
- defaultState () {
- return get(this.$parent, this.pathDefault)
- },
- isServerSide () {
- return this.path.startsWith('serverSide_')
- },
- isChanged () {
- return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
- },
- matchesExpertLevel () {
- return (this.expert || 0) <= this.$parent.expertLevel
- }
- },
- methods: {
- update (e) {
- set(this.$parent, this.path, e)
- },
- reset () {
- set(this.$parent, this.path, this.defaultState)
+ ...Setting,
+ props: {
+ ...Setting.props,
+ options: {
+ type: Array,
+ required: true
}
}
}
diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
index 8fdbb5d34f..4c4cdefec8 100644
--- a/src/components/settings_modal/helpers/choice_setting.vue
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -1,30 +1,30 @@
<template>
<label
v-if="matchesExpertLevel"
class="ChoiceSetting"
>
<slot />
{{ ' ' }}
<Select
:model-value="state"
:disabled="disabled"
@update:modelValue="update"
>
<option
v-for="option in options"
:key="option.key"
:value="option.value"
>
{{ option.label }}
{{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }}
</option>
</Select>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
- <ServerSideIndicator :server-side="isServerSide" />
+ <ProfileSettingIndicator :is-profile="isProfileSetting" />
</label>
</template>
<script src="./choice_setting.js"></script>
diff --git a/src/components/settings_modal/helpers/integer_setting.js b/src/components/settings_modal/helpers/integer_setting.js
index e64d0cee22..0f29f11aab 100644
--- a/src/components/settings_modal/helpers/integer_setting.js
+++ b/src/components/settings_modal/helpers/integer_setting.js
@@ -1,44 +1,15 @@
-import { get, set } from 'lodash'
import ModifiedIndicator from './modified_indicator.vue'
+import Setting from './setting.js'
+
export default {
components: {
ModifiedIndicator
},
- props: {
- path: String,
- disabled: Boolean,
- min: Number,
- expert: [Number, String]
- },
- computed: {
- pathDefault () {
- const [firstSegment, ...rest] = this.path.split('.')
- return [firstSegment + 'DefaultValue', ...rest].join('.')
- },
- state () {
- const value = get(this.$parent, this.path)
- if (value === undefined) {
- return this.defaultState
- } else {
- return value
- }
- },
- defaultState () {
- return get(this.$parent, this.pathDefault)
- },
- isChanged () {
- return this.state !== this.defaultState
- },
- matchesExpertLevel () {
- return (this.expert || 0) <= this.$parent.expertLevel
- }
- },
+ ...Setting,
methods: {
+ ...Setting.methods,
update (e) {
- set(this.$parent, this.path, parseInt(e.target.value))
- },
- reset () {
- set(this.$parent, this.path, this.defaultState)
+ this.configSink(this.path, parseInt(e.target.value))
}
}
}
diff --git a/src/components/settings_modal/helpers/server_side_indicator.vue b/src/components/settings_modal/helpers/profile_setting_indicator.vue
similarity index 81%
rename from src/components/settings_modal/helpers/server_side_indicator.vue
rename to src/components/settings_modal/helpers/profile_setting_indicator.vue
index bf181959fe..d160781b1a 100644
--- a/src/components/settings_modal/helpers/server_side_indicator.vue
+++ b/src/components/settings_modal/helpers/profile_setting_indicator.vue
@@ -1,51 +1,51 @@
<template>
<span
- v-if="serverSide"
- class="ServerSideIndicator"
+ v-if="isProfile"
+ class="ProfileSettingIndicator"
>
<Popover
trigger="hover"
>
<template #trigger>
<FAIcon
icon="server"
:aria-label="$t('settings.setting_server_side')"
/>
</template>
<template #content>
- <div class="serverside-tooltip">
+ <div class="profilesetting-tooltip">
{{ $t('settings.setting_server_side') }}
</div>
</template>
</Popover>
</span>
</template>
<script>
import Popover from 'src/components/popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faServer } from '@fortawesome/free-solid-svg-icons'
library.add(
faServer
)
export default {
components: { Popover },
- props: ['serverSide']
+ props: ['isProfile']
}
</script>
<style lang="scss">
-.ServerSideIndicator {
+.ProfileSettingIndicator {
display: inline-block;
position: relative;
}
-.serverside-tooltip {
+.profilesetting-tooltip {
margin: 0.5em 1em;
min-width: 10em;
text-align: center;
}
</style>
diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js
new file mode 100644
index 0000000000..dc37675c47
--- /dev/null
+++ b/src/components/settings_modal/helpers/setting.js
@@ -0,0 +1,84 @@
+import { get, set } from 'lodash'
+export default {
+ props: {
+ path: {
+ type: String,
+ required: true
+ },
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ parentPath: {
+ type: String
+ },
+ parentInvert: {
+ type: Boolean,
+ default: false
+ },
+ expert: {
+ type: [Number, String],
+ default: 0
+ },
+ source: {
+ type: String,
+ default: 'default'
+ }
+ },
+ computed: {
+ state () {
+ const value = get(this.configSource, this.path)
+ if (value === undefined) {
+ return this.defaultState
+ } else {
+ return value
+ }
+ },
+ shouldBeDisabled () {
+ const parentValue = this.parentPath !== undefined ? get(this.configSource, this.parentPath) : null
+ return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
+ },
+ configSource () {
+ switch (this.source) {
+ case 'profile':
+ return this.$store.state.profileConfig
+ default:
+ return this.$store.getters.mergedConfig
+ }
+ },
+ configSink () {
+ switch (this.source) {
+ case 'profile':
+ return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
+ default:
+ return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
+ }
+ },
+ defaultState () {
+ switch (this.source) {
+ case 'profile':
+ return {}
+ default:
+ return get(this.$store.getters.defaultConfig, this.path)
+ }
+ },
+ isProfileTied () {
+ return this.source === 'profile'
+ },
+ isChanged () {
+ return !this.source === 'default' && this.state !== this.defaultState
+ },
+ matchesExpertLevel () {
+ return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
+ }
+ },
+ methods: {
+ update (e) {
+ console.log('U', this.path, e)
+ this.configSink(this.path, e)
+ },
+ reset () {
+ set(this.$store.getters.mergedConfig, this.path, this.defaultState)
+ }
+ }
+}
diff --git a/src/components/settings_modal/helpers/shared_computed_object.js b/src/components/settings_modal/helpers/shared_computed_object.js
index 12431dcaf0..6b22174d87 100644
--- a/src/components/settings_modal/helpers/shared_computed_object.js
+++ b/src/components/settings_modal/helpers/shared_computed_object.js
@@ -1,53 +1,35 @@
import { defaultState as configDefaultState } from 'src/modules/config.js'
-import { defaultState as serverSideConfigDefaultState } from 'src/modules/serverSideConfig.js'
const SharedComputedObject = () => ({
user () {
return this.$store.state.users.currentUser
},
- // Getting values for default properties
- ...Object.keys(configDefaultState)
- .map(key => [
- key + 'DefaultValue',
- function () {
- return this.$store.getters.defaultConfig[key]
- }
- ])
- .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
// Generating computed values for vuex properties
...Object.keys(configDefaultState)
.map(key => [key, {
get () { return this.$store.getters.mergedConfig[key] },
set (value) {
this.$store.dispatch('setOption', { name: key, value })
}
}])
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
- ...Object.keys(serverSideConfigDefaultState)
- .map(key => ['serverSide_' + key, {
- get () { return this.$store.state.serverSideConfig[key] },
- set (value) {
- this.$store.dispatch('setServerSideOption', { name: key, value })
- }
- }])
- .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
// Special cases (need to transform values or perform actions first)
useStreamingApi: {
get () { return this.$store.getters.mergedConfig.useStreamingApi },
set (value) {
const promise = value
? this.$store.dispatch('enableMastoSockets')
: this.$store.dispatch('disableMastoSockets')
promise.then(() => {
this.$store.dispatch('setOption', { name: 'useStreamingApi', value })
}).catch((e) => {
console.error('Failed starting MastoAPI Streaming socket', e)
this.$store.dispatch('disableMastoSockets')
this.$store.dispatch('setOption', { name: 'useStreamingApi', value: false })
})
}
}
})
export default SharedComputedObject
diff --git a/src/components/settings_modal/helpers/size_setting.js b/src/components/settings_modal/helpers/size_setting.js
index 58697412df..4a0f7e48a2 100644
--- a/src/components/settings_modal/helpers/size_setting.js
+++ b/src/components/settings_modal/helpers/size_setting.js
@@ -1,67 +1,41 @@
-import { get, set } from 'lodash'
import ModifiedIndicator from './modified_indicator.vue'
import Select from 'src/components/select/select.vue'
+import Setting from './setting.js'
export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
export const defaultVerticalUnits = ['px', 'rem', 'vh']
export default {
components: {
ModifiedIndicator,
Select
},
+ ...Setting,
props: {
- path: String,
- disabled: Boolean,
+ ...Setting.props,
min: Number,
units: {
- type: [String],
+ type: Array,
default: () => allCssUnits
- },
- expert: [Number, String]
+ }
},
computed: {
- pathDefault () {
- const [firstSegment, ...rest] = this.path.split('.')
- return [firstSegment + 'DefaultValue', ...rest].join('.')
- },
+ ...Setting.computed,
stateUnit () {
- return (this.state || '').replace(/\d+/, '')
+ return this.state.replace(/\d+/, '')
},
stateValue () {
- return (this.state || '').replace(/\D+/, '')
- },
- state () {
- const value = get(this.$parent, this.path)
- if (value === undefined) {
- return this.defaultState
- } else {
- return value
- }
- },
- defaultState () {
- return get(this.$parent, this.pathDefault)
- },
- isChanged () {
- return this.state !== this.defaultState
- },
- matchesExpertLevel () {
- return (this.expert || 0) <= this.$parent.expertLevel
+ return this.state.replace(/\D+/, '')
}
},
methods: {
- update (e) {
- set(this.$parent, this.path, e)
- },
- reset () {
- set(this.$parent, this.path, this.defaultState)
- },
+ ...Setting.methods,
updateValue (e) {
- set(this.$parent, this.path, parseInt(e.target.value) + this.stateUnit)
+ this.configSink(this.path, parseInt(e.target.value) + this.stateUnit)
},
updateUnit (e) {
- set(this.$parent, this.path, this.stateValue + e.target.value)
+ this.configSink(this.path, this.stateValue + e.target.value)
}
}
}
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 97046ff0c9..46c6bc5cd0 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -1,111 +1,111 @@
<template>
<div :label="$t('settings.filtering')">
<div class="setting-item">
<h2>{{ $t('settings.posts') }}</h2>
<ul class="setting-list">
<li>
<BooleanSetting path="hideFilteredStatuses">
{{ $t('settings.hide_filtered_statuses') }}
</BooleanSetting>
- <ul
- class="setting-list suboptions"
- :class="[{disabled: !streaming}]"
- >
+ <ul class="setting-list suboptions">
<li>
<BooleanSetting
- :disabled="hideFilteredStatuses"
+ parentPath="hideFilteredStatuses"
+ :parentInvert="true"
path="hideWordFilteredPosts"
>
{{ $t('settings.hide_wordfiltered_statuses') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
v-if="user"
- :disabled="hideFilteredStatuses"
+ parentPath="hideFilteredStatuses"
+ :parentInvert="true"
path="hideMutedThreads"
>
{{ $t('settings.hide_muted_threads') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
v-if="user"
- :disabled="hideFilteredStatuses"
+ parentPath="hideFilteredStatuses"
+ :parentInvert="true"
path="hideMutedPosts"
>
{{ $t('settings.hide_muted_posts') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<BooleanSetting path="muteBotStatuses">
{{ $t('settings.mute_bot_posts') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="hidePostStats">
{{ $t('settings.hide_post_stats') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="hideBotIndication">
{{ $t('settings.hide_bot_indication') }}
</BooleanSetting>
</li>
<ChoiceSetting
v-if="user"
id="replyVisibility"
path="replyVisibility"
:options="replyVisibilityOptions"
>
{{ $t('settings.replies_in_timeline') }}
</ChoiceSetting>
<li>
<h3>{{ $t('settings.wordfilter') }}</h3>
<textarea
id="muteWords"
v-model="muteWordsString"
class="resize-height"
/>
<div>{{ $t('settings.filtering_explanation') }}</div>
</li>
<h3>{{ $t('settings.attachments') }}</h3>
<li>
<IntegerSetting
path="maxThumbnails"
expert="1"
:min="0"
>
{{ $t('settings.max_thumbnails') }}
</IntegerSetting>
</li>
<li>
<BooleanSetting path="hideAttachments">
{{ $t('settings.hide_attachments_in_tl') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="hideAttachmentsInConv">
{{ $t('settings.hide_attachments_in_convo') }}
</BooleanSetting>
</li>
</ul>
</div>
<div
v-if="expertLevel > 0"
class="setting-item"
>
<h2>{{ $t('settings.user_profiles') }}</h2>
<ul class="setting-list">
<li>
<BooleanSetting path="hideUserStats">
{{ $t('settings.hide_user_stats') }}
</BooleanSetting>
</li>
</ul>
</div>
</div>
</template>
<script src="./filtering_tab.js"></script>
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index ea24d6adb8..e8f7016e78 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -1,116 +1,116 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
-import ServerSideIndicator from '../helpers/server_side_indicator.vue'
+import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
const GeneralTab = {
data () {
return {
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
})),
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.conversation_display_${mode}`)
})),
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.conversation_other_replies_button_${mode}`)
})),
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.mention_link_display_${mode}`)
})),
thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.third_column_mode_${mode}`)
})),
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
})),
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
// Chrome-likes
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
// Future spec, still not supported in Nightly 63 as of 08/2018
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks')
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
SizeSetting,
InterfaceLanguageSwitcher,
ScopeSelector,
- ServerSideIndicator
+ ProfileSettingIndicator
},
computed: {
horizontalUnits () {
return defaultHorizontalUnits
},
postFormats () {
return this.$store.state.instance.postFormats || []
},
postContentOptions () {
return this.postFormats.map(format => ({
key: format,
value: format,
label: this.$t(`post_status.content_type["${format}"]`)
}))
},
columns () {
const mode = this.$store.getters.mergedConfig.thirdColumnMode
const notif = mode === 'none' ? [] : ['notifs']
if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') {
return [...notif, 'content', 'sidebar']
} else {
return ['sidebar', 'content', ...notif]
}
},
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
instanceWallpaperUsed () {
return this.$store.state.instance.background &&
!this.$store.state.users.currentUser.background_image
},
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
language: {
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
set: function (val) {
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
}
},
...SharedComputedObject()
},
methods: {
changeDefaultScope (value) {
- this.$store.dispatch('setServerSideOption', { name: 'defaultScope', value })
+ this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
}
}
}
export default GeneralTab
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 65248ac1ba..0eb3d06c17 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -1,531 +1,526 @@
<template>
<div :label="$t('settings.general')">
<div class="setting-item">
<h2>{{ $t('settings.interface') }}</h2>
<ul class="setting-list">
<li>
<interface-language-switcher
:prompt-text="$t('settings.interfaceLanguage')"
:language="language"
:set-language="val => language = val"
/>
</li>
<li v-if="instanceSpecificPanelPresent">
<BooleanSetting path="hideISP">
{{ $t('settings.hide_isp') }}
</BooleanSetting>
</li>
<li v-if="instanceWallpaperUsed">
<BooleanSetting path="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="stopGifs">
{{ $t('settings.stop_gifs') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="streaming">
{{ $t('settings.streaming') }}
</BooleanSetting>
- <ul
- class="setting-list suboptions"
- :class="[{disabled: !streaming}]"
- >
+ <ul class="setting-list suboptions">
<li>
<BooleanSetting
path="pauseOnUnfocused"
- :disabled="!streaming"
+ parentPath="streaming"
>
{{ $t('settings.pause_on_unfocused') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<BooleanSetting
path="useStreamingApi"
expert="1"
>
{{ $t('settings.useStreamingApi') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="virtualScrolling"
expert="1"
>
{{ $t('settings.virtual_scrolling') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
id="userPopoverAvatarAction"
path="userPopoverAvatarAction"
:options="userPopoverAvatarActionOptions"
expert="1"
>
{{ $t('settings.user_popover_avatar_action') }}
</ChoiceSetting>
</li>
<li>
<BooleanSetting
path="userPopoverOverlay"
expert="1"
>
{{ $t('settings.user_popover_avatar_overlay') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="alwaysShowNewPostButton"
expert="1"
>
{{ $t('settings.always_show_post_button') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="autohideFloatingPostButton"
expert="1"
>
{{ $t('settings.autohide_floating_post_button') }}
</BooleanSetting>
</li>
<li v-if="instanceShoutboxPresent">
<BooleanSetting
path="hideShoutbox"
expert="1"
>
{{ $t('settings.hide_shoutbox') }}
</BooleanSetting>
</li>
<li>
<h3>{{ $t('settings.columns') }}</h3>
</li>
<li>
<BooleanSetting path="disableStickyHeaders">
{{ $t('settings.disable_sticky_headers') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="showScrollbars">
{{ $t('settings.show_scrollbars') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="sidebarRight">
{{ $t('settings.right_sidebar') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="navbarColumnStretch">
{{ $t('settings.navbar_column_stretch') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
v-if="user"
id="thirdColumnMode"
path="thirdColumnMode"
:options="thirdColumnModeOptions"
>
{{ $t('settings.third_column_mode') }}
</ChoiceSetting>
</li>
<li v-if="expertLevel > 0">
{{ $t('settings.column_sizes') }}
<div class="column-settings">
<SizeSetting
v-for="column in columns"
:key="column"
:path="column + 'ColumnWidth'"
:units="horizontalUnits"
expert="1"
>
{{ $t('settings.column_sizes_' + column) }}
</SizeSetting>
</div>
</li>
<li class="select-multiple">
<span class="label">{{ $t('settings.confirm_dialogs') }}</span>
<ul class="option-list">
<li>
<BooleanSetting path="modalOnRepeat">
{{ $t('settings.confirm_dialogs_repeat') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnUnfollow">
{{ $t('settings.confirm_dialogs_unfollow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnBlock">
{{ $t('settings.confirm_dialogs_block') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnMute">
{{ $t('settings.confirm_dialogs_mute') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnDelete">
{{ $t('settings.confirm_dialogs_delete') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnLogout">
{{ $t('settings.confirm_dialogs_logout') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnApproveFollow">
{{ $t('settings.confirm_dialogs_approve_follow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnDenyFollow">
{{ $t('settings.confirm_dialogs_deny_follow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnRemoveUserFromFollowers">
{{ $t('settings.confirm_dialogs_remove_follower') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('settings.post_look_feel') }}</h2>
<ul class="setting-list">
<li>
<ChoiceSetting
id="conversationDisplay"
path="conversationDisplay"
:options="conversationDisplayOptions"
>
{{ $t('settings.conversation_display') }}
</ChoiceSetting>
</li>
<ul
v-if="conversationDisplay !== 'linear'"
class="setting-list suboptions"
>
<li>
<BooleanSetting path="conversationTreeAdvanced">
{{ $t('settings.tree_advanced') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="conversationTreeFadeAncestors"
:expert="1"
>
{{ $t('settings.tree_fade_ancestors') }}
</BooleanSetting>
</li>
<li>
<IntegerSetting
path="maxDepthInThread"
:min="3"
:expert="1"
>
{{ $t('settings.max_depth_in_thread') }}
</IntegerSetting>
</li>
<li>
<ChoiceSetting
id="conversationOtherRepliesButton"
path="conversationOtherRepliesButton"
:options="conversationOtherRepliesButtonOptions"
:expert="1"
>
{{ $t('settings.conversation_other_replies_button') }}
</ChoiceSetting>
</li>
</ul>
<li>
<BooleanSetting path="collapseMessageWithSubject">
{{ $t('settings.collapse_subject') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="emojiReactionsOnTimeline"
expert="1"
>
{{ $t('settings.emoji_reactions_on_timeline') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
v-if="user"
- path="serverSide_stripRichContent"
+ source="profile" path="stripRichContent"
expert="1"
>
{{ $t('settings.no_rich_text_description') }}
</BooleanSetting>
</li>
<h3>{{ $t('settings.attachments') }}</h3>
<li>
<BooleanSetting
path="useContainFit"
expert="1"
>
{{ $t('settings.use_contain_fit') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="hideNsfw">
{{ $t('settings.nsfw_clickthrough') }}
</BooleanSetting>
</li>
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path="preloadImage"
expert="1"
- :disabled="!hideNsfw"
+ parentPath="hideNsfw"
>
{{ $t('settings.preload_images') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="useOneClickNsfw"
expert="1"
- :disabled="!hideNsfw"
+ parentPath="hideNsfw"
>
{{ $t('settings.use_one_click_nsfw') }}
</BooleanSetting>
</li>
</ul>
<li>
<BooleanSetting
path="loopVideo"
expert="1"
>
{{ $t('settings.loop_video') }}
</BooleanSetting>
- <ul
- class="setting-list suboptions"
- :class="[{disabled: !streaming}]"
- >
+ <ul class="setting-list suboptions">
<li>
<BooleanSetting
path="loopVideoSilentOnly"
expert="1"
- :disabled="!loopVideo || !loopSilentAvailable"
+ parentPath="loopVideo"
+ :disabled="!loopSilentAvailable"
>
{{ $t('settings.loop_video_silent_only') }}
</BooleanSetting>
<div
v-if="!loopSilentAvailable"
class="unavailable"
>
<FAIcon icon="globe" />! {{ $t('settings.limited_availability') }}
</div>
</li>
</ul>
</li>
<li>
<BooleanSetting
path="playVideosInModal"
expert="1"
>
{{ $t('settings.play_videos_in_modal') }}
</BooleanSetting>
</li>
<h3>{{ $t('settings.mention_links') }}</h3>
<li>
<ChoiceSetting
id="mentionLinkDisplay"
path="mentionLinkDisplay"
:options="mentionLinkDisplayOptions"
>
{{ $t('settings.mention_link_display') }}
</ChoiceSetting>
</li>
<li>
<BooleanSetting
path="mentionLinkShowTooltip"
expert="1"
>
{{ $t('settings.mention_link_use_tooltip') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="useAtIcon"
expert="1"
>
{{ $t('settings.use_at_icon') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="mentionLinkShowAvatar">
{{ $t('settings.mention_link_show_avatar') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="mentionLinkFadeDomain"
expert="1"
>
{{ $t('settings.mention_link_fade_domain') }}
</BooleanSetting>
</li>
<li v-if="user">
<BooleanSetting
path="mentionLinkBoldenYou"
expert="1"
>
{{ $t('settings.mention_link_bolden_you') }}
</BooleanSetting>
</li>
<h3 v-if="expertLevel > 0">
{{ $t('settings.fun') }}
</h3>
<li>
<BooleanSetting
path="greentext"
expert="1"
>
{{ $t('settings.greentext') }}
</BooleanSetting>
</li>
<li v-if="user">
<BooleanSetting
path="mentionLinkShowYous"
expert="1"
>
{{ $t('settings.show_yous') }}
</BooleanSetting>
</li>
</ul>
</div>
<div
v-if="user"
class="setting-item"
>
<h2>{{ $t('settings.composing') }}</h2>
<ul class="setting-list">
<li>
<label for="default-vis">
- {{ $t('settings.default_vis') }} <ServerSideIndicator :server-side="true" />
+ {{ $t('settings.default_vis') }} <ProfileSettingIndicator :is-profile="true" />
<ScopeSelector
class="scope-selector"
:show-all="true"
- :user-default="serverSide_defaultScope"
- :initial-scope="serverSide_defaultScope"
+ :user-default="$store.state.profileConfig.defaultScope"
+ :initial-scope="$store.state.profileConfig.defaultScope"
:on-scope-change="changeDefaultScope"
/>
</label>
</li>
<li>
- <!-- <BooleanSetting path="serverSide_defaultNSFW"> -->
+ <!-- <BooleanSetting source="profile" path="defaultNSFW"> -->
<BooleanSetting path="sensitiveByDefault">
{{ $t('settings.sensitive_by_default') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="scopeCopy"
expert="1"
>
{{ $t('settings.scope_copy') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="alwaysShowSubjectInput"
expert="1"
>
{{ $t('settings.subject_input_always_show') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
id="subjectLineBehavior"
path="subjectLineBehavior"
:options="subjectLineOptions"
expert="1"
>
{{ $t('settings.subject_line_behavior') }}
</ChoiceSetting>
</li>
<li v-if="postFormats.length > 0">
<ChoiceSetting
id="postContentType"
path="postContentType"
:options="postContentOptions"
>
{{ $t('settings.post_status_content_type') }}
</ChoiceSetting>
</li>
<li>
<BooleanSetting
path="minimalScopesMode"
expert="1"
>
{{ $t('settings.minimal_scopes_mode') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="alwaysShowNewPostButton"
expert="1"
>
{{ $t('settings.always_show_post_button') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="autohideFloatingPostButton"
expert="1"
>
{{ $t('settings.autohide_floating_post_button') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="padEmoji"
expert="1"
>
{{ $t('settings.pad_emoji') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="autocompleteSelect"
expert="1"
>
{{ $t('settings.autocomplete_select_first') }}
</BooleanSetting>
</li>
</ul>
</div>
</div>
</template>
<script src="./general_tab.js"></script>
<style lang="scss">
.column-settings {
display: flex;
justify-content: space-evenly;
flex-wrap: wrap;
}
.column-settings .size-label {
display: block;
margin-bottom: 0.5em;
margin-top: 0.5em;
}
</style>
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index dd3806ed1a..ea09537270 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -1,86 +1,86 @@
<template>
<div :label="$t('settings.notifications')">
<div class="setting-item">
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
<ul class="setting-list">
<li>
- <BooleanSetting path="serverSide_blockNotificationsFromStrangers">
+ <BooleanSetting source="profile" path="blockNotificationsFromStrangers">
{{ $t('settings.notification_setting_block_from_strangers') }}
</BooleanSetting>
</li>
<li class="select-multiple">
<span class="label">{{ $t('settings.notification_visibility') }}</span>
<ul class="option-list">
<li>
<BooleanSetting path="notificationVisibility.likes">
{{ $t('settings.notification_visibility_likes') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationVisibility.repeats">
{{ $t('settings.notification_visibility_repeats') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationVisibility.follows">
{{ $t('settings.notification_visibility_follows') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationVisibility.mentions">
{{ $t('settings.notification_visibility_mentions') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationVisibility.moves">
{{ $t('settings.notification_visibility_moves') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationVisibility.emojiReactions">
{{ $t('settings.notification_visibility_emoji_reactions') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationVisibility.polls">
{{ $t('settings.notification_visibility_polls') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</div>
<div
v-if="expertLevel > 0"
class="setting-item"
>
<h2>{{ $t('settings.notification_setting_privacy') }}</h2>
<ul class="setting-list">
<li>
<BooleanSetting
path="webPushNotifications"
expert="1"
>
{{ $t('settings.enable_web_push_notifications') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
- path="serverSide_webPushHideContents"
+ source="profile" path="webPushHideContents"
expert="1"
>
{{ $t('settings.notification_setting_hide_notification_contents') }}
</BooleanSetting>
</li>
</ul>
</div>
<div class="setting-item">
<p>{{ $t('settings.notification_mutes') }}</p>
<p>{{ $t('settings.notification_blocks') }}</p>
</div>
</div>
</template>
<script src="./notifications_tab.js"></script>
<!-- <style lang="scss" src="./profile.scss"></style> -->
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index 6a5b478a1f..7465299031 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -1,318 +1,314 @@
<template>
<div class="profile-tab">
<div class="setting-item">
<h2>{{ $t('settings.name_bio') }}</h2>
<p>{{ $t('settings.name') }}</p>
<EmojiInput
v-model="newName"
enable-emoji-picker
:suggest="emojiSuggestor"
>
<template #default="inputProps">
<input
id="username"
v-model="newName"
class="name-changer"
v-bind="propsToNative(inputProps)"
>
</template>
</EmojiInput>
<p>{{ $t('settings.bio') }}</p>
<EmojiInput
v-model="newBio"
enable-emoji-picker
:suggest="emojiUserSuggestor"
>
<template #default="inputProps">
<textarea
v-model="newBio"
class="bio resize-height"
v-bind="propsToNative(inputProps)"
/>
</template>
</EmojiInput>
<p v-if="role === 'admin' || role === 'moderator'">
<Checkbox v-model="showRole">
<template v-if="role === 'admin'">
{{ $t('settings.show_admin_badge') }}
</template>
<template v-if="role === 'moderator'">
{{ $t('settings.show_moderator_badge') }}
</template>
</Checkbox>
</p>
<div>
<p>{{ $t('settings.birthday.label') }}</p>
<input
id="birthday"
v-model="newBirthday"
type="date"
class="birthday-input"
>
<Checkbox v-model="showBirthday">
{{ $t('settings.birthday.show_birthday') }}
</Checkbox>
</div>
<div v-if="maxFields > 0">
<p>{{ $t('settings.profile_fields.label') }}</p>
<div
v-for="(_, i) in newFields"
:key="i"
class="profile-fields"
>
<EmojiInput
v-model="newFields[i].name"
enable-emoji-picker
hide-emoji-button
:suggest="userSuggestor"
>
<template #default="inputProps">
<input
v-model="newFields[i].name"
:placeholder="$t('settings.profile_fields.name')"
v-bind="propsToNative(inputProps)"
>
</template>
</EmojiInput>
<EmojiInput
v-model="newFields[i].value"
enable-emoji-picker
hide-emoji-button
:suggest="userSuggestor"
>
<template #default="inputProps">
<input
v-model="newFields[i].value"
:placeholder="$t('settings.profile_fields.value')"
v-bind="propsToNative(inputProps)"
>
</template>
</EmojiInput>
<button
class="delete-field button-unstyled -hover-highlight"
@click="deleteField(i)"
>
<!-- TODO something is wrong with v-show here -->
<FAIcon
v-if="newFields.length > 1"
icon="times"
/>
</button>
</div>
<button
v-if="newFields.length < maxFields"
class="add-field faint button-unstyled -hover-highlight"
@click="addField"
>
<FAIcon icon="plus" />
{{ $t("settings.profile_fields.add_field") }}
</button>
</div>
<p>
<Checkbox v-model="bot">
{{ $t('settings.bot') }}
</Checkbox>
</p>
<p>
<interface-language-switcher
:prompt-text="$t('settings.email_language')"
:language="emailLanguage"
:set-language="val => emailLanguage = val"
/>
</p>
<button
:disabled="newName && newName.length === 0"
class="btn button-default"
@click="updateProfile"
>
{{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
<h2>{{ $t('settings.avatar') }}</h2>
<p class="visibility-notice">
{{ $t('settings.avatar_size_instruction') }}
</p>
<div class="current-avatar-container">
<img
:src="user.profile_image_url_original"
class="current-avatar"
>
<button
v-if="!isDefaultAvatar && pickAvatarBtnVisible"
:title="$t('settings.reset_avatar')"
class="button-unstyled reset-button"
@click="resetAvatar"
>
<FAIcon
icon="times"
type="button"
/>
</button>
</div>
<p>{{ $t('settings.set_new_avatar') }}</p>
<button
v-show="pickAvatarBtnVisible"
id="pick-avatar"
class="button-default btn"
type="button"
>
{{ $t('settings.upload_a_photo') }}
</button>
<image-cropper
trigger="#pick-avatar"
:submit-handler="submitAvatar"
@open="pickAvatarBtnVisible=false"
@close="pickAvatarBtnVisible=true"
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.profile_banner') }}</h2>
<div class="banner-background-preview">
<img :src="user.cover_photo">
<button
v-if="!isDefaultBanner"
class="button-unstyled reset-button"
:title="$t('settings.reset_profile_banner')"
@click="resetBanner"
>
<FAIcon
icon="times"
type="button"
/>
</button>
</div>
<p>{{ $t('settings.set_new_profile_banner') }}</p>
<img
v-if="bannerPreview"
class="banner-background-preview"
:src="bannerPreview"
>
<div>
<input
type="file"
@change="uploadFile('banner', $event)"
>
</div>
<FAIcon
v-if="bannerUploading"
class="uploading"
spin
icon="circle-notch"
/>
<button
v-else-if="bannerPreview"
class="btn button-default"
@click="submitBanner(banner)"
>
{{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
<h2>{{ $t('settings.profile_background') }}</h2>
<div class="banner-background-preview">
<img :src="user.background_image">
<button
v-if="!isDefaultBackground"
class="button-unstyled reset-button"
:title="$t('settings.reset_profile_background')"
@click="resetBackground"
>
<FAIcon
icon="times"
type="button"
/>
</button>
</div>
<p>{{ $t('settings.set_new_profile_background') }}</p>
<img
v-if="backgroundPreview"
class="banner-background-preview"
:src="backgroundPreview"
>
<div>
<input
type="file"
@change="uploadFile('background', $event)"
>
</div>
<FAIcon
v-if="backgroundUploading"
class="uploading"
spin
icon="circle-notch"
/>
<button
v-else-if="backgroundPreview"
class="btn button-default"
@click="submitBackground(background)"
>
{{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
<h2>{{ $t('settings.account_privacy') }}</h2>
<ul class="setting-list">
<li>
- <BooleanSetting path="serverSide_locked">
+ <BooleanSetting source="profile" path="locked">
{{ $t('settings.lock_account_description') }}
</BooleanSetting>
</li>
<li>
- <BooleanSetting path="serverSide_discoverable">
+ <BooleanSetting source="profile" path="discoverable">
{{ $t('settings.discoverable') }}
</BooleanSetting>
</li>
<li>
- <BooleanSetting path="serverSide_allowFollowingMove">
+ <BooleanSetting source="profile" path="allowFollowingMove">
{{ $t('settings.allow_following_move') }}
</BooleanSetting>
</li>
<li>
- <BooleanSetting path="serverSide_hideFavorites">
+ <BooleanSetting source="profile" path="hideFavorites">
{{ $t('settings.hide_favorites_description') }}
</BooleanSetting>
</li>
<li>
- <BooleanSetting path="serverSide_hideFollowers">
+ <BooleanSetting source="profile" path="hideFollowers">
{{ $t('settings.hide_followers_description') }}
</BooleanSetting>
- <ul
- class="setting-list suboptions"
- :class="[{disabled: !serverSide_hideFollowers}]"
- >
+ <ul class="setting-list suboptions">
<li>
<BooleanSetting
- path="serverSide_hideFollowersCount"
- :disabled="!serverSide_hideFollowers"
+ source="profile"
+ path="hideFollowersCount"
+ parentPath="hideFollowers"
>
{{ $t('settings.hide_followers_count_description') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
- <BooleanSetting path="serverSide_hideFollows">
+ <BooleanSetting source="profile" path="hideFollows">
{{ $t('settings.hide_follows_description') }}
</BooleanSetting>
- <ul
- class="setting-list suboptions"
- :class="[{disabled: !serverSide_hideFollows}]"
- >
+ <ul class="setting-list suboptions">
<li>
<BooleanSetting
- path="serverSide_hideFollowsCount"
- :disabled="!serverSide_hideFollows"
+ source="profile"
+ path="hideFollowsCount"
+ parentPath="hideFollows"
>
{{ $t('settings.hide_follows_count_description') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</div>
</div>
</template>
<script src="./profile_tab.js"></script>
<style lang="scss" src="./profile_tab.scss"></style>
diff --git a/src/main.js b/src/main.js
index d3e60a0fa1..fd712113fc 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,112 +1,112 @@
import { createStore } from 'vuex'
import 'custom-event-polyfill'
import './lib/event_target_polyfill.js'
import interfaceModule from './modules/interface.js'
import instanceModule from './modules/instance.js'
import statusesModule from './modules/statuses.js'
import listsModule from './modules/lists.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
-import serverSideConfigModule from './modules/serverSideConfig.js'
+import profileConfigModule from './modules/profileConfig.js'
import serverSideStorageModule from './modules/serverSideStorage.js'
import shoutModule from './modules/shout.js'
import oauthModule from './modules/oauth.js'
import authFlowModule from './modules/auth_flow.js'
import mediaViewerModule from './modules/media_viewer.js'
import oauthTokensModule from './modules/oauth_tokens.js'
import reportsModule from './modules/reports.js'
import pollsModule from './modules/polls.js'
import postStatusModule from './modules/postStatus.js'
import editStatusModule from './modules/editStatus.js'
import statusHistoryModule from './modules/statusHistory.js'
import chatsModule from './modules/chats.js'
import announcementsModule from './modules/announcements.js'
import { createI18n } from 'vue-i18n'
import createPersistedState from './lib/persisted_state.js'
import pushNotifications from './lib/push_notifications_plugin.js'
import messages from './i18n/messages.js'
import afterStoreSetup from './boot/after_store.js'
const currentLocale = (window.navigator.language || 'en').split('-')[0]
const i18n = createI18n({
// By default, use the browser locale, we will update it if neccessary
locale: 'en',
fallbackLocale: 'en',
messages: messages.default
})
messages.setLanguage(i18n, currentLocale)
const persistedStateOptions = {
paths: [
'serverSideStorage.cache',
'config',
'users.lastLoginName',
'oauth'
]
};
(async () => {
let storageError = false
const plugins = [pushNotifications]
try {
const persistedState = await createPersistedState(persistedStateOptions)
plugins.push(persistedState)
} catch (e) {
console.error(e)
storageError = true
}
const store = createStore({
modules: {
i18n: {
getters: {
i18n: () => i18n.global
}
},
interface: interfaceModule,
instance: instanceModule,
// TODO refactor users/statuses modules, they depend on each other
users: usersModule,
statuses: statusesModule,
lists: listsModule,
api: apiModule,
config: configModule,
- serverSideConfig: serverSideConfigModule,
+ profileConfig: profileConfigModule,
serverSideStorage: serverSideStorageModule,
shout: shoutModule,
oauth: oauthModule,
authFlow: authFlowModule,
mediaViewer: mediaViewerModule,
oauthTokens: oauthTokensModule,
reports: reportsModule,
polls: pollsModule,
postStatus: postStatusModule,
editStatus: editStatusModule,
statusHistory: statusHistoryModule,
chats: chatsModule,
announcements: announcementsModule
},
plugins,
strict: false // Socket modifies itself, let's ignore this for now.
// strict: process.env.NODE_ENV !== 'production'
})
if (storageError) {
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
}
afterStoreSetup({ store, i18n })
})()
// These are inlined by webpack's DefinePlugin
/* eslint-disable */
window.___pleromafe_mode = process.env
window.___pleromafe_commit_hash = COMMIT_HASH
window.___pleromafe_dev_overrides = DEV_OVERRIDES
diff --git a/src/modules/config.js b/src/modules/config.js
index 3d9cf59140..6ab59d6d3d 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -1,210 +1,211 @@
import Cookies from 'js-cookie'
import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
import messages from '../i18n/messages'
+import { set } from 'lodash'
import localeService from '../services/locale/locale.service.js'
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
const browserLocale = (window.navigator.language || 'en').split('-')[0]
/* TODO this is a bit messy.
* We need to declare settings with their types and also deal with
* instance-default settings in some way, hopefully try to avoid copy-pasta
* in general.
*/
export const multiChoiceProperties = [
'postContentType',
'subjectLineBehavior',
'conversationDisplay', // tree | linear
'conversationOtherRepliesButton', // below | inside
'mentionLinkDisplay', // short | full_for_remote | full
'userPopoverAvatarAction' // close | zoom | open
]
export const defaultState = {
expertLevel: 0, // used to track which settings to show and hide
colors: {},
theme: undefined,
customTheme: undefined,
customThemeSource: undefined,
hideISP: false,
hideInstanceWallpaper: false,
hideShoutbox: false,
// bad name: actually hides posts of muted USERS
hideMutedPosts: undefined, // instance default
hideMutedThreads: undefined, // instance default
hideWordFilteredPosts: undefined, // instance default
muteBotStatuses: undefined, // instance default
collapseMessageWithSubject: undefined, // instance default
padEmoji: true,
hideAttachments: false,
hideAttachmentsInConv: false,
maxThumbnails: 16,
hideNsfw: true,
preloadImage: true,
loopVideo: true,
loopVideoSilentOnly: true,
streaming: false,
emojiReactionsOnTimeline: true,
alwaysShowNewPostButton: false,
autohideFloatingPostButton: false,
pauseOnUnfocused: true,
stopGifs: true,
replyVisibility: 'all',
thirdColumnMode: 'notifications',
notificationVisibility: {
follows: true,
mentions: true,
likes: true,
repeats: true,
moves: true,
emojiReactions: true,
followRequest: true,
reports: true,
chatMention: true,
polls: true
},
webPushNotifications: false,
muteWords: [],
highlight: {},
interfaceLanguage: browserLocale,
hideScopeNotice: false,
useStreamingApi: false,
sidebarRight: undefined, // instance default
scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default
alwaysShowSubjectInput: undefined, // instance default
postContentType: undefined, // instance default
minimalScopesMode: undefined, // instance default
// This hides statuses filtered via a word filter
hideFilteredStatuses: undefined, // instance default
modalOnRepeat: undefined, // instance default
modalOnUnfollow: undefined, // instance default
modalOnBlock: undefined, // instance default
modalOnMute: undefined, // instance default
modalOnDelete: undefined, // instance default
modalOnLogout: undefined, // instance default
modalOnApproveFollow: undefined, // instance default
modalOnDenyFollow: undefined, // instance default
modalOnRemoveUserFromFollowers: undefined, // instance default
playVideosInModal: false,
useOneClickNsfw: false,
useContainFit: true,
disableStickyHeaders: false,
showScrollbars: false,
userPopoverAvatarAction: 'open',
userPopoverOverlay: false,
sidebarColumnWidth: '25rem',
contentColumnWidth: '45rem',
notifsColumnWidth: '25rem',
navbarColumnStretch: false,
greentext: undefined, // instance default
useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default
mentionLinkShowTooltip: undefined, // instance default
mentionLinkShowAvatar: undefined, // instance default
mentionLinkFadeDomain: undefined, // instance default
mentionLinkShowYous: undefined, // instance default
mentionLinkBoldenYou: undefined, // instance default
hidePostStats: undefined, // instance default
hideBotIndication: undefined, // instance default
hideUserStats: undefined, // instance default
virtualScrolling: undefined, // instance default
sensitiveByDefault: undefined, // instance default
conversationDisplay: undefined, // instance default
conversationTreeAdvanced: undefined, // instance default
conversationOtherRepliesButton: undefined, // instance default
conversationTreeFadeAncestors: undefined, // instance default
maxDepthInThread: undefined, // instance default
autocompleteSelect: undefined // instance default
}
// caching the instance default properties
export const instanceDefaultProperties = Object.entries(defaultState)
.filter(([key, value]) => value === undefined)
.map(([key, value]) => key)
const config = {
state: { ...defaultState },
getters: {
defaultConfig (state, getters, rootState, rootGetters) {
const { instance } = rootState
return {
...defaultState,
...Object.fromEntries(
instanceDefaultProperties.map(key => [key, instance[key]])
)
}
},
mergedConfig (state, getters, rootState, rootGetters) {
const { defaultConfig } = rootGetters
return {
...defaultConfig,
// Do not override with undefined
...Object.fromEntries(Object.entries(state).filter(([k, v]) => v !== undefined))
}
}
},
mutations: {
setOption (state, { name, value }) {
- state[name] = value
+ set(state, name, value)
},
setHighlight (state, { user, color, type }) {
const data = this.state.config.highlight[user]
if (color || type) {
state.highlight[user] = { color: color || data.color, type: type || data.type }
} else {
delete state.highlight[user]
}
}
},
actions: {
loadSettings ({ dispatch }, data) {
const knownKeys = new Set(Object.keys(defaultState))
const presentKeys = new Set(Object.keys(data))
const intersection = new Set()
for (const elem of presentKeys) {
if (knownKeys.has(elem)) {
intersection.add(elem)
}
}
intersection.forEach(
name => dispatch('setOption', { name, value: data[name] })
)
},
setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', { user, color, type })
},
setOption ({ commit, dispatch, state }, { name, value }) {
commit('setOption', { name, value })
switch (name) {
case 'theme':
setPreset(value)
break
case 'sidebarColumnWidth':
case 'contentColumnWidth':
case 'notifsColumnWidth':
applyConfig(state)
break
case 'customTheme':
case 'customThemeSource':
applyTheme(value)
break
case 'interfaceLanguage':
messages.setLanguage(this.getters.i18n, value)
dispatch('loadUnicodeEmojiData', value)
Cookies.set(
BACKEND_LANGUAGE_COOKIE_NAME,
localeService.internalToBackendLocaleMulti(value)
)
break
case 'thirdColumnMode':
dispatch('setLayoutWidth', undefined)
break
}
}
}
}
export default config
diff --git a/src/modules/serverSideConfig.js b/src/modules/profileConfig.js
similarity index 85%
rename from src/modules/serverSideConfig.js
rename to src/modules/profileConfig.js
index 476263bc8b..2cb2014acc 100644
--- a/src/modules/serverSideConfig.js
+++ b/src/modules/profileConfig.js
@@ -1,140 +1,140 @@
import { get, set } from 'lodash'
const defaultApi = ({ rootState, commit }, { path, value }) => {
const params = {}
set(params, path, value)
return rootState
.api
.backendInteractor
.updateProfile({ params })
.then(result => {
commit('addNewUsers', [result])
commit('setCurrentUser', result)
})
}
const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => {
const settings = {}
set(settings, path, value)
return rootState
.api
.backendInteractor
.updateNotificationSettings({ settings })
.then(result => {
if (result.status === 'success') {
- commit('confirmServerSideOption', { name, value })
+ commit('confirmProfileOption', { name, value })
} else {
- commit('confirmServerSideOption', { name, value: oldValue })
+ commit('confirmProfileOption', { name, value: oldValue })
}
})
}
/**
* Map that stores relation between path for reading (from user profile),
* for writing (into API) an what API to use.
*
* Shorthand - instead of { get, set, api? } object it's possible to use string
* in case default api is used and get = set
*
* If no api is specified, defaultApi is used (see above)
*/
export const settingsMap = {
defaultScope: 'source.privacy',
defaultNSFW: 'source.sensitive', // BROKEN: pleroma/pleroma#2837
stripRichContent: {
get: 'source.pleroma.no_rich_text',
set: 'no_rich_text'
},
// Privacy
locked: 'locked',
acceptChatMessages: {
get: 'pleroma.accepts_chat_messages',
set: 'accepts_chat_messages'
},
allowFollowingMove: {
get: 'pleroma.allow_following_move',
set: 'allow_following_move'
},
discoverable: {
get: 'source.pleroma.discoverable',
set: 'discoverable'
},
hideFavorites: {
get: 'pleroma.hide_favorites',
set: 'hide_favorites'
},
hideFollowers: {
get: 'pleroma.hide_followers',
set: 'hide_followers'
},
hideFollows: {
get: 'pleroma.hide_follows',
set: 'hide_follows'
},
hideFollowersCount: {
get: 'pleroma.hide_followers_count',
set: 'hide_followers_count'
},
hideFollowsCount: {
get: 'pleroma.hide_follows_count',
set: 'hide_follows_count'
},
// NotificationSettingsAPIs
webPushHideContents: {
get: 'pleroma.notification_settings.hide_notification_contents',
set: 'hide_notification_contents',
api: notificationsApi
},
blockNotificationsFromStrangers: {
get: 'pleroma.notification_settings.block_from_strangers',
set: 'block_from_strangers',
api: notificationsApi
}
}
export const defaultState = Object.fromEntries(Object.keys(settingsMap).map(key => [key, null]))
-const serverSideConfig = {
+const profileConfig = {
state: { ...defaultState },
mutations: {
- confirmServerSideOption (state, { name, value }) {
+ confirmProfileOption (state, { name, value }) {
set(state, name, value)
},
- wipeServerSideOption (state, { name }) {
+ wipeProfileOption (state, { name }) {
set(state, name, null)
},
- wipeAllServerSideOptions (state) {
+ wipeAllProfileOptions (state) {
Object.keys(settingsMap).forEach(key => {
set(state, key, null)
})
},
// Set the settings based on their path location
setCurrentUser (state, user) {
Object.entries(settingsMap).forEach((map) => {
const [name, value] = map
const { get: path = value } = value
set(state, name, get(user._original, path))
})
}
},
actions: {
- setServerSideOption ({ rootState, state, commit, dispatch }, { name, value }) {
+ setProfileOption ({ rootState, state, commit, dispatch }, { name, value }) {
const oldValue = get(state, name)
const map = settingsMap[name]
if (!map) throw new Error('Invalid server-side setting')
const { set: path = map, api = defaultApi } = map
- commit('wipeServerSideOption', { name })
+ commit('wipeProfileOption', { name })
api({ rootState, commit }, { path, value, oldValue })
.catch((e) => {
console.warn('Error setting server-side option:', e)
- commit('confirmServerSideOption', { name, value: oldValue })
+ commit('confirmProfileOption', { name, value: oldValue })
})
},
logout ({ commit }) {
- commit('wipeAllServerSideOptions')
+ commit('wipeAllProfileOptions')
}
}
}
-export default serverSideConfig
+export default profileConfig
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jun 4, 6:44 PM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1539181
Default Alt Text
(70 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment