Merge branch 'improve_settings_reusability' into shigusegubu-vue3
* improve_settings_reusability: ChoiceSetting support added, added captcha settings limits tab, backend descriptions remove obsolete files lint setting admin settings works now. also now we have draftable settings initial admin settings prototype (WIP) initial implementation of an admin settings module lint fixes for stuff i missed minimize the rest of the sharedcomputedobject move websocket connection logic into module serverSideConfig renamed into profileSettingConfig to avoid confusion with serverSideStorage, reduced overall need for SharedComputedObject in settings tabs, moved copypaste code of "setting" type of helpers into a separate file.
This commit is contained in:
commit
a010dd73e9
44 changed files with 1368 additions and 380 deletions
|
|
@ -107,7 +107,10 @@ export default {
|
||||||
this.searchBarHidden = hidden
|
this.searchBarHidden = hidden
|
||||||
},
|
},
|
||||||
openSettingsModal () {
|
openSettingsModal () {
|
||||||
this.$store.dispatch('openSettingsModal')
|
this.$store.dispatch('openSettingsModal', 'user')
|
||||||
|
},
|
||||||
|
openAdminModal () {
|
||||||
|
this.$store.dispatch('openSettingsModal', 'admin')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,20 +48,19 @@
|
||||||
icon="cog"
|
icon="cog"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<a
|
<button
|
||||||
v-if="currentUser && currentUser.role === 'admin'"
|
v-if="currentUser && currentUser.role === 'admin'"
|
||||||
href="/pleroma/admin/#/login-pleroma"
|
class="button-unstyled nav-icon"
|
||||||
class="nav-icon"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:title="$t('nav.administration')"
|
:title="$t('nav.administration')"
|
||||||
@click.stop
|
@click.stop="openAdminModal"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
class="fa-scale-110 fa-old-padding"
|
class="fa-scale-110 fa-old-padding"
|
||||||
icon="tachometer-alt"
|
icon="tachometer-alt"
|
||||||
/>
|
/>
|
||||||
</a>
|
</button>
|
||||||
<span class="spacer" />
|
<span class="spacer" />
|
||||||
<button
|
<button
|
||||||
v-if="currentUser"
|
v-if="currentUser"
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
<button
|
<button
|
||||||
class="button-default btn"
|
class="button-default btn"
|
||||||
@click="addLanguage"
|
@click="addLanguage"
|
||||||
>{{ $t('settings.add_language') }}</button>
|
>
|
||||||
|
{{ $t('settings.add_language') }}
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -162,8 +162,8 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled expand-icon"
|
class="button-unstyled expand-icon"
|
||||||
:aria-expanded="statusExpanded"
|
|
||||||
:title="$t('tool_tip.toggle_expand')"
|
:title="$t('tool_tip.toggle_expand')"
|
||||||
|
:aria-expanded="statusExpanded"
|
||||||
@click.prevent="toggleStatusExpanded"
|
@click.prevent="toggleStatusExpanded"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
|
||||||
29
src/components/settings_modal/admin_tabs/instance_tab.js
Normal file
29
src/components/settings_modal/admin_tabs/instance_tab.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
|
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||||
|
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||||
|
import StringSetting from '../helpers/string_setting.vue'
|
||||||
|
|
||||||
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faGlobe
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faGlobe
|
||||||
|
)
|
||||||
|
|
||||||
|
const InstanceTab = {
|
||||||
|
data () {},
|
||||||
|
components: {
|
||||||
|
BooleanSetting,
|
||||||
|
ChoiceSetting,
|
||||||
|
IntegerSetting,
|
||||||
|
StringSetting
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...SharedComputedObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstanceTab
|
||||||
170
src/components/settings_modal/admin_tabs/instance_tab.vue
Normal file
170
src/components/settings_modal/admin_tabs/instance_tab.vue
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
<template>
|
||||||
|
<div :label="$t('admin_dash.instance')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.instance') }}</h2>
|
||||||
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:name"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
NAME
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:email"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
ADMIN EMAIL
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:description"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
DESCRIPTION
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:short_description"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
SHORT DESCRIPTION
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:instance_thumbnail"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
INSTANCE THUMBNAIL
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:background_image"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
BACKGROUND IMAGE
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:public"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
PUBLIC
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.registrations') }}</h2>
|
||||||
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:registrations_open"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
REGISTRATIONS OPEN
|
||||||
|
</BooleanSetting>
|
||||||
|
<ul class="setting-list suboptions">
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:invites_enabled"
|
||||||
|
parent-path=":pleroma.:instance.:registrations_open"
|
||||||
|
:parent-invert="true"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
INVITES ENABLED
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:account_activation_required"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
ACTIVATION REQUIRED
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:account_approval_required"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
APPROVAL REQUIRED
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>{{ $t('admin_dash.captcha.header') }}</h3>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
source="admin"
|
||||||
|
:path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
CAPTCHA
|
||||||
|
</BooleanSetting>
|
||||||
|
<ul class="setting-list suboptions">
|
||||||
|
<li>
|
||||||
|
<ChoiceSetting
|
||||||
|
source="admin"
|
||||||
|
:path="[':pleroma', 'Pleroma.Captcha', ':method']"
|
||||||
|
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
|
||||||
|
:option-label-map="{
|
||||||
|
'Pleroma.Captcha.Native': $t('admin_dash.captcha.native'),
|
||||||
|
'Pleroma.Captcha.Kocaptcha': $t('admin_dash.captcha.kocaptcha')
|
||||||
|
}"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
CAPTCHA TYPE
|
||||||
|
</ChoiceSetting>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
:path="[':pleroma', 'Pleroma.Captcha', ':seconds_valid']"
|
||||||
|
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
VALID
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul
|
||||||
|
v-if="adminConfig[':pleroma']['Pleroma.Captcha'][':enabled'] && adminConfig[':pleroma']['Pleroma.Captcha'][':method'] === 'Pleroma.Captcha.Kocaptcha'"
|
||||||
|
class="setting-list suboptions"
|
||||||
|
>
|
||||||
|
<h4>{{ $t('admin_dash.kocaptcha') }}</h4>
|
||||||
|
<li>
|
||||||
|
<StringSetting
|
||||||
|
source="admin"
|
||||||
|
:path="[':pleroma', 'Pleroma.Captcha.Kocaptcha', ':endpoint']"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
cockAPTCHA ENDPOINT
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./instance_tab.js"></script>
|
||||||
29
src/components/settings_modal/admin_tabs/limits_tab.js
Normal file
29
src/components/settings_modal/admin_tabs/limits_tab.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
|
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||||
|
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||||
|
import StringSetting from '../helpers/string_setting.vue'
|
||||||
|
|
||||||
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faGlobe
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faGlobe
|
||||||
|
)
|
||||||
|
|
||||||
|
const LimitsTab = {
|
||||||
|
data () {},
|
||||||
|
components: {
|
||||||
|
BooleanSetting,
|
||||||
|
ChoiceSetting,
|
||||||
|
IntegerSetting,
|
||||||
|
StringSetting
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...SharedComputedObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LimitsTab
|
||||||
152
src/components/settings_modal/admin_tabs/limits_tab.vue
Normal file
152
src/components/settings_modal/admin_tabs/limits_tab.vue
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
<template>
|
||||||
|
<div :label="$t('admin_dash.instance')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.arbitrary_limits') }}</h2>
|
||||||
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
<h3>{{ $t('admin_dash.limits.posts') }}</h3>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:limit"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
POST LIMIT
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:remote_limit"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
POST LIMIT (remote)
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>{{ $t('admin_dash.limits.uploads') }}</h3>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:description_limit"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
IMAGE DESCRIPTION LIMIT
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:upload_limit"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
UPLOAD LIMIT KiB
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:max_media_attachments"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX ATTACHMENTS
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>{{ $t('admin_dash.limits.users') }}</h3>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:user_bio_length"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
BIO LENGTH
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:user_name_length"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
NAME LENGTH
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h4>{{ $t('admin_dash.limits.profile_fields') }}</h4>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:max_account_fields"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX ACCOUNT FIELDS
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:max_remote_account_fields"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX ACCOUNT FIELDS (remote)
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:account_field_name_length"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX ACCOUNT FIELD NAME
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:account_field_value_length"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX ACCOUNT VALUE NAME
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h4>{{ $t('admin_dash.limits.user_uploads') }}</h4>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:avatar_upload_limit"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX AVATAR SIZE KiB
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:banner_upload_limit"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX BANNER SIZE KiB
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
source="admin"
|
||||||
|
path=":pleroma.:instance.:max_pinned_statuses"
|
||||||
|
draft-mode
|
||||||
|
>
|
||||||
|
MAX PINNED POSTS
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./limits_tab.js"></script>
|
||||||
|
|
@ -1,56 +1,16 @@
|
||||||
import { get, set } from 'lodash'
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import ModifiedIndicator from './modified_indicator.vue'
|
import Setting from './setting.js'
|
||||||
import ServerSideIndicator from './server_side_indicator.vue'
|
|
||||||
export default {
|
export default {
|
||||||
|
...Setting,
|
||||||
components: {
|
components: {
|
||||||
Checkbox,
|
...Setting.components,
|
||||||
ModifiedIndicator,
|
Checkbox
|
||||||
ServerSideIndicator
|
|
||||||
},
|
|
||||||
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: {
|
methods: {
|
||||||
update (e) {
|
...Setting.methods,
|
||||||
const [firstSegment, ...rest] = this.path.split('.')
|
getValue (e) {
|
||||||
set(this.$parent, this.path, e)
|
return 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,34 @@
|
||||||
class="BooleanSetting"
|
class="BooleanSetting"
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
:model-value="state"
|
:model-value="draftMode ? draft :state"
|
||||||
:disabled="disabled"
|
:disabled="shouldBeDisabled"
|
||||||
@update:modelValue="update"
|
@update:modelValue="update"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="!!$slots.default"
|
v-if="!!$slots.default"
|
||||||
class="label"
|
class="label"
|
||||||
>
|
>
|
||||||
|
<template v-if="backendDescription">
|
||||||
|
{{ backendDescriptionLabel + ' ' }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<slot />
|
<slot />
|
||||||
|
</template>
|
||||||
</span>
|
</span>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
<ModifiedIndicator
|
<ModifiedIndicator
|
||||||
:changed="isChanged"
|
:changed="isChanged"
|
||||||
:onclick="reset"
|
:onclick="reset"
|
||||||
/>
|
/>
|
||||||
<ServerSideIndicator :server-side="isServerSide" />
|
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||||
|
<DraftButtons />
|
||||||
|
<p
|
||||||
|
v-if="backendDescriptionDescription"
|
||||||
|
class="setting-description"
|
||||||
|
>
|
||||||
|
{{ backendDescriptionDescription + ' ' }}
|
||||||
|
</p>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,42 @@
|
||||||
import { get, set } from 'lodash'
|
|
||||||
import Select from 'src/components/select/select.vue'
|
import Select from 'src/components/select/select.vue'
|
||||||
import ModifiedIndicator from './modified_indicator.vue'
|
import Setting from './setting.js'
|
||||||
import ServerSideIndicator from './server_side_indicator.vue'
|
|
||||||
export default {
|
export default {
|
||||||
|
...Setting,
|
||||||
components: {
|
components: {
|
||||||
Select,
|
...Setting.components,
|
||||||
ModifiedIndicator,
|
Select
|
||||||
ServerSideIndicator
|
|
||||||
},
|
},
|
||||||
props: [
|
props: {
|
||||||
'path',
|
...Setting.props,
|
||||||
'disabled',
|
options: {
|
||||||
'options',
|
type: Array,
|
||||||
'expert'
|
required: false
|
||||||
],
|
|
||||||
computed: {
|
|
||||||
pathDefault () {
|
|
||||||
const [firstSegment, ...rest] = this.path.split('.')
|
|
||||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
|
||||||
},
|
},
|
||||||
state () {
|
optionLabelMap: {
|
||||||
const value = get(this.$parent, this.path)
|
type: Object,
|
||||||
if (value === undefined) {
|
required: false,
|
||||||
return this.defaultState
|
default: {}
|
||||||
} else {
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
defaultState () {
|
computed: {
|
||||||
return get(this.$parent, this.pathDefault)
|
...Setting.computed,
|
||||||
},
|
realOptions () {
|
||||||
isServerSide () {
|
if (this.source === 'admin') {
|
||||||
return this.path.startsWith('serverSide_')
|
console.log(this.backendDescriptionSuggestions)
|
||||||
},
|
return this.backendDescriptionSuggestions.map(x => ({
|
||||||
isChanged () {
|
key: x,
|
||||||
return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
|
value: x,
|
||||||
},
|
label: this.optionLabelMap[x] || x
|
||||||
matchesExpertLevel () {
|
}))
|
||||||
return (this.expert || 0) <= this.$parent.expertLevel
|
}
|
||||||
|
return this.options
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
update (e) {
|
...Setting.methods,
|
||||||
set(this.$parent, this.path, e)
|
getValue (e) {
|
||||||
},
|
return e
|
||||||
reset () {
|
|
||||||
set(this.$parent, this.path, this.defaultState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,20 @@
|
||||||
v-if="matchesExpertLevel"
|
v-if="matchesExpertLevel"
|
||||||
class="ChoiceSetting"
|
class="ChoiceSetting"
|
||||||
>
|
>
|
||||||
|
<template v-if="backendDescription">
|
||||||
|
{{ backendDescriptionLabel }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<slot />
|
<slot />
|
||||||
|
</template>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
<Select
|
<Select
|
||||||
:model-value="state"
|
:model-value="draftMode ? draft :state"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@update:modelValue="update"
|
@update:modelValue="update"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in options"
|
v-for="option in realOptions"
|
||||||
:key="option.key"
|
:key="option.key"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
>
|
>
|
||||||
|
|
@ -23,7 +28,14 @@
|
||||||
:changed="isChanged"
|
:changed="isChanged"
|
||||||
:onclick="reset"
|
:onclick="reset"
|
||||||
/>
|
/>
|
||||||
<ServerSideIndicator :server-side="isServerSide" />
|
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||||
|
<DraftButtons />
|
||||||
|
<p
|
||||||
|
v-if="backendDescriptionDescription"
|
||||||
|
class="setting-description"
|
||||||
|
>
|
||||||
|
{{ backendDescriptionDescription + ' ' }}
|
||||||
|
</p>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
102
src/components/settings_modal/helpers/draft_buttons.vue
Normal file
102
src/components/settings_modal/helpers/draft_buttons.vue
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<!-- this is a helper exclusive to Setting components -->
|
||||||
|
<!-- TODO make it reusable -->
|
||||||
|
<template>
|
||||||
|
<span
|
||||||
|
class="DraftButtons"
|
||||||
|
>
|
||||||
|
<Popover
|
||||||
|
trigger="hover"
|
||||||
|
:trigger-attrs="{ 'aria-label': $t('settings.commit_value_tooltip') }"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="$parent.isDirty"
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
:title="$t('settings.commit_value')"
|
||||||
|
@click="$parent.commitDraft"
|
||||||
|
>
|
||||||
|
{{ $t('settings.commit_value') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div class="modified-tooltip">
|
||||||
|
{{ $t('settings.commit_value_tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
<Popover
|
||||||
|
trigger="hover"
|
||||||
|
:trigger-attrs="{ 'aria-label': $t('settings.reset_value_tooltip') }"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="$parent.isDirty"
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
:title="$t('settings.reset_value')"
|
||||||
|
@click="$parent.reset"
|
||||||
|
>
|
||||||
|
{{ $t('settings.reset_value') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div class="modified-tooltip">
|
||||||
|
{{ $t('settings.reset_value_tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
<Popover
|
||||||
|
trigger="hover"
|
||||||
|
:trigger-attrs="{ 'aria-label': $t('settings.hard_reset_value_tooltip') }"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="$parent.canHardReset"
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
:title="$t('settings.hard_reset_value')"
|
||||||
|
@click="$parent.hardReset"
|
||||||
|
>
|
||||||
|
{{ $t('settings.hard_reset_value') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div class="modified-tooltip">
|
||||||
|
{{ $t('settings.hard_reset_value_tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import { faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faWrench
|
||||||
|
)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Popover },
|
||||||
|
props: ['changed']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.DraftButtons {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draft-tooltip {
|
||||||
|
margin: 0.5em 1em;
|
||||||
|
min-width: 10em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,56 +1,24 @@
|
||||||
import { get, set } from 'lodash'
|
import Setting from './setting.js'
|
||||||
import ModifiedIndicator from './modified_indicator.vue'
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
...Setting,
|
||||||
ModifiedIndicator
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
path: String,
|
...Setting.props,
|
||||||
disabled: Boolean,
|
truncate: {
|
||||||
min: Number,
|
type: Number,
|
||||||
step: Number,
|
required: false,
|
||||||
truncate: Number,
|
default: 1
|
||||||
expert: [Number, String]
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
pathDefault () {
|
|
||||||
const [firstSegment, ...rest] = this.path.split('.')
|
|
||||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
|
||||||
},
|
|
||||||
parent () {
|
|
||||||
return this.$parent.$parent
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
truncateValue (value) {
|
...Setting.methods,
|
||||||
if (!this.truncate) {
|
getValue (e) {
|
||||||
return value
|
if (!this.truncate === 1) {
|
||||||
|
return parseInt(e.target.value)
|
||||||
|
} else if (this.truncate > 1) {
|
||||||
|
return Math.trunc(e.target.value / this.truncate) * this.truncate
|
||||||
}
|
}
|
||||||
|
return parseFloat(e.target.value)
|
||||||
return Math.trunc(value / this.truncate) * this.truncate
|
|
||||||
},
|
|
||||||
update (e) {
|
|
||||||
set(this.parent, this.path, this.truncateValue(parseFloat(e.target.value)))
|
|
||||||
},
|
|
||||||
reset () {
|
|
||||||
set(this.parent, this.path, this.defaultState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,12 @@
|
||||||
class="NumberSetting"
|
class="NumberSetting"
|
||||||
>
|
>
|
||||||
<label :for="path">
|
<label :for="path">
|
||||||
|
<template v-if="backendDescription">
|
||||||
|
{{ backendDescriptionLabel + ' ' }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<slot />
|
<slot />
|
||||||
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
:id="path"
|
:id="path"
|
||||||
|
|
@ -13,7 +18,7 @@
|
||||||
:step="step || 1"
|
:step="step || 1"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:min="min || 0"
|
:min="min || 0"
|
||||||
:value="state"
|
:value="draftMode ? draft :state"
|
||||||
@change="update"
|
@change="update"
|
||||||
>
|
>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
|
|
@ -21,6 +26,14 @@
|
||||||
:changed="isChanged"
|
:changed="isChanged"
|
||||||
:onclick="reset"
|
:onclick="reset"
|
||||||
/>
|
/>
|
||||||
|
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||||
|
<DraftButtons />
|
||||||
|
<p
|
||||||
|
v-if="backendDescriptionDescription"
|
||||||
|
class="setting-description"
|
||||||
|
>
|
||||||
|
{{ backendDescriptionDescription + ' ' }}
|
||||||
|
</p>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
v-if="serverSide"
|
v-if="isProfile"
|
||||||
class="ServerSideIndicator"
|
class="ProfileSettingIndicator"
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="serverside-tooltip">
|
<div class="profilesetting-tooltip">
|
||||||
{{ $t('settings.setting_server_side') }}
|
{{ $t('settings.setting_server_side') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -33,17 +33,17 @@ library.add(
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Popover },
|
components: { Popover },
|
||||||
props: ['serverSide']
|
props: ['isProfile']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.ServerSideIndicator {
|
.ProfileSettingIndicator {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.serverside-tooltip {
|
.profilesetting-tooltip {
|
||||||
margin: 0.5em 1em;
|
margin: 0.5em 1em;
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
164
src/components/settings_modal/helpers/setting.js
Normal file
164
src/components/settings_modal/helpers/setting.js
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
import ModifiedIndicator from './modified_indicator.vue'
|
||||||
|
import ProfileSettingIndicator from './profile_setting_indicator.vue'
|
||||||
|
import DraftButtons from './draft_buttons.vue'
|
||||||
|
import { get, set } from 'lodash'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Checkbox,
|
||||||
|
ModifiedIndicator,
|
||||||
|
DraftButtons,
|
||||||
|
ProfileSettingIndicator
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
path: {
|
||||||
|
type: [String, Array],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
parentPath: {
|
||||||
|
type: [String, Array]
|
||||||
|
},
|
||||||
|
parentInvert: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
expert: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
source: {
|
||||||
|
type: String,
|
||||||
|
default: 'default'
|
||||||
|
},
|
||||||
|
draftMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
draft: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.draftMode) {
|
||||||
|
this.draft = this.state
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
state () {
|
||||||
|
const value = get(this.configSource, this.path)
|
||||||
|
if (value === undefined) {
|
||||||
|
return this.defaultState
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backendDescription () {
|
||||||
|
return get(this.$store.state.adminSettings.descriptions, this.path)
|
||||||
|
},
|
||||||
|
backendDescriptionLabel () {
|
||||||
|
return this.backendDescription?.label
|
||||||
|
},
|
||||||
|
backendDescriptionDescription () {
|
||||||
|
return this.backendDescription?.description
|
||||||
|
},
|
||||||
|
backendDescriptionSuggestions () {
|
||||||
|
return this.backendDescription?.suggestions
|
||||||
|
},
|
||||||
|
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
|
||||||
|
case 'admin':
|
||||||
|
return this.$store.state.adminSettings.config
|
||||||
|
default:
|
||||||
|
return this.$store.getters.mergedConfig
|
||||||
|
}
|
||||||
|
},
|
||||||
|
configSink () {
|
||||||
|
switch (this.source) {
|
||||||
|
case 'profile':
|
||||||
|
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
|
||||||
|
case 'admin':
|
||||||
|
return (k, v) => this.$store.dispatch('pushAdminSetting', { path: 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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isProfileSetting () {
|
||||||
|
return this.source === 'profile'
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
switch (this.source) {
|
||||||
|
case 'profile':
|
||||||
|
case 'admin':
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return this.state !== this.defaultState
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isDirty () {
|
||||||
|
return this.draftMode && this.draft !== this.state
|
||||||
|
},
|
||||||
|
canHardReset () {
|
||||||
|
return this.source === 'admin' && this.$store.state.adminSettings.modifiedPaths.has(this.path)
|
||||||
|
},
|
||||||
|
matchesExpertLevel () {
|
||||||
|
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getValue (e) {
|
||||||
|
return e.target.value
|
||||||
|
},
|
||||||
|
update (e) {
|
||||||
|
if (this.draftMode) {
|
||||||
|
this.draft = this.getValue(e)
|
||||||
|
} else {
|
||||||
|
this.configSink(this.path, this.getValue(e))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commitDraft () {
|
||||||
|
if (this.draftMode) {
|
||||||
|
this.configSink(this.path, this.draft)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset () {
|
||||||
|
console.log('reset')
|
||||||
|
if (this.draftMode) {
|
||||||
|
console.log(this.draft)
|
||||||
|
console.log(this.state)
|
||||||
|
this.draft = this.state
|
||||||
|
} else {
|
||||||
|
set(this.$store.getters.mergedConfig, this.path, this.defaultState)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hardReset () {
|
||||||
|
switch (this.source) {
|
||||||
|
case 'admin':
|
||||||
|
return this.$store.dispatch('resetAdminSetting', { path: this.path })
|
||||||
|
.then(() => { this.draft = this.state })
|
||||||
|
default:
|
||||||
|
console.warn('Hard reset not implemented yet!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,52 +1,15 @@
|
||||||
import { defaultState as configDefaultState } from 'src/modules/config.js'
|
|
||||||
import { defaultState as serverSideConfigDefaultState } from 'src/modules/serverSideConfig.js'
|
|
||||||
|
|
||||||
const SharedComputedObject = () => ({
|
const SharedComputedObject = () => ({
|
||||||
user () {
|
user () {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
},
|
},
|
||||||
// Getting values for default properties
|
expertLevel () {
|
||||||
...Object.keys(configDefaultState)
|
return this.$store.getters.mergedConfig.expertLevel > 0
|
||||||
.map(key => [
|
},
|
||||||
key + 'DefaultValue',
|
mergedConfig () {
|
||||||
function () {
|
return this.$store.getters.mergedConfig
|
||||||
return this.$store.getters.defaultConfig[key]
|
},
|
||||||
}
|
adminConfig () {
|
||||||
])
|
return this.$store.state.adminSettings.config
|
||||||
.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 })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,40 @@
|
||||||
import { get, set } from 'lodash'
|
|
||||||
import ModifiedIndicator from './modified_indicator.vue'
|
|
||||||
import Select from 'src/components/select/select.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 allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
|
||||||
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
|
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
|
||||||
export const defaultVerticalUnits = ['px', 'rem', 'vh']
|
export const defaultVerticalUnits = ['px', 'rem', 'vh']
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
...Setting,
|
||||||
components: {
|
components: {
|
||||||
ModifiedIndicator,
|
...Setting.components,
|
||||||
Select
|
Select
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
path: String,
|
...Setting.props,
|
||||||
disabled: Boolean,
|
|
||||||
min: Number,
|
min: Number,
|
||||||
units: {
|
units: {
|
||||||
type: [String],
|
type: Array,
|
||||||
default: () => allCssUnits
|
default: () => allCssUnits
|
||||||
},
|
|
||||||
expert: [Number, String]
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
pathDefault () {
|
|
||||||
const [firstSegment, ...rest] = this.path.split('.')
|
|
||||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
|
||||||
},
|
|
||||||
stateUnit () {
|
|
||||||
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 () {
|
computed: {
|
||||||
return get(this.$parent, this.pathDefault)
|
...Setting.computed,
|
||||||
|
stateUnit () {
|
||||||
|
return this.state.replace(/\d+/, '')
|
||||||
},
|
},
|
||||||
isChanged () {
|
stateValue () {
|
||||||
return this.state !== this.defaultState
|
return this.state.replace(/\D+/, '')
|
||||||
},
|
|
||||||
matchesExpertLevel () {
|
|
||||||
return (this.expert || 0) <= this.$parent.expertLevel
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
update (e) {
|
...Setting.methods,
|
||||||
set(this.$parent, this.path, e)
|
|
||||||
},
|
|
||||||
reset () {
|
|
||||||
set(this.$parent, this.path, this.defaultState)
|
|
||||||
},
|
|
||||||
updateValue (e) {
|
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) {
|
updateUnit (e) {
|
||||||
set(this.$parent, this.path, this.stateValue + e.target.value)
|
this.configSink(this.path, this.stateValue + e.target.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
src/components/settings_modal/helpers/string_setting.js
Normal file
5
src/components/settings_modal/helpers/string_setting.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Setting from './setting.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...Setting
|
||||||
|
}
|
||||||
38
src/components/settings_modal/helpers/string_setting.vue
Normal file
38
src/components/settings_modal/helpers/string_setting.vue
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<label
|
||||||
|
v-if="matchesExpertLevel"
|
||||||
|
class="StringSetting"
|
||||||
|
>
|
||||||
|
<label :for="path">
|
||||||
|
<template v-if="backendDescription">
|
||||||
|
{{ backendDescriptionLabel + ' ' }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
:id="path"
|
||||||
|
class="string-input"
|
||||||
|
step="1"
|
||||||
|
:disabled="disabled"
|
||||||
|
:value="draftMode ? draft :state"
|
||||||
|
@change="update"
|
||||||
|
>
|
||||||
|
{{ ' ' }}
|
||||||
|
<ModifiedIndicator
|
||||||
|
:changed="isChanged"
|
||||||
|
:onclick="reset"
|
||||||
|
/>
|
||||||
|
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||||
|
<DraftButtons />
|
||||||
|
<p
|
||||||
|
v-if="backendDescriptionDescription"
|
||||||
|
class="setting-description"
|
||||||
|
>
|
||||||
|
{{ backendDescriptionDescription + ' ' }}
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./string_setting.js"></script>
|
||||||
|
|
@ -53,8 +53,16 @@ const SettingsModal = {
|
||||||
Modal,
|
Modal,
|
||||||
Popover,
|
Popover,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
SettingsModalContent: getResettableAsyncComponent(
|
SettingsModalUserContent: getResettableAsyncComponent(
|
||||||
() => import('./settings_modal_content.vue'),
|
() => import('./settings_modal_user_content.vue'),
|
||||||
|
{
|
||||||
|
loadingComponent: PanelLoading,
|
||||||
|
errorComponent: AsyncComponentError,
|
||||||
|
delay: 0
|
||||||
|
}
|
||||||
|
),
|
||||||
|
SettingsModalAdminContent: getResettableAsyncComponent(
|
||||||
|
() => import('./settings_modal_admin_content.vue'),
|
||||||
{
|
{
|
||||||
loadingComponent: PanelLoading,
|
loadingComponent: PanelLoading,
|
||||||
errorComponent: AsyncComponentError,
|
errorComponent: AsyncComponentError,
|
||||||
|
|
@ -156,8 +164,14 @@ const SettingsModal = {
|
||||||
modalActivated () {
|
modalActivated () {
|
||||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||||
},
|
},
|
||||||
modalOpenedOnce () {
|
modalMode () {
|
||||||
return this.$store.state.interface.settingsModalLoaded
|
return this.$store.state.interface.settingsModalMode
|
||||||
|
},
|
||||||
|
modalOpenedOnceUser () {
|
||||||
|
return this.$store.state.interface.settingsModalLoadedUser
|
||||||
|
},
|
||||||
|
modalOpenedOnceAdmin () {
|
||||||
|
return this.$store.state.interface.settingsModalLoadedAdmin
|
||||||
},
|
},
|
||||||
modalPeeked () {
|
modalPeeked () {
|
||||||
return this.$store.state.interface.settingsModalState === 'minimized'
|
return this.$store.state.interface.settingsModalState === 'minimized'
|
||||||
|
|
@ -167,7 +181,6 @@ const SettingsModal = {
|
||||||
return this.$store.state.config.expertLevel > 0
|
return this.$store.state.config.expertLevel > 0
|
||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
console.log(value)
|
|
||||||
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setting-description {
|
||||||
|
margin-top: 0.2em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-modal-panel {
|
.settings-modal-panel {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: transform;
|
transition: transform;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<SettingsModalContent v-if="modalOpenedOnce" />
|
<SettingsModalUserContent v-if="modalMode === 'user' && modalOpenedOnceUser" />
|
||||||
|
<SettingsModalAdminContent v-if="modalMode === 'admin' && modalOpenedOnceAdmin" />
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer settings-footer">
|
<div class="panel-footer settings-footer">
|
||||||
<Popover
|
<Popover
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
|
||||||
|
import DataImportExportTab from './tabs/data_import_export_tab.vue'
|
||||||
|
import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue'
|
||||||
|
import InstanceTab from './admin_tabs/instance_tab.vue'
|
||||||
|
import LimitsTab from './admin_tabs/limits_tab.vue'
|
||||||
|
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faWrench,
|
||||||
|
faUser,
|
||||||
|
faFilter,
|
||||||
|
faPaintBrush,
|
||||||
|
faBell,
|
||||||
|
faDownload,
|
||||||
|
faEyeSlash,
|
||||||
|
faInfo
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faWrench,
|
||||||
|
faUser,
|
||||||
|
faFilter,
|
||||||
|
faPaintBrush,
|
||||||
|
faBell,
|
||||||
|
faDownload,
|
||||||
|
faEyeSlash,
|
||||||
|
faInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
const SettingsModalAdminContent = {
|
||||||
|
components: {
|
||||||
|
TabSwitcher,
|
||||||
|
|
||||||
|
DataImportExportTab,
|
||||||
|
MutesAndBlocksTab,
|
||||||
|
InstanceTab,
|
||||||
|
LimitsTab
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoggedIn () {
|
||||||
|
return !!this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
open () {
|
||||||
|
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||||
|
},
|
||||||
|
bodyLock () {
|
||||||
|
return this.$store.state.interface.settingsModalState === 'visible'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onOpen () {
|
||||||
|
const targetTab = this.$store.state.interface.settingsModalTargetTab
|
||||||
|
// We're being told to open in specific tab
|
||||||
|
if (targetTab) {
|
||||||
|
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
|
||||||
|
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||||
|
})
|
||||||
|
if (tabIndex >= 0) {
|
||||||
|
this.$refs.tabSwitcher.setTab(tabIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear the state of target tab, so that next time settings is opened
|
||||||
|
// it doesn't force it.
|
||||||
|
this.$store.dispatch('clearSettingsModalTargetTab')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.onOpen()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
open: function (value) {
|
||||||
|
if (value) this.onOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsModalAdminContent
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<tab-switcher
|
||||||
|
ref="tabSwitcher"
|
||||||
|
class="settings_tab-switcher"
|
||||||
|
:side-tab-bar="true"
|
||||||
|
:scrollable-tabs="true"
|
||||||
|
:body-scroll-lock="bodyLock"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:label="$t('admin_dash.instance')"
|
||||||
|
icon="wrench"
|
||||||
|
data-tab-name="general"
|
||||||
|
>
|
||||||
|
<InstanceTab />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:label="$t('admin_dash.limits')"
|
||||||
|
icon="wrench"
|
||||||
|
data-tab-name="limits"
|
||||||
|
>
|
||||||
|
<LimitsTab />
|
||||||
|
</div>
|
||||||
|
</tab-switcher>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./settings_modal_admin_content.js"></script>
|
||||||
|
|
||||||
|
<style src="./settings_modal_admin_content.scss" lang="scss"></style>
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
@import "src/variables";
|
||||||
|
|
||||||
|
.settings_tab-switcher {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.setting-item {
|
||||||
|
border-bottom: 2px solid var(--fg, $fallback--fg);
|
||||||
|
margin: 1em 1em 1.4em;
|
||||||
|
padding-bottom: 1.4em;
|
||||||
|
|
||||||
|
> div,
|
||||||
|
> label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-multiple {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.option-list {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
min-width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unavailable,
|
||||||
|
.unavailable svg {
|
||||||
|
color: var(--cRed, $fallback--cRed);
|
||||||
|
color: $fallback--cRed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-input {
|
||||||
|
max-width: 6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -78,6 +78,6 @@
|
||||||
</tab-switcher>
|
</tab-switcher>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./settings_modal_content.js"></script>
|
<script src="./settings_modal_user_content.js"></script>
|
||||||
|
|
||||||
<style src="./settings_modal_content.scss" lang="scss"></style>
|
<style src="./settings_modal_user_content.scss" lang="scss"></style>
|
||||||
|
|
@ -7,13 +7,11 @@
|
||||||
<BooleanSetting path="hideFilteredStatuses">
|
<BooleanSetting path="hideFilteredStatuses">
|
||||||
{{ $t('settings.hide_filtered_statuses') }}
|
{{ $t('settings.hide_filtered_statuses') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
<ul
|
<ul class="setting-list suboptions">
|
||||||
class="setting-list suboptions"
|
|
||||||
:class="[{disabled: !streaming}]"
|
|
||||||
>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
:disabled="hideFilteredStatuses"
|
parent-path="hideFilteredStatuses"
|
||||||
|
:parent-invert="true"
|
||||||
path="hideWordFilteredPosts"
|
path="hideWordFilteredPosts"
|
||||||
>
|
>
|
||||||
{{ $t('settings.hide_wordfiltered_statuses') }}
|
{{ $t('settings.hide_wordfiltered_statuses') }}
|
||||||
|
|
@ -22,7 +20,8 @@
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
v-if="user"
|
v-if="user"
|
||||||
:disabled="hideFilteredStatuses"
|
parent-path="hideFilteredStatuses"
|
||||||
|
:parent-invert="true"
|
||||||
path="hideMutedThreads"
|
path="hideMutedThreads"
|
||||||
>
|
>
|
||||||
{{ $t('settings.hide_muted_threads') }}
|
{{ $t('settings.hide_muted_threads') }}
|
||||||
|
|
@ -31,7 +30,8 @@
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
v-if="user"
|
v-if="user"
|
||||||
:disabled="hideFilteredStatuses"
|
parent-path="hideFilteredStatuses"
|
||||||
|
:parent-invert="true"
|
||||||
path="hideMutedPosts"
|
path="hideMutedPosts"
|
||||||
>
|
>
|
||||||
{{ $t('settings.hide_muted_posts') }}
|
{{ $t('settings.hide_muted_posts') }}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue
|
||||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||||
|
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faGlobe
|
faGlobe
|
||||||
|
|
@ -67,7 +67,7 @@ const GeneralTab = {
|
||||||
SizeSetting,
|
SizeSetting,
|
||||||
InterfaceLanguageSwitcher,
|
InterfaceLanguageSwitcher,
|
||||||
ScopeSelector,
|
ScopeSelector,
|
||||||
ServerSideIndicator
|
ProfileSettingIndicator
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
horizontalUnits () {
|
horizontalUnits () {
|
||||||
|
|
@ -110,7 +110,7 @@ const GeneralTab = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeDefaultScope (value) {
|
changeDefaultScope (value) {
|
||||||
this.$store.dispatch('setServerSideOption', { name: 'defaultScope', value })
|
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,11 @@
|
||||||
<BooleanSetting path="streaming">
|
<BooleanSetting path="streaming">
|
||||||
{{ $t('settings.streaming') }}
|
{{ $t('settings.streaming') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
<ul
|
<ul class="setting-list suboptions">
|
||||||
class="setting-list suboptions"
|
|
||||||
:class="[{disabled: !streaming}]"
|
|
||||||
>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="pauseOnUnfocused"
|
path="pauseOnUnfocused"
|
||||||
:disabled="!streaming"
|
parent-path="streaming"
|
||||||
>
|
>
|
||||||
{{ $t('settings.pause_on_unfocused') }}
|
{{ $t('settings.pause_on_unfocused') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
@ -213,7 +210,7 @@
|
||||||
</ChoiceSetting>
|
</ChoiceSetting>
|
||||||
</li>
|
</li>
|
||||||
<ul
|
<ul
|
||||||
v-if="conversationDisplay !== 'linear'"
|
v-if="mergedConfig.conversationDisplay !== 'linear'"
|
||||||
class="setting-list suboptions"
|
class="setting-list suboptions"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
|
|
@ -265,7 +262,8 @@
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
v-if="user"
|
v-if="user"
|
||||||
path="serverSide_stripRichContent"
|
source="profile"
|
||||||
|
path="stripRichContent"
|
||||||
expert="1"
|
expert="1"
|
||||||
>
|
>
|
||||||
{{ $t('settings.no_rich_text_description') }}
|
{{ $t('settings.no_rich_text_description') }}
|
||||||
|
|
@ -299,7 +297,7 @@
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="preloadImage"
|
path="preloadImage"
|
||||||
expert="1"
|
expert="1"
|
||||||
:disabled="!hideNsfw"
|
parent-path="hideNsfw"
|
||||||
>
|
>
|
||||||
{{ $t('settings.preload_images') }}
|
{{ $t('settings.preload_images') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
@ -308,7 +306,7 @@
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="useOneClickNsfw"
|
path="useOneClickNsfw"
|
||||||
expert="1"
|
expert="1"
|
||||||
:disabled="!hideNsfw"
|
parent-path="hideNsfw"
|
||||||
>
|
>
|
||||||
{{ $t('settings.use_one_click_nsfw') }}
|
{{ $t('settings.use_one_click_nsfw') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
@ -321,15 +319,13 @@
|
||||||
>
|
>
|
||||||
{{ $t('settings.loop_video') }}
|
{{ $t('settings.loop_video') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
<ul
|
<ul class="setting-list suboptions">
|
||||||
class="setting-list suboptions"
|
|
||||||
:class="[{disabled: !streaming}]"
|
|
||||||
>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="loopVideoSilentOnly"
|
path="loopVideoSilentOnly"
|
||||||
expert="1"
|
expert="1"
|
||||||
:disabled="!loopVideo || !loopSilentAvailable"
|
parent-path="loopVideo"
|
||||||
|
:disabled="!loopSilentAvailable"
|
||||||
>
|
>
|
||||||
{{ $t('settings.loop_video_silent_only') }}
|
{{ $t('settings.loop_video_silent_only') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
@ -427,18 +423,18 @@
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<label for="default-vis">
|
<label for="default-vis">
|
||||||
{{ $t('settings.default_vis') }} <ServerSideIndicator :server-side="true" />
|
{{ $t('settings.default_vis') }} <ProfileSettingIndicator :is-profile="true" />
|
||||||
<ScopeSelector
|
<ScopeSelector
|
||||||
class="scope-selector"
|
class="scope-selector"
|
||||||
:show-all="true"
|
:show-all="true"
|
||||||
:user-default="serverSide_defaultScope"
|
:user-default="$store.state.profileConfig.defaultScope"
|
||||||
:initial-scope="serverSide_defaultScope"
|
:initial-scope="$store.state.profileConfig.defaultScope"
|
||||||
:on-scope-change="changeDefaultScope"
|
:on-scope-change="changeDefaultScope"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<!-- <BooleanSetting path="serverSide_defaultNSFW"> -->
|
<!-- <BooleanSetting source="profile" path="defaultNSFW"> -->
|
||||||
<BooleanSetting path="sensitiveByDefault">
|
<BooleanSetting path="sensitiveByDefault">
|
||||||
{{ $t('settings.sensitive_by_default') }}
|
{{ $t('settings.sensitive_by_default') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,10 @@
|
||||||
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
|
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_blockNotificationsFromStrangers">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="blockNotificationsFromStrangers"
|
||||||
|
>
|
||||||
{{ $t('settings.notification_setting_block_from_strangers') }}
|
{{ $t('settings.notification_setting_block_from_strangers') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -67,7 +70,8 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="serverSide_webPushHideContents"
|
source="profile"
|
||||||
|
path="webPushHideContents"
|
||||||
expert="1"
|
expert="1"
|
||||||
>
|
>
|
||||||
{{ $t('settings.notification_setting_hide_notification_contents') }}
|
{{ $t('settings.notification_setting_hide_notification_contents') }}
|
||||||
|
|
|
||||||
|
|
@ -254,37 +254,50 @@
|
||||||
<h2>{{ $t('settings.account_privacy') }}</h2>
|
<h2>{{ $t('settings.account_privacy') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_locked">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="locked"
|
||||||
|
>
|
||||||
{{ $t('settings.lock_account_description') }}
|
{{ $t('settings.lock_account_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_discoverable">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="discoverable"
|
||||||
|
>
|
||||||
{{ $t('settings.discoverable') }}
|
{{ $t('settings.discoverable') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_allowFollowingMove">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="allowFollowingMove"
|
||||||
|
>
|
||||||
{{ $t('settings.allow_following_move') }}
|
{{ $t('settings.allow_following_move') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_hideFavorites">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="hideFavorites"
|
||||||
|
>
|
||||||
{{ $t('settings.hide_favorites_description') }}
|
{{ $t('settings.hide_favorites_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_hideFollowers">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="hideFollowers"
|
||||||
|
>
|
||||||
{{ $t('settings.hide_followers_description') }}
|
{{ $t('settings.hide_followers_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
<ul
|
<ul class="setting-list suboptions">
|
||||||
class="setting-list suboptions"
|
|
||||||
:class="[{disabled: !serverSide_hideFollowers}]"
|
|
||||||
>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="serverSide_hideFollowersCount"
|
source="profile"
|
||||||
:disabled="!serverSide_hideFollowers"
|
path="hideFollowersCount"
|
||||||
|
parent-path="hideFollowers"
|
||||||
>
|
>
|
||||||
{{ $t('settings.hide_followers_count_description') }}
|
{{ $t('settings.hide_followers_count_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
@ -292,17 +305,18 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="serverSide_hideFollows">
|
<BooleanSetting
|
||||||
|
source="profile"
|
||||||
|
path="hideFollows"
|
||||||
|
>
|
||||||
{{ $t('settings.hide_follows_description') }}
|
{{ $t('settings.hide_follows_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
<ul
|
<ul class="setting-list suboptions">
|
||||||
class="setting-list suboptions"
|
|
||||||
:class="[{disabled: !serverSide_hideFollows}]"
|
|
||||||
>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="serverSide_hideFollowsCount"
|
source="profile"
|
||||||
:disabled="!serverSide_hideFollows"
|
path="hideFollowsCount"
|
||||||
|
parent-path="hideFollows"
|
||||||
>
|
>
|
||||||
{{ $t('settings.hide_follows_count_description') }}
|
{{ $t('settings.hide_follows_count_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
|
|
|
||||||
|
|
@ -143,8 +143,8 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<i18n
|
<i18n-t
|
||||||
path="settings.new_alias_target"
|
keypath="settings.new_alias_target"
|
||||||
tag="p"
|
tag="p"
|
||||||
>
|
>
|
||||||
<code
|
<code
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
>
|
>
|
||||||
foo@example.org
|
foo@example.org
|
||||||
</code>
|
</code>
|
||||||
</i18n>
|
</i18n-t>
|
||||||
<input
|
<input
|
||||||
v-model="addAliasTarget"
|
v-model="addAliasTarget"
|
||||||
>
|
>
|
||||||
|
|
@ -175,16 +175,16 @@
|
||||||
<h2>{{ $t('settings.move_account') }}</h2>
|
<h2>{{ $t('settings.move_account') }}</h2>
|
||||||
<p>{{ $t('settings.move_account_notes') }}</p>
|
<p>{{ $t('settings.move_account_notes') }}</p>
|
||||||
<div>
|
<div>
|
||||||
<i18n
|
<i18n-t
|
||||||
path="settings.move_account_target"
|
keypath="settings.move_account_target"
|
||||||
tag="p"
|
tag="p"
|
||||||
>
|
>
|
||||||
<code
|
<template #example>
|
||||||
place="example"
|
<code>
|
||||||
>
|
|
||||||
foo@example.org
|
foo@example.org
|
||||||
</code>
|
</code>
|
||||||
</i18n>
|
</template>
|
||||||
|
</i18n-t>
|
||||||
<input
|
<input
|
||||||
v-model="moveAccountTarget"
|
v-model="moveAccountTarget"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -60,13 +60,7 @@ export default {
|
||||||
const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
||||||
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
settingsModalVisible () {
|
|
||||||
return this.settingsModalState === 'visible'
|
|
||||||
},
|
|
||||||
...mapState({
|
|
||||||
settingsModalState: state => state.interface.settingsModalState
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
beforeUpdate () {
|
beforeUpdate () {
|
||||||
const currentSlot = this.slots()[this.active]
|
const currentSlot = this.slots()[this.active]
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,9 @@ import listsModule from './modules/lists.js'
|
||||||
import usersModule from './modules/users.js'
|
import usersModule from './modules/users.js'
|
||||||
import apiModule from './modules/api.js'
|
import apiModule from './modules/api.js'
|
||||||
import configModule from './modules/config.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 serverSideStorageModule from './modules/serverSideStorage.js'
|
||||||
|
import adminSettingsModule from './modules/adminSettings.js'
|
||||||
import shoutModule from './modules/shout.js'
|
import shoutModule from './modules/shout.js'
|
||||||
import oauthModule from './modules/oauth.js'
|
import oauthModule from './modules/oauth.js'
|
||||||
import authFlowModule from './modules/auth_flow.js'
|
import authFlowModule from './modules/auth_flow.js'
|
||||||
|
|
@ -80,8 +81,9 @@ const persistedStateOptions = {
|
||||||
lists: listsModule,
|
lists: listsModule,
|
||||||
api: apiModule,
|
api: apiModule,
|
||||||
config: configModule,
|
config: configModule,
|
||||||
serverSideConfig: serverSideConfigModule,
|
profileConfig: profileConfigModule,
|
||||||
serverSideStorage: serverSideStorageModule,
|
serverSideStorage: serverSideStorageModule,
|
||||||
|
adminSettings: adminSettingsModule,
|
||||||
shout: shoutModule,
|
shout: shoutModule,
|
||||||
oauth: oauthModule,
|
oauth: oauthModule,
|
||||||
authFlow: authFlowModule,
|
authFlow: authFlowModule,
|
||||||
|
|
|
||||||
116
src/modules/adminSettings.js
Normal file
116
src/modules/adminSettings.js
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
import { set, cloneDeep } from 'lodash'
|
||||||
|
|
||||||
|
export const defaultState = {
|
||||||
|
needsReboot: null,
|
||||||
|
config: null,
|
||||||
|
modifiedPaths: null,
|
||||||
|
descriptions: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const newUserFlags = {
|
||||||
|
...defaultState.flagStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminSettingsStorage = {
|
||||||
|
state: {
|
||||||
|
...cloneDeep(defaultState)
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
updateAdminSettings (state, { config, modifiedPaths }) {
|
||||||
|
state.config = config
|
||||||
|
state.modifiedPaths = modifiedPaths
|
||||||
|
},
|
||||||
|
updateAdminDescriptions (state, { descriptions }) {
|
||||||
|
state.descriptions = descriptions
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) {
|
||||||
|
const config = state.config || {}
|
||||||
|
const modifiedPaths = state.modifiedPaths || new Set()
|
||||||
|
backendDbConfig.configs.forEach(c => {
|
||||||
|
const path = [c.group, c.key]
|
||||||
|
if (c.db) {
|
||||||
|
c.db.forEach(x => modifiedPaths.add(path + '.' + x))
|
||||||
|
}
|
||||||
|
const convert = (value) => {
|
||||||
|
if (Array.isArray(value) && value.length > 0 && value[0].tuple) {
|
||||||
|
return value.reduce((acc, c) => {
|
||||||
|
return { ...acc, [c.tuple[0]]: convert(c.tuple[1]) }
|
||||||
|
}, {})
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(config, path, convert(c.value))
|
||||||
|
})
|
||||||
|
console.log(config[':pleroma'])
|
||||||
|
commit('updateAdminSettings', { config, modifiedPaths })
|
||||||
|
},
|
||||||
|
setInstanceAdminDescriptions ({ state, commit, dispatch }, { backendDescriptions }) {
|
||||||
|
const convert = ({ children, description, label, key = '<ROOT>', group, suggestions }, path, acc) => {
|
||||||
|
const newPath = group ? [group, key] : [key]
|
||||||
|
const obj = { description, label, suggestions }
|
||||||
|
if (Array.isArray(children)) {
|
||||||
|
children.forEach(c => {
|
||||||
|
convert(c, newPath, obj)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
set(acc, newPath, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
const descriptions = {}
|
||||||
|
backendDescriptions.forEach(d => convert(d, '', descriptions))
|
||||||
|
console.log(descriptions[':pleroma']['Pleroma.Captcha'])
|
||||||
|
commit('updateAdminDescriptions', { descriptions })
|
||||||
|
},
|
||||||
|
pushAdminSetting ({ rootState, state, commit, dispatch }, { path, value }) {
|
||||||
|
const [group, key, ...rest] = Array.isArray(path) ? path : path.split(/\./g)
|
||||||
|
const clone = {} // not actually cloning the entire thing to avoid excessive writes
|
||||||
|
set(clone, rest, value)
|
||||||
|
|
||||||
|
// TODO cleanup paths in modifiedPaths
|
||||||
|
const convert = (value) => {
|
||||||
|
if (typeof value !== 'object') {
|
||||||
|
return value
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
return value.map(convert)
|
||||||
|
} else {
|
||||||
|
return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootState.api.backendInteractor.pushInstanceDBConfig({
|
||||||
|
payload: {
|
||||||
|
configs: [{
|
||||||
|
group,
|
||||||
|
key,
|
||||||
|
value: convert(clone)
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => rootState.api.backendInteractor.fetchInstanceDBConfig())
|
||||||
|
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||||
|
},
|
||||||
|
resetAdminSetting ({ rootState, state, commit, dispatch }, { path }) {
|
||||||
|
const [group, key, subkey] = path.split(/\./g)
|
||||||
|
|
||||||
|
state.modifiedPaths.delete(path)
|
||||||
|
|
||||||
|
return rootState.api.backendInteractor.pushInstanceDBConfig({
|
||||||
|
payload: {
|
||||||
|
configs: [{
|
||||||
|
group,
|
||||||
|
key,
|
||||||
|
delete: true,
|
||||||
|
subkeys: [subkey]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => rootState.api.backendInteractor.fetchInstanceDBConfig())
|
||||||
|
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default adminSettingsStorage
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
||||||
import messages from '../i18n/messages'
|
import messages from '../i18n/messages'
|
||||||
|
import { set } from 'lodash'
|
||||||
import localeService from '../services/locale/locale.service.js'
|
import localeService from '../services/locale/locale.service.js'
|
||||||
|
|
||||||
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
||||||
|
|
@ -148,7 +149,7 @@ const config = {
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setOption (state, { name, value }) {
|
setOption (state, { name, value }) {
|
||||||
state[name] = value
|
set(state, name, value)
|
||||||
},
|
},
|
||||||
setHighlight (state, { user, color, type }) {
|
setHighlight (state, { user, color, type }) {
|
||||||
const data = this.state.config.highlight[user]
|
const data = this.state.config.highlight[user]
|
||||||
|
|
@ -178,6 +179,25 @@ const config = {
|
||||||
commit('setHighlight', { user, color, type })
|
commit('setHighlight', { user, color, type })
|
||||||
},
|
},
|
||||||
setOption ({ commit, dispatch, state }, { name, value }) {
|
setOption ({ commit, dispatch, state }, { name, value }) {
|
||||||
|
const exceptions = new Set([
|
||||||
|
'useStreamingApi'
|
||||||
|
])
|
||||||
|
|
||||||
|
if (exceptions.has(name)) {
|
||||||
|
switch (name) {
|
||||||
|
case 'useStreamingApi': {
|
||||||
|
const action = value ? 'enableMastoSockets' : 'disableMastoSockets'
|
||||||
|
|
||||||
|
dispatch(action).then(() => {
|
||||||
|
commit('setOption', { name: 'useStreamingApi', value })
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error('Failed starting MastoAPI Streaming socket', e)
|
||||||
|
dispatch('disableMastoSockets')
|
||||||
|
dispatch('setOption', { name: 'useStreamingApi', value: false })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
commit('setOption', { name, value })
|
commit('setOption', { name, value })
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'theme':
|
case 'theme':
|
||||||
|
|
@ -207,6 +227,7 @@ const config = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
settingsModalState: 'hidden',
|
settingsModalState: 'hidden',
|
||||||
settingsModalLoaded: false,
|
settingsModalLoadedUser: false,
|
||||||
|
settingsModalLoadedAdmin: false,
|
||||||
settingsModalTargetTab: null,
|
settingsModalTargetTab: null,
|
||||||
|
settingsModalMode: 'user',
|
||||||
settings: {
|
settings: {
|
||||||
currentSaveStateNotice: null,
|
currentSaveStateNotice: null,
|
||||||
noticeClearTimeout: null,
|
noticeClearTimeout: null,
|
||||||
|
|
@ -54,10 +56,17 @@ const interfaceMod = {
|
||||||
throw new Error('Illegal minimization state of settings modal')
|
throw new Error('Illegal minimization state of settings modal')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openSettingsModal (state) {
|
openSettingsModal (state, value) {
|
||||||
|
state.settingsModalMode = value
|
||||||
state.settingsModalState = 'visible'
|
state.settingsModalState = 'visible'
|
||||||
if (!state.settingsModalLoaded) {
|
if (value === 'user') {
|
||||||
state.settingsModalLoaded = true
|
if (!state.settingsModalLoadedUser) {
|
||||||
|
state.settingsModalLoadedUser = true
|
||||||
|
}
|
||||||
|
} else if (value === 'admin') {
|
||||||
|
if (!state.settingsModalLoadedAdmin) {
|
||||||
|
state.settingsModalLoadedAdmin = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setSettingsModalTargetTab (state, value) {
|
setSettingsModalTargetTab (state, value) {
|
||||||
|
|
@ -92,8 +101,8 @@ const interfaceMod = {
|
||||||
closeSettingsModal ({ commit }) {
|
closeSettingsModal ({ commit }) {
|
||||||
commit('closeSettingsModal')
|
commit('closeSettingsModal')
|
||||||
},
|
},
|
||||||
openSettingsModal ({ commit }) {
|
openSettingsModal ({ commit }, value = 'user') {
|
||||||
commit('openSettingsModal')
|
commit('openSettingsModal', value)
|
||||||
},
|
},
|
||||||
togglePeekSettingsModal ({ commit }) {
|
togglePeekSettingsModal ({ commit }) {
|
||||||
commit('togglePeekSettingsModal')
|
commit('togglePeekSettingsModal')
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => {
|
||||||
.updateNotificationSettings({ settings })
|
.updateNotificationSettings({ settings })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (result.status === 'success') {
|
if (result.status === 'success') {
|
||||||
commit('confirmServerSideOption', { name, value })
|
commit('confirmProfileOption', { name, value })
|
||||||
} else {
|
} else {
|
||||||
commit('confirmServerSideOption', { name, value: oldValue })
|
commit('confirmProfileOption', { name, value: oldValue })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -94,16 +94,16 @@ export const settingsMap = {
|
||||||
|
|
||||||
export const defaultState = Object.fromEntries(Object.keys(settingsMap).map(key => [key, null]))
|
export const defaultState = Object.fromEntries(Object.keys(settingsMap).map(key => [key, null]))
|
||||||
|
|
||||||
const serverSideConfig = {
|
const profileConfig = {
|
||||||
state: { ...defaultState },
|
state: { ...defaultState },
|
||||||
mutations: {
|
mutations: {
|
||||||
confirmServerSideOption (state, { name, value }) {
|
confirmProfileOption (state, { name, value }) {
|
||||||
set(state, name, value)
|
set(state, name, value)
|
||||||
},
|
},
|
||||||
wipeServerSideOption (state, { name }) {
|
wipeProfileOption (state, { name }) {
|
||||||
set(state, name, null)
|
set(state, name, null)
|
||||||
},
|
},
|
||||||
wipeAllServerSideOptions (state) {
|
wipeAllProfileOptions (state) {
|
||||||
Object.keys(settingsMap).forEach(key => {
|
Object.keys(settingsMap).forEach(key => {
|
||||||
set(state, key, null)
|
set(state, key, null)
|
||||||
})
|
})
|
||||||
|
|
@ -118,23 +118,23 @@ const serverSideConfig = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setServerSideOption ({ rootState, state, commit, dispatch }, { name, value }) {
|
setProfileOption ({ rootState, state, commit, dispatch }, { name, value }) {
|
||||||
const oldValue = get(state, name)
|
const oldValue = get(state, name)
|
||||||
const map = settingsMap[name]
|
const map = settingsMap[name]
|
||||||
if (!map) throw new Error('Invalid server-side setting')
|
if (!map) throw new Error('Invalid server-side setting')
|
||||||
const { set: path = map, api = defaultApi } = map
|
const { set: path = map, api = defaultApi } = map
|
||||||
commit('wipeServerSideOption', { name })
|
commit('wipeProfileOption', { name })
|
||||||
|
|
||||||
api({ rootState, commit }, { path, value, oldValue })
|
api({ rootState, commit }, { path, value, oldValue })
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.warn('Error setting server-side option:', e)
|
console.warn('Error setting server-side option:', e)
|
||||||
commit('confirmServerSideOption', { name, value: oldValue })
|
commit('confirmProfileOption', { name, value: oldValue })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
logout ({ commit }) {
|
logout ({ commit }) {
|
||||||
commit('wipeAllServerSideOptions')
|
commit('wipeAllProfileOptions')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default serverSideConfig
|
export default profileConfig
|
||||||
|
|
@ -551,6 +551,7 @@ const users = {
|
||||||
loginUser (store, accessToken) {
|
loginUser (store, accessToken) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const commit = store.commit
|
const commit = store.commit
|
||||||
|
const dispatch = store.dispatch
|
||||||
commit('beginLogin')
|
commit('beginLogin')
|
||||||
store.rootState.api.backendInteractor.verifyCredentials(accessToken)
|
store.rootState.api.backendInteractor.verifyCredentials(accessToken)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
|
@ -563,59 +564,65 @@ const users = {
|
||||||
user.domainMutes = []
|
user.domainMutes = []
|
||||||
commit('setCurrentUser', user)
|
commit('setCurrentUser', user)
|
||||||
commit('setServerSideStorage', user)
|
commit('setServerSideStorage', user)
|
||||||
|
if (user.rights.admin) {
|
||||||
|
store.rootState.api.backendInteractor.fetchInstanceDBConfig()
|
||||||
|
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||||
|
store.rootState.api.backendInteractor.fetchInstanceConfigDescriptions()
|
||||||
|
.then(backendDescriptions => dispatch('setInstanceAdminDescriptions', { backendDescriptions }))
|
||||||
|
}
|
||||||
commit('addNewUsers', [user])
|
commit('addNewUsers', [user])
|
||||||
|
|
||||||
store.dispatch('fetchEmoji')
|
dispatch('fetchEmoji')
|
||||||
|
|
||||||
getNotificationPermission()
|
getNotificationPermission()
|
||||||
.then(permission => commit('setNotificationPermission', permission))
|
.then(permission => commit('setNotificationPermission', permission))
|
||||||
|
|
||||||
// Set our new backend interactor
|
// Set our new backend interactor
|
||||||
commit('setBackendInteractor', backendInteractorService(accessToken))
|
commit('setBackendInteractor', backendInteractorService(accessToken))
|
||||||
store.dispatch('pushServerSideStorage')
|
dispatch('pushServerSideStorage')
|
||||||
|
|
||||||
if (user.token) {
|
if (user.token) {
|
||||||
store.dispatch('setWsToken', user.token)
|
dispatch('setWsToken', user.token)
|
||||||
|
|
||||||
// Initialize the shout socket.
|
// Initialize the shout socket.
|
||||||
store.dispatch('initializeSocket')
|
dispatch('initializeSocket')
|
||||||
}
|
}
|
||||||
|
|
||||||
const startPolling = () => {
|
const startPolling = () => {
|
||||||
// Start getting fresh posts.
|
// Start getting fresh posts.
|
||||||
store.dispatch('startFetchingTimeline', { timeline: 'friends' })
|
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
|
|
||||||
// Start fetching notifications
|
// Start fetching notifications
|
||||||
store.dispatch('startFetchingNotifications')
|
dispatch('startFetchingNotifications')
|
||||||
|
|
||||||
// Start fetching chats
|
// Start fetching chats
|
||||||
store.dispatch('startFetchingChats')
|
dispatch('startFetchingChats')
|
||||||
}
|
}
|
||||||
|
|
||||||
store.dispatch('startFetchingLists')
|
dispatch('startFetchingLists')
|
||||||
|
|
||||||
if (user.locked) {
|
if (user.locked) {
|
||||||
store.dispatch('startFetchingFollowRequests')
|
dispatch('startFetchingFollowRequests')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.mergedConfig.useStreamingApi) {
|
if (store.getters.mergedConfig.useStreamingApi) {
|
||||||
store.dispatch('fetchTimeline', { timeline: 'friends', since: null })
|
dispatch('fetchTimeline', { timeline: 'friends', since: null })
|
||||||
store.dispatch('fetchNotifications', { since: null })
|
dispatch('fetchNotifications', { since: null })
|
||||||
store.dispatch('enableMastoSockets', true).catch((error) => {
|
dispatch('enableMastoSockets', true).catch((error) => {
|
||||||
console.error('Failed initializing MastoAPI Streaming socket', error)
|
console.error('Failed initializing MastoAPI Streaming socket', error)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
store.dispatch('fetchChats', { latest: true })
|
dispatch('fetchChats', { latest: true })
|
||||||
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
|
setTimeout(() => dispatch('setNotificationsSilence', false), 10000)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
startPolling()
|
startPolling()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user mutes
|
// Get user mutes
|
||||||
store.dispatch('fetchMutes')
|
dispatch('fetchMutes')
|
||||||
|
|
||||||
store.dispatch('setLayoutWidth', windowWidth())
|
dispatch('setLayoutWidth', windowWidth())
|
||||||
store.dispatch('setLayoutHeight', windowHeight())
|
dispatch('setLayoutHeight', windowHeight())
|
||||||
|
|
||||||
// Fetch our friends
|
// Fetch our friends
|
||||||
store.rootState.api.backendInteractor.fetchFriends({ id: user.id })
|
store.rootState.api.backendInteractor.fetchFriends({ id: user.id })
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,9 @@ const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements'
|
||||||
const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
||||||
const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
||||||
|
|
||||||
|
const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
|
||||||
|
const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions'
|
||||||
|
|
||||||
const oldfetch = window.fetch
|
const oldfetch = window.fetch
|
||||||
|
|
||||||
const fetch = (url, options) => {
|
const fetch = (url, options) => {
|
||||||
|
|
@ -1659,6 +1662,58 @@ const setReportState = ({ id, state, credentials }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ADMIN STUFF // EXPERIMENTAL
|
||||||
|
const fetchInstanceDBConfig = ({ credentials }) => {
|
||||||
|
return fetch(PLEROMA_ADMIN_CONFIG_URL, {
|
||||||
|
headers: authHeaders(credentials)
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json()
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchInstanceConfigDescriptions = ({ credentials }) => {
|
||||||
|
return fetch(PLEROMA_ADMIN_DESCRIPTIONS_URL, {
|
||||||
|
headers: authHeaders(credentials)
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json()
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushInstanceDBConfig = ({ credentials, payload }) => {
|
||||||
|
return fetch(PLEROMA_ADMIN_CONFIG_URL, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...authHeaders(credentials)
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json()
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const apiService = {
|
const apiService = {
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
fetchTimeline,
|
fetchTimeline,
|
||||||
|
|
@ -1772,7 +1827,10 @@ const apiService = {
|
||||||
postAnnouncement,
|
postAnnouncement,
|
||||||
editAnnouncement,
|
editAnnouncement,
|
||||||
deleteAnnouncement,
|
deleteAnnouncement,
|
||||||
adminFetchAnnouncements
|
adminFetchAnnouncements,
|
||||||
|
fetchInstanceDBConfig,
|
||||||
|
fetchInstanceConfigDescriptions,
|
||||||
|
pushInstanceDBConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiService
|
export default apiService
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue