Merge branch 'minimal-scopes-mode' into 'develop'

Initial work on deprecating scopeModesEnabled in favor of minimalScopeMode

See merge request pleroma/pleroma-fe!633
This commit is contained in:
HJ 2019-04-01 19:32:37 +00:00
commit 3c2931529e
18 changed files with 158 additions and 33 deletions

10
BREAKING_CHANGES.md Normal file
View file

@ -0,0 +1,10 @@
# v1.0
## Removed features/radically changed behavior
### minimalScopesMode
As of !633, `scopeOptions` is no longer available and instead is changed for `minimalScopesMode` (default: `false`)
Reasoning is that scopeOptions option originally existed mostly as a backwards-compatibility with GNU Social which only had `public` scope available and using scope selector would''t work. Since at some point we dropped GNU Social support, this option was mostly a nuisance (being default `false`'), however some people think scopes are an annoyance to a certain degree and want as less of that feature as possible.
Solution - to only show minimal set among: *Direct*, *User default* and *Scope of post replying to*. This also makes it impossible to reply to a DM with a non-DM post from UI.
*This setting is admin-default, user-configurable. Admin can choose different default for their instance but user can override it.*

View file

@ -41,7 +41,7 @@ FE Build process also leaves current commit hash in global variable `___pleromaf
# Configuration # Configuration
Edit config.json for configuration. scopeOptionsEnabled gives you input fields for CWs and the scope settings. Edit config.json for configuration.
## Options ## Options

View file

@ -95,7 +95,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
copyInstanceOption('redirectRootNoLogin') copyInstanceOption('redirectRootNoLogin')
copyInstanceOption('redirectRootLogin') copyInstanceOption('redirectRootLogin')
copyInstanceOption('showInstanceSpecificPanel') copyInstanceOption('showInstanceSpecificPanel')
copyInstanceOption('scopeOptionsEnabled') copyInstanceOption('minimalScopesMode')
copyInstanceOption('formattingOptionsEnabled') copyInstanceOption('formattingOptionsEnabled')
copyInstanceOption('hideMutedPosts') copyInstanceOption('hideMutedPosts')
copyInstanceOption('collapseMessageWithSubject') copyInstanceOption('collapseMessageWithSubject')

View file

@ -6,7 +6,7 @@ const FeaturesPanel = {
gopher: function () { return this.$store.state.instance.gopherAvailable }, gopher: function () { return this.$store.state.instance.gopherAvailable },
whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled }, whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable }, mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },
scopeOptions: function () { return this.$store.state.instance.scopeOptionsEnabled }, minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode },
textlimit: function () { return this.$store.state.instance.textlimit } textlimit: function () { return this.$store.state.instance.textlimit }
} }
} }

View file

@ -12,7 +12,7 @@
<li v-if="gopher">{{$t('features_panel.gopher')}}</li> <li v-if="gopher">{{$t('features_panel.gopher')}}</li>
<li v-if="whoToFollow">{{$t('features_panel.who_to_follow')}}</li> <li v-if="whoToFollow">{{$t('features_panel.who_to_follow')}}</li>
<li v-if="mediaProxy">{{$t('features_panel.media_proxy')}}</li> <li v-if="mediaProxy">{{$t('features_panel.media_proxy')}}</li>
<li v-if="scopeOptions">{{$t('features_panel.scope_options')}}</li> <li>{{$t('features_panel.scope_options')}}</li>
<li>{{$t('features_panel.text_limit')}} = {{textlimit}}</li> <li>{{$t('features_panel.text_limit')}} = {{textlimit}}</li>
</ul> </ul>
</div> </div>

View file

@ -1,5 +1,6 @@
import statusPoster from '../../services/status_poster/status_poster.service.js' import statusPoster from '../../services/status_poster/status_poster.service.js'
import MediaUpload from '../media_upload/media_upload.vue' import MediaUpload from '../media_upload/media_upload.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue'
import EmojiInput from '../emoji-input/emoji-input.vue' import EmojiInput from '../emoji-input/emoji-input.vue'
import fileTypeService from '../../services/file_type/file_type.service.js' import fileTypeService from '../../services/file_type/file_type.service.js'
import Completion from '../../services/completion/completion.js' import Completion from '../../services/completion/completion.js'
@ -30,6 +31,7 @@ const PostStatusForm = {
], ],
components: { components: {
MediaUpload, MediaUpload,
ScopeSelector,
EmojiInput EmojiInput
}, },
mounted () { mounted () {
@ -80,14 +82,6 @@ const PostStatusForm = {
} }
}, },
computed: { computed: {
vis () {
return {
public: { selected: this.newStatus.visibility === 'public' },
unlisted: { selected: this.newStatus.visibility === 'unlisted' },
private: { selected: this.newStatus.visibility === 'private' },
direct: { selected: this.newStatus.visibility === 'direct' }
}
},
candidates () { candidates () {
const firstchar = this.textAtCaret.charAt(0) const firstchar = this.textAtCaret.charAt(0)
if (firstchar === '@') { if (firstchar === '@') {
@ -135,6 +129,15 @@ const PostStatusForm = {
users () { users () {
return this.$store.state.users.users return this.$store.state.users.users
}, },
userDefaultScope () {
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
},
emoji () { emoji () {
return this.$store.state.instance.emoji || [] return this.$store.state.instance.emoji || []
}, },
@ -159,8 +162,8 @@ const PostStatusForm = {
isOverLengthLimit () { isOverLengthLimit () {
return this.hasStatusLengthLimit && (this.charactersLeft < 0) return this.hasStatusLengthLimit && (this.charactersLeft < 0)
}, },
scopeOptionsEnabled () { minimalScopesMode () {
return this.$store.state.instance.scopeOptionsEnabled return this.$store.state.instance.minimalScopesMode
}, },
alwaysShowSubject () { alwaysShowSubject () {
if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') { if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') {
@ -168,7 +171,7 @@ const PostStatusForm = {
} else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') { } else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') {
return this.$store.state.instance.alwaysShowSubjectInput return this.$store.state.instance.alwaysShowSubjectInput
} else { } else {
return this.$store.state.instance.scopeOptionsEnabled return true
} }
}, },
formattingOptionsEnabled () { formattingOptionsEnabled () {

View file

@ -48,12 +48,12 @@
</label> </label>
</span> </span>
<div v-if="scopeOptionsEnabled"> <scope-selector
<i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct" :title="$t('post_status.scope.direct')"></i> :showAll="showAllScopes"
<i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private" :title="$t('post_status.scope.private')"></i> :userDefault="userDefaultScope"
<i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted" :title="$t('post_status.scope.unlisted')"></i> :originalScope="copyMessageScope"
<i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public" :title="$t('post_status.scope.public')"></i> :initialScope="newStatus.visibility"
</div> :onScopeChange="changeVis"/>
</div> </div>
</div> </div>
<div class="autocomplete-panel" v-if="candidates"> <div class="autocomplete-panel" v-if="candidates">

View file

@ -0,0 +1,54 @@
const ScopeSelector = {
props: [
'showAll',
'userDefault',
'originalScope',
'initialScope',
'onScopeChange'
],
data () {
return {
currentScope: this.initialScope
}
},
computed: {
showNothing () {
return !this.showPublic && !this.showUnlisted && !this.showPrivate && !this.showDirect
},
showPublic () {
return this.originalScope !== 'direct' && this.shouldShow('public')
},
showUnlisted () {
return this.originalScope !== 'direct' && this.shouldShow('unlisted')
},
showPrivate () {
return this.originalScope !== 'direct' && this.shouldShow('private')
},
showDirect () {
return this.shouldShow('direct')
},
css () {
return {
public: {selected: this.currentScope === 'public'},
unlisted: {selected: this.currentScope === 'unlisted'},
private: {selected: this.currentScope === 'private'},
direct: {selected: this.currentScope === 'direct'}
}
}
},
methods: {
shouldShow (scope) {
return this.showAll ||
this.currentScope === scope ||
this.originalScope === scope ||
this.userDefault === scope ||
scope === 'direct'
},
changeVis (scope) {
this.currentScope = scope
this.onScopeChange && this.onScopeChange(scope)
}
}
}
export default ScopeSelector

View file

@ -0,0 +1,30 @@
<template>
<div v-if="!showNothing">
<i class="icon-mail-alt"
:class="css.direct"
:title="$t('post_status.scope.direct')"
v-if="showDirect"
@click="changeVis('direct')">
</i>
<i class="icon-lock"
:class="css.private"
:title="$t('post_status.scope.private')"
v-if="showPrivate"
v-on:click="changeVis('private')">
</i>
<i class="icon-lock-open-alt"
:class="css.unlisted"
:title="$t('post_status.scope.unlisted')"
v-if="showUnlisted"
@click="changeVis('unlisted')">
</i>
<i class="icon-globe"
:class="css.public"
:title="$t('post_status.scope.public')"
v-if="showPublic"
@click="changeVis('public')">
</i>
</div>
</template>
<script src="./scope_selector.js"></script>

View file

@ -70,13 +70,18 @@ const settings = {
alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined' alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined'
? instance.alwaysShowSubjectInput ? instance.alwaysShowSubjectInput
: user.alwaysShowSubjectInput, : user.alwaysShowSubjectInput,
alwaysShowSubjectInputDefault: instance.alwaysShowSubjectInput, alwaysShowSubjectInputDefault: this.$t('settings.values.' + instance.alwaysShowSubjectInput),
scopeCopyLocal: typeof user.scopeCopy === 'undefined' scopeCopyLocal: typeof user.scopeCopy === 'undefined'
? instance.scopeCopy ? instance.scopeCopy
: user.scopeCopy, : user.scopeCopy,
scopeCopyDefault: this.$t('settings.values.' + instance.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, stopGifs: user.stopGifs,
webPushNotificationsLocal: user.webPushNotifications, webPushNotificationsLocal: user.webPushNotifications,
loopVideoSilentOnlyLocal: user.loopVideosSilentOnly, loopVideoSilentOnlyLocal: user.loopVideosSilentOnly,
@ -200,6 +205,9 @@ const settings = {
postContentTypeLocal (value) { postContentTypeLocal (value) {
this.$store.dispatch('setOption', { name: 'postContentType', value }) this.$store.dispatch('setOption', { name: 'postContentType', value })
}, },
minimalScopesModeLocal (value) {
this.$store.dispatch('setOption', { name: 'minimalScopesMode', value })
},
stopGifs (value) { stopGifs (value) {
this.$store.dispatch('setOption', { name: 'stopGifs', value }) this.$store.dispatch('setOption', { name: 'stopGifs', value })
}, },

View file

@ -118,6 +118,12 @@
</label> </label>
</div> </div>
</li> </li>
<li>
<input type="checkbox" id="minimalScopesMode" v-model="minimalScopesModeLocal">
<label for="minimalScopesMode">
{{$t('settings.minimal_scopes_mode')}} {{$t('settings.instance_default', { value: minimalScopesModeDefault })}}
</label>
</li>
</ul> </ul>
</div> </div>

View file

@ -4,6 +4,7 @@ import get from 'lodash/get'
import TabSwitcher from '../tab_switcher/tab_switcher.js' import TabSwitcher from '../tab_switcher/tab_switcher.js'
import ImageCropper from '../image_cropper/image_cropper.vue' import ImageCropper from '../image_cropper/image_cropper.vue'
import StyleSwitcher from '../style_switcher/style_switcher.vue' import StyleSwitcher from '../style_switcher/style_switcher.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue'
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js' import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
import BlockCard from '../block_card/block_card.vue' import BlockCard from '../block_card/block_card.vue'
import MuteCard from '../mute_card/mute_card.vue' import MuteCard from '../mute_card/mute_card.vue'
@ -67,6 +68,7 @@ const UserSettings = {
}, },
components: { components: {
StyleSwitcher, StyleSwitcher,
ScopeSelector,
TabSwitcher, TabSwitcher,
ImageCropper, ImageCropper,
BlockList, BlockList,
@ -80,8 +82,8 @@ const UserSettings = {
pleromaBackend () { pleromaBackend () {
return this.$store.state.instance.pleromaBackend return this.$store.state.instance.pleromaBackend
}, },
scopeOptionsEnabled () { minimalScopesMode () {
return this.$store.state.instance.scopeOptionsEnabled return this.$store.state.instance.minimalScopesMode
}, },
vis () { vis () {
return { return {

View file

@ -38,13 +38,13 @@
<input type="checkbox" v-model="newLocked" id="account-locked"> <input type="checkbox" v-model="newLocked" id="account-locked">
<label for="account-locked">{{$t('settings.lock_account_description')}}</label> <label for="account-locked">{{$t('settings.lock_account_description')}}</label>
</p> </p>
<div v-if="scopeOptionsEnabled"> <div>
<label for="default-vis">{{$t('settings.default_vis')}}</label> <label for="default-vis">{{$t('settings.default_vis')}}</label>
<div id="default-vis" class="visibility-tray"> <div id="default-vis" class="visibility-tray">
<i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct" :title="$t('post_status.scope.direct')" ></i> <scope-selector
<i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private" :title="$t('post_status.scope.private')"></i> :showAll="true"
<i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted" :title="$t('post_status.scope.unlisted')"></i> :userDefault="newDefaultScope"
<i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public" :title="$t('post_status.scope.public')"></i> :onScopeChange="changeVis"/>
</div> </div>
</div> </div>
<p> <p>

View file

@ -217,6 +217,7 @@
"saving_ok": "Settings saved", "saving_ok": "Settings saved",
"security_tab": "Security", "security_tab": "Security",
"scope_copy": "Copy scope when replying (DMs are always copied)", "scope_copy": "Copy scope when replying (DMs are always copied)",
"minimal_scopes_mode": "Minimize post scope selection options",
"set_new_avatar": "Set new avatar", "set_new_avatar": "Set new avatar",
"set_new_profile_background": "Set new profile background", "set_new_profile_background": "Set new profile background",
"set_new_profile_banner": "Set new profile banner", "set_new_profile_banner": "Set new profile banner",

View file

@ -111,6 +111,8 @@
"import_theme": "Загрузить Тему", "import_theme": "Загрузить Тему",
"inputRadius": "Поля ввода", "inputRadius": "Поля ввода",
"checkboxRadius": "Чекбоксы", "checkboxRadius": "Чекбоксы",
"instance_default": "(по умолчанию: {value})",
"instance_default_simple": "(по умолчанию)",
"interface": "Интерфейс", "interface": "Интерфейс",
"interfaceLanguage": "Язык интерфейса", "interfaceLanguage": "Язык интерфейса",
"limited_availability": "Не доступно в вашем браузере", "limited_availability": "Не доступно в вашем браузере",
@ -149,7 +151,11 @@
"reply_visibility_all": "Показывать все ответы", "reply_visibility_all": "Показывать все ответы",
"reply_visibility_following": "Показывать только ответы мне и тех на кого я подписан", "reply_visibility_following": "Показывать только ответы мне и тех на кого я подписан",
"reply_visibility_self": "Показывать только ответы мне", "reply_visibility_self": "Показывать только ответы мне",
"saving_err": "Не удалось сохранить настройки",
"saving_ok": "Сохранено",
"security_tab": "Безопасность", "security_tab": "Безопасность",
"scope_copy": "Копировать видимость поста при ответе (всегда включено для Личных Сообщений)",
"minimal_scopes_mode": "Минимизировать набор опций видимости поста",
"set_new_avatar": "Загрузить новый аватар", "set_new_avatar": "Загрузить новый аватар",
"set_new_profile_background": "Загрузить новый фон профиля", "set_new_profile_background": "Загрузить новый фон профиля",
"set_new_profile_banner": "Загрузить новый баннер профиля", "set_new_profile_banner": "Загрузить новый баннер профиля",
@ -164,6 +170,10 @@
"theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.", "theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.",
"tooltipRadius": "Всплывающие подсказки/уведомления", "tooltipRadius": "Всплывающие подсказки/уведомления",
"user_settings": "Настройки пользователя", "user_settings": "Настройки пользователя",
"values": {
"false": "нет",
"true": "да"
},
"style": { "style": {
"switcher": { "switcher": {
"keep_color": "Оставить цвета", "keep_color": "Оставить цвета",

View file

@ -33,7 +33,8 @@ const defaultState = {
scopeCopy: undefined, // instance default scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default subjectLineBehavior: undefined, // instance default
alwaysShowSubjectInput: undefined, // instance default alwaysShowSubjectInput: undefined, // instance default
postContentType: undefined // instance default postContentType: undefined, // instance default
minimalScopesMode: undefined // instance default
} }
const config = { const config = {

View file

@ -15,7 +15,6 @@ const defaultState = {
redirectRootNoLogin: '/main/all', redirectRootNoLogin: '/main/all',
redirectRootLogin: '/main/friends', redirectRootLogin: '/main/friends',
showInstanceSpecificPanel: false, showInstanceSpecificPanel: false,
scopeOptionsEnabled: true,
formattingOptionsEnabled: false, formattingOptionsEnabled: false,
alwaysShowSubjectInput: true, alwaysShowSubjectInput: true,
hideMutedPosts: false, hideMutedPosts: false,
@ -32,6 +31,7 @@ const defaultState = {
vapidPublicKey: undefined, vapidPublicKey: undefined,
noAttachmentLinks: false, noAttachmentLinks: false,
showFeaturesPanel: true, showFeaturesPanel: true,
minimalScopesMode: false,
// Nasty stuff // Nasty stuff
pleromaBackend: true, pleromaBackend: true,

View file

@ -8,7 +8,6 @@
"redirectRootLogin": "/main/friends", "redirectRootLogin": "/main/friends",
"chatDisabled": false, "chatDisabled": false,
"showInstanceSpecificPanel": false, "showInstanceSpecificPanel": false,
"scopeOptionsEnabled": false,
"formattingOptionsEnabled": false, "formattingOptionsEnabled": false,
"collapseMessageWithSubject": false, "collapseMessageWithSubject": false,
"scopeCopy": true, "scopeCopy": true,
@ -21,5 +20,6 @@
"webPushNotifications": false, "webPushNotifications": false,
"noAttachmentLinks": false, "noAttachmentLinks": false,
"nsfwCensorImage": "", "nsfwCensorImage": "",
"showFeaturesPanel": true "showFeaturesPanel": true,
"minimalScopesMode": false
} }