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> <template>
<div :label="$t('settings.general')"> <div :label="$t('admin_dash.instance')">
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.instance') }}</h2> <h2>{{ $t('admin_dash.instance') }}</h2>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<StringSetting source="admin" path=":pleroma.:instance.:name"> <StringSetting
source="admin"
path=":pleroma.:instance.:name"
draft-mode
>
NAME NAME
</StringSetting> </StringSetting>
</li> </li>
<li> <li>
<StringSetting source="admin" path=":pleroma.:instance.:description"> <StringSetting
source="admin"
path=":pleroma.:instance.:description"
draft-mode
>
DESCRIPTION DESCRIPTION
</StringSetting> </StringSetting>
</li> </li>
<li>
<IntegerSetting
source="admin"
path=":pleroma.:instance.:limit"
draft-mode
>
POST LIMIT
</IntegerSetting>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -1,13 +1,16 @@
import Checkbox from 'src/components/checkbox/checkbox.vue' 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' import Setting from './setting.js'
export default { export default {
...Setting,
components: { components: {
Checkbox, ...Setting.components,
ModifiedIndicator, Checkbox
ProfileSettingIndicator
}, },
...Setting methods: {
...Setting.methods,
getValue (e) {
return e
}
}
} }

View File

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

View File

@ -1,15 +1,12 @@
import Select from 'src/components/select/select.vue' 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' import Setting from './setting.js'
export default { export default {
components: {
Select,
ModifiedIndicator,
ProfileSettingIndicator
},
...Setting, ...Setting,
components: {
...Setting.components,
Select
},
props: { props: {
...Setting.props, ...Setting.props,
options: { 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' import Setting from './setting.js'
export default { export default {
components: {
ModifiedIndicator
},
...Setting, ...Setting,
methods: { methods: {
...Setting.methods, ...Setting.methods,
update (e) { getValue (e) {
this.configSink(this.path, parseInt(e.target.value)) return parseInt(e.target.value)
} }
} }
} }

View File

@ -13,7 +13,7 @@
step="1" step="1"
:disabled="disabled" :disabled="disabled"
:min="min || 0" :min="min || 0"
:value="state" :value="draftMode ? draft :state"
@change="update" @change="update"
> >
{{ ' ' }} {{ ' ' }}
@ -21,6 +21,8 @@
:changed="isChanged" :changed="isChanged"
:onclick="reset" :onclick="reset"
/> />
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</span> </span>
</template> </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' import { get, set } from 'lodash'
export default { export default {
components: {
Checkbox,
ModifiedIndicator,
DraftButtons,
ProfileSettingIndicator
},
props: { props: {
path: { path: {
type: String, type: String,
@ -23,6 +34,20 @@ export default {
source: { source: {
type: String, type: String,
default: 'default' default: 'default'
},
draftMode: {
type: Boolean,
default: false
}
},
data () {
return {
draft: null
}
},
created () {
if (this.draftMode) {
this.draft = this.state
} }
}, },
computed: { computed: {
@ -53,7 +78,7 @@ export default {
case 'profile': case 'profile':
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v }) return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
case 'admin': case 'admin':
return (k, v) => console.log(this.path, k, v) return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v })
default: default:
return (k, v) => this.$store.dispatch('setOption', { name: k, value: v }) return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
} }
@ -72,25 +97,56 @@ export default {
isChanged () { isChanged () {
switch (this.source) { switch (this.source) {
case 'profile': case 'profile':
return false
case 'admin': case 'admin':
console.log(this.$store.state.adminSettings.modifiedPaths) return false
return this.$store.state.adminSettings.modifiedPaths.has(this.path)
default: default:
return this.state !== this.defaultState 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 () { matchesExpertLevel () {
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0 return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
} }
}, },
methods: { methods: {
getValue (e) {
return e.target.value
},
update (e) { update (e) {
console.log('U', this.path, e) if (this.draftMode) {
this.configSink(this.path, e) this.draft = this.getValue(e)
} else {
this.configSink(this.path, this.getValue(e))
}
},
commitDraft () {
if (this.draftMode) {
this.configSink(this.path, this.draft)
}
}, },
reset () { 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) 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 Select from 'src/components/select/select.vue'
import Setting from './setting.js' import Setting from './setting.js'
@ -7,11 +6,11 @@ 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
}, },
...Setting,
props: { props: {
...Setting.props, ...Setting.props,
min: Number, min: Number,

View File

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

View File

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

View File

@ -22,8 +22,8 @@ const adminSettingsStorage = {
}, },
actions: { actions: {
setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) { setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) {
const config = {} const config = state.config || {}
const modifiedPaths = new Set() const modifiedPaths = state.modifiedPaths || new Set()
backendDbConfig.configs.forEach(c => { backendDbConfig.configs.forEach(c => {
const path = c.group + '.' + c.key const path = c.group + '.' + c.key
if (c.db) { if (c.db) {
@ -40,8 +40,54 @@ const adminSettingsStorage = {
} }
set(config, path, convert(c.value)) set(config, path, convert(c.value))
}) })
console.log(config)
commit('updateAdminSettings', { config, modifiedPaths }) 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 = [] user.domainMutes = []
commit('setCurrentUser', user) commit('setCurrentUser', user)
commit('setServerSideStorage', user) commit('setServerSideStorage', user)
if (user.rights.moderator || user.rights.admin) { if (user.rights.admin) {
store.rootState.api.backendInteractor.fetchInstanceDBConfig() store.rootState.api.backendInteractor.fetchInstanceDBConfig()
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig })) .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_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/v1/pleroma/admin/config' const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
const oldfetch = window.fetch 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 = { const apiService = {
verifyCredentials, verifyCredentials,
fetchTimeline, fetchTimeline,
@ -1791,7 +1812,8 @@ const apiService = {
editAnnouncement, editAnnouncement,
deleteAnnouncement, deleteAnnouncement,
adminFetchAnnouncements, adminFetchAnnouncements,
fetchInstanceDBConfig fetchInstanceDBConfig,
pushInstanceDBConfig
} }
export default apiService export default apiService