setting admin settings works now. also now we have draftable settings

This commit is contained in:
Henry Jameson 2023-03-16 23:18:55 +02:00
parent 4d23d31fec
commit bfd802ad04
14 changed files with 285 additions and 46 deletions

View File

@ -1,18 +1,35 @@
<template>
<div :label="$t('settings.general')">
<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">
<StringSetting
source="admin"
path=":pleroma.:instance.:name"
draft-mode
>
NAME
</StringSetting>
</li>
<li>
<StringSetting source="admin" path=":pleroma.:instance.:description">
<StringSetting
source="admin"
path=":pleroma.:instance.:description"
draft-mode
>
DESCRIPTION
</StringSetting>
</li>
<li>
<IntegerSetting
source="admin"
path=":pleroma.:instance.:limit"
draft-mode
>
POST LIMIT
</IntegerSetting>
</li>
</ul>
</div>
</div>

View File

@ -1,13 +1,16 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import ModifiedIndicator from './modified_indicator.vue'
import ProfileSettingIndicator from './profile_setting_indicator.vue'
import Setting from './setting.js'
export default {
...Setting,
components: {
Checkbox,
ModifiedIndicator,
ProfileSettingIndicator
...Setting.components,
Checkbox
},
...Setting
methods: {
...Setting.methods,
getValue (e) {
return e
}
}
}

View File

@ -4,7 +4,7 @@
class="BooleanSetting"
>
<Checkbox
:model-value="state"
:model-value="draftMode ? draft :state"
:disabled="shouldBeDisabled"
@update:modelValue="update"
>
@ -20,6 +20,7 @@
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</Checkbox>
</label>
</template>

View File

@ -1,15 +1,12 @@
import Select from 'src/components/select/select.vue'
import ModifiedIndicator from './modified_indicator.vue'
import ProfileSettingIndicator from './profile_setting_indicator.vue'
import Setting from './setting.js'
export default {
components: {
Select,
ModifiedIndicator,
ProfileSettingIndicator
},
...Setting,
components: {
...Setting.components,
Select
},
props: {
...Setting.props,
options: {

View 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>
&nbsp;
<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>
&nbsp;
<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>
&nbsp;
<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>

View File

@ -1,15 +1,11 @@
import ModifiedIndicator from './modified_indicator.vue'
import Setting from './setting.js'
export default {
components: {
ModifiedIndicator
},
...Setting,
methods: {
...Setting.methods,
update (e) {
this.configSink(this.path, parseInt(e.target.value))
getValue (e) {
return parseInt(e.target.value)
}
}
}

View File

@ -13,7 +13,7 @@
step="1"
:disabled="disabled"
:min="min || 0"
:value="state"
:value="draftMode ? draft :state"
@change="update"
>
{{ ' ' }}
@ -21,6 +21,8 @@
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</span>
</template>

View File

@ -1,5 +1,16 @@
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,
@ -23,6 +34,20 @@ export default {
source: {
type: String,
default: 'default'
},
draftMode: {
type: Boolean,
default: false
}
},
data () {
return {
draft: null
}
},
created () {
if (this.draftMode) {
this.draft = this.state
}
},
computed: {
@ -53,7 +78,7 @@ export default {
case 'profile':
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
case 'admin':
return (k, v) => console.log(this.path, k, v)
return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v })
default:
return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
}
@ -72,25 +97,56 @@ export default {
isChanged () {
switch (this.source) {
case 'profile':
return false
case 'admin':
console.log(this.$store.state.adminSettings.modifiedPaths)
return this.$store.state.adminSettings.modifiedPaths.has(this.path)
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) {
console.log('U', this.path, e)
this.configSink(this.path, 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!')
}
}
}
}

View File

@ -1,4 +1,3 @@
import ModifiedIndicator from './modified_indicator.vue'
import Select from 'src/components/select/select.vue'
import Setting from './setting.js'
@ -7,11 +6,11 @@ export const defaultHorizontalUnits = ['px', 'rem', 'vw']
export const defaultVerticalUnits = ['px', 'rem', 'vh']
export default {
...Setting,
components: {
ModifiedIndicator,
...Setting.components,
Select
},
...Setting,
props: {
...Setting.props,
min: Number,

View File

@ -1,9 +1,5 @@
import ModifiedIndicator from './modified_indicator.vue'
import Setting from './setting.js'
export default {
components: {
ModifiedIndicator
},
...Setting
}

View File

@ -11,7 +11,7 @@
class="string-input"
step="1"
:disabled="disabled"
:value="state"
:value="draftMode ? draft :state"
@change="update"
>
{{ ' ' }}
@ -19,7 +19,9 @@
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</label>
</template>
<script src="./boolean_setting.js"></script>
<script src="./string_setting.js"></script>

View File

@ -22,8 +22,8 @@ const adminSettingsStorage = {
},
actions: {
setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) {
const config = {}
const modifiedPaths = new Set()
const config = state.config || {}
const modifiedPaths = state.modifiedPaths || new Set()
backendDbConfig.configs.forEach(c => {
const path = c.group + '.' + c.key
if (c.db) {
@ -40,8 +40,54 @@ const adminSettingsStorage = {
}
set(config, path, convert(c.value))
})
console.log(config)
commit('updateAdminSettings', { config, modifiedPaths })
},
pushAdminSetting ({ rootState, state, commit, dispatch }, { path, value }) {
const [group, key, ...rest] = path.split(/\./g)
const clone = {} // not actually cloning the entire thing to avoid excessive writes
set(clone, rest.join('.'), 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 }) {
console.log('ASS')
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 }))
}
}
}

View File

@ -564,7 +564,7 @@ const users = {
user.domainMutes = []
commit('setCurrentUser', user)
commit('setServerSideStorage', user)
if (user.rights.moderator || user.rights.admin) {
if (user.rights.admin) {
store.rootState.api.backendInteractor.fetchInstanceDBConfig()
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
}

View File

@ -108,7 +108,7 @@ const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements'
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_ADMIN_CONFIG_URL = '/api/v1/pleroma/admin/config'
const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
const oldfetch = window.fetch
@ -1677,6 +1677,27 @@ const fetchInstanceDBConfig = ({ credentials }) => {
})
}
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 = {
verifyCredentials,
fetchTimeline,
@ -1791,7 +1812,8 @@ const apiService = {
editAnnouncement,
deleteAnnouncement,
adminFetchAnnouncements,
fetchInstanceDBConfig
fetchInstanceDBConfig,
pushInstanceDBConfig
}
export default apiService