Commit 21989c67 authored by HJ's avatar HJ 🐼

Merge branch 'settings-refactor' into 'develop'

Settings refactor

See merge request !960
parents 42f428d9 b66564a3
......@@ -45,7 +45,7 @@ export default {
}),
created () {
// Load the locale from the storage
this.$i18n.locale = this.$store.state.config.interfaceLanguage
this.$i18n.locale = this.$store.getters.mergedConfig.interfaceLanguage
window.addEventListener('resize', this.updateMobileState)
},
destroyed () {
......@@ -93,7 +93,7 @@ export default {
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
!this.$store.state.config.hideISP &&
!this.$store.getters.mergedConfig.hideISP &&
this.$store.state.instance.instanceSpecificPanelContent
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
......
......@@ -16,8 +16,8 @@ const Attachment = {
data () {
return {
nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage,
hideNsfwLocal: this.$store.state.config.hideNsfw,
preloadImage: this.$store.state.config.preloadImage,
hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw,
preloadImage: this.$store.getters.mergedConfig.preloadImage,
loading: false,
img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'),
modalOpen: false,
......@@ -58,7 +58,7 @@ const Attachment = {
}
},
openModal (event) {
const modalTypes = this.$store.state.config.playVideosInModal
const modalTypes = this.$store.getters.mergedConfig.playVideosInModal
? ['image', 'video']
: ['image']
if (fileTypeService.fileMatchesSomeType(modalTypes, this.attachment) ||
......@@ -71,7 +71,7 @@ const Attachment = {
}
},
toggleHidden (event) {
if (this.$store.state.config.useOneClickNsfw && !this.showHidden) {
if (this.$store.getters.mergedConfig.useOneClickNsfw && !this.showHidden) {
this.openModal(event)
return
}
......
<template>
<label class="checkbox">
<label
class="checkbox"
:class="{ disabled, indeterminate }"
>
<input
type="checkbox"
:disabled="disabled"
:checked="checked"
:indeterminate.prop="indeterminate"
@change="$emit('change', $event.target.checked)"
>
<i class="checkbox-indicator" />
<span v-if="!!$slots.default"><slot /></span>
<span
class="label"
v-if="!!$slots.default"
>
<slot />
</span>
</label>
</template>
......@@ -17,7 +26,11 @@ export default {
prop: 'checked',
event: 'change'
},
props: ['checked', 'indeterminate']
props: [
'checked',
'indeterminate',
'disabled'
]
}
</script>
......@@ -27,12 +40,16 @@ export default {
.checkbox {
position: relative;
display: inline-block;
padding-left: 1.2em;
min-height: 1.2em;
&-indicator {
position: relative;
padding-left: 1.2em;
}
&-indicator::before {
position: absolute;
left: 0;
right: 0;
top: 0;
display: block;
content: '✔';
......@@ -54,6 +71,17 @@ export default {
box-sizing: border-box;
}
&.disabled {
.checkbox-indicator::before,
.label {
opacity: .5;
}
.label {
color: $fallback--faint;
color: var(--faint, $fallback--faint);
}
}
input[type=checkbox] {
display: none;
......@@ -68,9 +96,6 @@ export default {
color: var(--text, $fallback--text);
}
&:disabled + .checkbox-indicator::before {
opacity: .5;
}
}
& > span {
......
......@@ -99,7 +99,7 @@ const EmojiInput = {
},
computed: {
padEmoji () {
return this.$store.state.config.padEmoji
return this.$store.getters.mergedConfig.padEmoji
},
suggestions () {
const firstchar = this.textAtCaret.charAt(0)
......
import Checkbox from '../checkbox/checkbox.vue'
const filterByKeyword = (list, keyword = '') => {
return list.filter(x => x.displayText.includes(keyword))
......@@ -13,7 +14,6 @@ const EmojiPicker = {
},
data () {
return {
labelKey: String(Math.random() * 100000),
keyword: '',
activeGroup: 'custom',
showingStickers: false,
......@@ -22,7 +22,8 @@ const EmojiPicker = {
}
},
components: {
StickerPicker: () => import('../sticker_picker/sticker_picker.vue')
StickerPicker: () => import('../sticker_picker/sticker_picker.vue'),
Checkbox
},
methods: {
onEmoji (emoji) {
......
......@@ -14,10 +14,6 @@
padding: 7px;
line-height: normal;
}
.keep-open-label {
padding: 0 7px;
display: flex;
}
.heading {
display: flex;
......
......@@ -75,22 +75,10 @@
</span>
</div>
</div>
<div
class="keep-open"
>
<input
:id="labelKey + 'keep-open'"
v-model="keepOpen"
type="checkbox"
>
<label
class="keep-open-label"
:for="labelKey + 'keep-open'"
>
<div class="keep-open-label-text">
<div class="keep-open">
<Checkbox v-model="keepOpen">
{{ $t('emoji.keep_open') }}
</div>
</label>
</Checkbox>
</div>
</div>
<div
......
import { mapGetters } from 'vuex'
const FavoriteButton = {
props: ['status', 'loggedIn'],
data () {
return {
hidePostStatsLocal: typeof this.$store.state.config.hidePostStats === 'undefined'
? this.$store.state.instance.hidePostStats
: this.$store.state.config.hidePostStats,
animated: false
}
},
......@@ -28,7 +27,8 @@ const FavoriteButton = {
'icon-star': this.status.favorited,
'animate-spin': this.animated
}
}
},
...mapGetters(['mergedConfig'])
}
}
......
......@@ -6,7 +6,7 @@
:title="$t('tool_tip.favorite')"
@click.prevent="favorite()"
/>
<span v-if="!hidePostStatsLocal && status.fave_num > 0">{{ status.fave_num }}</span>
<span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
</div>
<div v-else>
<i
......@@ -14,7 +14,7 @@
class="button-icon favorite-button"
:title="$t('tool_tip.favorite')"
/>
<span v-if="!hidePostStatsLocal && status.fave_num > 0">{{ status.fave_num }}</span>
<span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
</div>
</template>
......
......@@ -29,7 +29,7 @@ const Gallery = {
return rows
},
useContainFit () {
return this.$store.state.config.useContainFit
return this.$store.getters.mergedConfig.useContainFit
}
},
methods: {
......
......@@ -40,7 +40,7 @@ export default {
},
language: {
get: function () { return this.$store.state.config.interfaceLanguage },
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
set: function (val) {
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
this.$i18n.locale = val
......
......@@ -63,7 +63,7 @@ const MobileNav = {
this.$refs.notifications.markAsSeen()
},
onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) {
if (this.$store.state.config.autoLoad && scrollTop + clientHeight >= scrollHeight) {
if (this.$store.getters.mergedConfig.autoLoad && scrollTop + clientHeight >= scrollHeight) {
this.$refs.notifications.fetchOlderNotifications()
}
}
......
......@@ -30,7 +30,7 @@ const MobilePostStatusButton = {
return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
},
autohideFloatingPostButton () {
return !!this.$store.state.config.autohideFloatingPostButton
return !!this.$store.getters.mergedConfig.autohideFloatingPostButton
}
},
watch: {
......
......@@ -39,7 +39,7 @@ const Notification = {
return highlightClass(this.notification.from_profile)
},
userStyle () {
const highlight = this.$store.state.config.highlight
const highlight = this.$store.getters.mergedConfig.highlight
const user = this.notification.from_profile
return highlightStyle(highlight[user.screen_name])
},
......
......@@ -7,6 +7,8 @@ import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
import { reject, map, uniqBy } from 'lodash'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
let allAttentions = [...attentions]
......@@ -35,7 +37,8 @@ const PostStatusForm = {
MediaUpload,
EmojiInput,
PollForm,
ScopeSelector
ScopeSelector,
Checkbox
},
mounted () {
this.resize(this.$refs.textarea)
......@@ -50,9 +53,7 @@ const PostStatusForm = {
const preset = this.$route.query.message
let statusText = preset || ''
const scopeCopy = typeof this.$store.state.config.scopeCopy === 'undefined'
? this.$store.state.instance.scopeCopy
: this.$store.state.config.scopeCopy
const { scopeCopy } = this.$store.getters.mergedConfig
if (this.replyTo) {
const currentUser = this.$store.state.users.currentUser
......@@ -63,9 +64,7 @@ const PostStatusForm = {
? this.copyMessageScope
: this.$store.state.users.currentUser.default_scope
const contentType = typeof this.$store.state.config.postContentType === 'undefined'
? this.$store.state.instance.postContentType
: this.$store.state.config.postContentType
const { postContentType: contentType } = this.$store.getters.mergedConfig
return {
dropFiles: [],
......@@ -94,10 +93,7 @@ const PostStatusForm = {
return this.$store.state.users.currentUser.default_scope
},
showAllScopes () {
const minimalScopesMode = typeof this.$store.state.config.minimalScopesMode === 'undefined'
? this.$store.state.instance.minimalScopesMode
: this.$store.state.config.minimalScopesMode
return !minimalScopesMode
return !this.mergedConfig.minimalScopesMode
},
emojiUserSuggestor () {
return suggestor({
......@@ -145,13 +141,7 @@ const PostStatusForm = {
return this.$store.state.instance.minimalScopesMode
},
alwaysShowSubject () {
if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') {
return this.$store.state.config.alwaysShowSubjectInput
} else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') {
return this.$store.state.instance.alwaysShowSubjectInput
} else {
return true
}
return this.mergedConfig.alwaysShowSubjectInput
},
postFormats () {
return this.$store.state.instance.postFormats || []
......@@ -164,13 +154,14 @@ const PostStatusForm = {
this.$store.state.instance.pollLimits.max_options >= 2
},
hideScopeNotice () {
return this.$store.state.config.hideScopeNotice
return this.$store.getters.mergedConfig.hideScopeNotice
},
pollContentError () {
return this.pollFormVisible &&
this.newStatus.poll &&
this.newStatus.poll.error
}
},
...mapGetters(['mergedConfig'])
},
methods: {
postStatus (newStatus) {
......
......@@ -261,12 +261,9 @@
v-if="newStatus.files.length > 0"
class="upload_settings"
>
<input
id="filesSensitive"
v-model="newStatus.nsfw"
type="checkbox"
>
<label for="filesSensitive">{{ $t('post_status.attachments_sensitive') }}</label>
<Checkbox v-model="newStatus.nsfw">
{{ $t('post_status.attachments_sensitive') }}
</Checkbox>
</div>
</form>
</div>
......
import { mapGetters } from 'vuex'
const RetweetButton = {
props: ['status', 'loggedIn', 'visibility'],
data () {
return {
hidePostStatsLocal: typeof this.$store.state.config.hidePostStats === 'undefined'
? this.$store.state.instance.hidePostStats
: this.$store.state.config.hidePostStats,
animated: false
}
},
......@@ -28,7 +27,8 @@ const RetweetButton = {
'retweeted-empty': !this.status.repeated,
'animate-spin': this.animated
}
}
},
...mapGetters(['mergedConfig'])
}
}
......
......@@ -7,7 +7,7 @@
:title="$t('tool_tip.repeat')"
@click.prevent="retweet()"
/>
<span v-if="!hidePostStatsLocal && status.repeat_num > 0">{{ status.repeat_num }}</span>
<span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0">{{ status.repeat_num }}</span>
</template>
<template v-else>
<i
......@@ -23,7 +23,7 @@
class="button-icon icon-retweet"
:title="$t('tool_tip.repeat')"
/>
<span v-if="!hidePostStatsLocal && status.repeat_num > 0">{{ status.repeat_num }}</span>
<span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0">{{ status.repeat_num }}</span>
</div>
</template>
......
......@@ -5,88 +5,22 @@ import TabSwitcher from '../tab_switcher/tab_switcher.js'
import StyleSwitcher from '../style_switcher/style_switcher.vue'
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
import { extractCommit } from '../../services/version/version.service'
import { instanceDefaultProperties, defaultState as configDefaultState } from '../../modules/config.js'
import Checkbox from '../checkbox/checkbox.vue'
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
const pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/'
const multiChoiceProperties = [
'postContentType',
'subjectLineBehavior'
]
const settings = {
data () {
const user = this.$store.state.config
const instance = this.$store.state.instance
return {
hideAttachmentsLocal: user.hideAttachments,
padEmojiLocal: user.padEmoji,
hideAttachmentsInConvLocal: user.hideAttachmentsInConv,
maxThumbnails: user.maxThumbnails,
hideNsfwLocal: user.hideNsfw,
useOneClickNsfw: user.useOneClickNsfw,
hideISPLocal: user.hideISP,
preloadImage: user.preloadImage,
hidePostStatsLocal: typeof user.hidePostStats === 'undefined'
? instance.hidePostStats
: user.hidePostStats,
hidePostStatsDefault: this.$t('settings.values.' + instance.hidePostStats),
hideUserStatsLocal: typeof user.hideUserStats === 'undefined'
? instance.hideUserStats
: user.hideUserStats,
hideUserStatsDefault: this.$t('settings.values.' + instance.hideUserStats),
hideFilteredStatusesLocal: typeof user.hideFilteredStatuses === 'undefined'
? instance.hideFilteredStatuses
: user.hideFilteredStatuses,
hideFilteredStatusesDefault: this.$t('settings.values.' + instance.hideFilteredStatuses),
notificationVisibilityLocal: user.notificationVisibility,
replyVisibilityLocal: user.replyVisibility,
loopVideoLocal: user.loopVideo,
muteWordsString: user.muteWords.join('\n'),
autoLoadLocal: user.autoLoad,
streamingLocal: user.streaming,
pauseOnUnfocusedLocal: user.pauseOnUnfocused,
hoverPreviewLocal: user.hoverPreview,
autohideFloatingPostButtonLocal: user.autohideFloatingPostButton,
hideMutedPostsLocal: typeof user.hideMutedPosts === 'undefined'
? instance.hideMutedPosts
: user.hideMutedPosts,
hideMutedPostsDefault: this.$t('settings.values.' + instance.hideMutedPosts),
collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined'
? instance.collapseMessageWithSubject
: user.collapseMessageWithSubject,
collapseMessageWithSubjectDefault: this.$t('settings.values.' + instance.collapseMessageWithSubject),
subjectLineBehaviorLocal: typeof user.subjectLineBehavior === 'undefined'
? instance.subjectLineBehavior
: user.subjectLineBehavior,
subjectLineBehaviorDefault: instance.subjectLineBehavior,
postContentTypeLocal: typeof user.postContentType === 'undefined'
? instance.postContentType
: user.postContentType,
postContentTypeDefault: instance.postContentType,
alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined'
? instance.alwaysShowSubjectInput
: user.alwaysShowSubjectInput,
alwaysShowSubjectInputDefault: this.$t('settings.values.' + instance.alwaysShowSubjectInput),
scopeCopyLocal: typeof user.scopeCopy === 'undefined'
? instance.scopeCopy
: user.scopeCopy,
scopeCopyDefault: this.$t('settings.values.' + instance.scopeCopy),
minimalScopesModeLocal: typeof user.minimalScopesMode === 'undefined'
? instance.minimalScopesMode
: user.minimalScopesMode,
minimalScopesModeDefault: this.$t('settings.values.' + instance.minimalScopesMode),
stopGifs: user.stopGifs,
webPushNotificationsLocal: user.webPushNotifications,
loopVideoSilentOnlyLocal: user.loopVideosSilentOnly,
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
......@@ -94,8 +28,6 @@ const settings = {
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
// Future spec, still not supported in Nightly 63 as of 08/2018
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
playVideosInModal: user.playVideosInModal,
useContainFit: user.useContainFit,
backendVersion: instance.backendVersion,
frontendVersion: instance.frontendVersion
......@@ -104,7 +36,8 @@ const settings = {
components: {
TabSwitcher,
StyleSwitcher,
InterfaceLanguageSwitcher
InterfaceLanguageSwitcher,
Checkbox
},
computed: {
user () {
......@@ -122,116 +55,56 @@ const settings = {
},
backendVersionLink () {
return pleromaBeCommitUrl + extractCommit(this.backendVersion)
},
// Getting localized values for instance-default properties
...instanceDefaultProperties
.filter(key => multiChoiceProperties.includes(key))
.map(key => [
key + 'DefaultValue',
function () {
return this.$store.getters.instanceDefaultConfig[key]
}
])
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
...instanceDefaultProperties
.filter(key => !multiChoiceProperties.includes(key))
.map(key => [
key + 'LocalizedValue',
function () {
return this.$t('settings.values.' + this.$store.getters.instanceDefaultConfig[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 }), {}),
// Special cases (need to transform values)
muteWordsString: {
get () { return this.$store.getters.mergedConfig.muteWords.join('\n') },
set (value) {
this.$store.dispatch('setOption', {
name: 'muteWords',
value: filter(value.split('\n'), (word) => trim(word).length > 0)
})
}
}
},
// Updating nested properties
watch: {
hideAttachmentsLocal (value) {
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
},
padEmojiLocal (value) {
this.$store.dispatch('setOption', { name: 'padEmoji', value })
},
hideAttachmentsInConvLocal (value) {
this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })
},
hidePostStatsLocal (value) {
this.$store.dispatch('setOption', { name: 'hidePostStats', value })
},
hideUserStatsLocal (value) {
this.$store.dispatch('setOption', { name: 'hideUserStats', value })
},
hideFilteredStatusesLocal (value) {
this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
},
hideNsfwLocal (value) {
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
},
useOneClickNsfw (value) {
this.$store.dispatch('setOption', { name: 'useOneClickNsfw', value })
},
preloadImage (value) {
this.$store.dispatch('setOption', { name: 'preloadImage', value })