initial admin settings prototype (WIP)
This commit is contained in:
parent
9632b77786
commit
4d23d31fec
68
src/components/admin_modal/admin_modal.js
Normal file
68
src/components/admin_modal/admin_modal.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
|
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
|
||||||
|
import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue'
|
||||||
|
import getResettableAsyncComponent from 'src/services/resettable_async_component.js'
|
||||||
|
import Popover from '../popover/popover.vue'
|
||||||
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
newImporter,
|
||||||
|
newExporter
|
||||||
|
} from 'src/services/export_import/export_import.js'
|
||||||
|
import {
|
||||||
|
faTimes,
|
||||||
|
faFileUpload,
|
||||||
|
faFileDownload,
|
||||||
|
faChevronDown
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import {
|
||||||
|
faWindowMinimize
|
||||||
|
} from '@fortawesome/free-regular-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faTimes,
|
||||||
|
faWindowMinimize,
|
||||||
|
faFileUpload,
|
||||||
|
faFileDownload,
|
||||||
|
faChevronDown
|
||||||
|
)
|
||||||
|
|
||||||
|
const AdminModal = {
|
||||||
|
data () {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Modal,
|
||||||
|
Popover,
|
||||||
|
Checkbox,
|
||||||
|
AdminModalContent: getResettableAsyncComponent(
|
||||||
|
() => import('./admin_modal_content.vue'),
|
||||||
|
{
|
||||||
|
loadingComponent: PanelLoading,
|
||||||
|
errorComponent: AsyncComponentError,
|
||||||
|
delay: 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeModal () {
|
||||||
|
this.$store.dispatch('closeAdminModal')
|
||||||
|
},
|
||||||
|
peekModal () {
|
||||||
|
this.$store.dispatch('togglePeekAdminModal')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalActivated () {
|
||||||
|
return this.$store.state.interface.adminModalState !== 'hidden'
|
||||||
|
},
|
||||||
|
modalOpenedOnce () {
|
||||||
|
return this.$store.state.interface.adminModalLoaded
|
||||||
|
},
|
||||||
|
modalPeeked () {
|
||||||
|
return this.$store.state.interface.adminModalState === 'minimized'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdminModal
|
80
src/components/admin_modal/admin_modal.scss
Normal file
80
src/components/admin_modal/admin_modal.scss
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
@import "src/variables";
|
||||||
|
|
||||||
|
.admin-modal {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.setting-list,
|
||||||
|
.option-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 2em;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suboptions {
|
||||||
|
margin-top: 0.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-modal-panel {
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
transition-duration: 300ms;
|
||||||
|
width: 1000px;
|
||||||
|
max-width: 90vw;
|
||||||
|
height: 90vh;
|
||||||
|
|
||||||
|
@media all and (max-width: 800px) {
|
||||||
|
max-width: 100vw;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.panel-body {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
min-height: 2em;
|
||||||
|
min-width: 10em;
|
||||||
|
padding: 0 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-footer {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
>* {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-content {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.peek {
|
||||||
|
.admin-modal-panel {
|
||||||
|
/* Explanation:
|
||||||
|
* Modal is positioned vertically centered.
|
||||||
|
* 100vh - 100% = Distance between modal's top+bottom boundaries and screen
|
||||||
|
* (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen
|
||||||
|
* + 100% - we move modal completely off-screen, it's top boundary touches
|
||||||
|
* bottom of the screen
|
||||||
|
* - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible
|
||||||
|
*/
|
||||||
|
transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));
|
||||||
|
|
||||||
|
@media all and (max-width: 800px) {
|
||||||
|
/* For mobile, the modal takes 100% of the available screen.
|
||||||
|
This ensures the minimized modal is always 50px above the browser bottom
|
||||||
|
bar regardless of whether or not it is visible.
|
||||||
|
*/
|
||||||
|
transform: translateY(calc(100% - 50px));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
src/components/admin_modal/admin_modal.vue
Normal file
121
src/components/admin_modal/admin_modal.vue
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:is-open="modalActivated"
|
||||||
|
class="admin-modal"
|
||||||
|
:class="{ peek: modalPeeked }"
|
||||||
|
:no-background="modalPeeked"
|
||||||
|
>
|
||||||
|
<div class="admin-modal-panel panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<span class="title">
|
||||||
|
{{ $t('admin.settings') }}
|
||||||
|
</span>
|
||||||
|
<transition name="fade">
|
||||||
|
<div
|
||||||
|
v-if="currentSaveStateNotice"
|
||||||
|
class="alert"
|
||||||
|
:class="{ transparent: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}"
|
||||||
|
@click.prevent
|
||||||
|
>
|
||||||
|
{{ currentSaveStateNotice.error ? $t('admin.saving_err') : $t('settings.saving_ok') }}
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
:title="$t('general.peek')"
|
||||||
|
@click="peekModal"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
:icon="['far', 'window-minimize']"
|
||||||
|
fixed-width
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
:title="$t('general.close')"
|
||||||
|
@click="closeModal"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
icon="times"
|
||||||
|
fixed-width
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<AdminModalContent v-if="modalOpenedOnce" />
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer admin-footer">
|
||||||
|
<Popover
|
||||||
|
class="export"
|
||||||
|
trigger="click"
|
||||||
|
placement="top"
|
||||||
|
:offset="{ y: 5, x: 5 }"
|
||||||
|
:bound-to="{ x: 'container' }"
|
||||||
|
remove-padding
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
:title="$t('general.close')"
|
||||||
|
>
|
||||||
|
<span>{{ $t("admin.file_export_import.backup_restore") }}</span>
|
||||||
|
{{ ' ' }}
|
||||||
|
<FAIcon
|
||||||
|
icon="chevron-down"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #content="{close}">
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@click.prevent="backup"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
icon="file-download"
|
||||||
|
fixed-width
|
||||||
|
/><span>{{ $t("admin.file_export_import.backup_settings") }}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@click.prevent="backupWithTheme"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
icon="file-download"
|
||||||
|
fixed-width
|
||||||
|
/><span>{{ $t("admin.file_export_import.backup_settings_theme") }}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@click.prevent="restore"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
icon="file-upload"
|
||||||
|
fixed-width
|
||||||
|
/><span>{{ $t("admin.file_export_import.restore_settings") }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
:model-value="!!expertLevel"
|
||||||
|
@update:modelValue="expertLevel = Number($event)"
|
||||||
|
>
|
||||||
|
{{ $t("admin.expert_mode") }}
|
||||||
|
</Checkbox>
|
||||||
|
<span
|
||||||
|
id="unscrolled-content"
|
||||||
|
class="extra-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./admin_modal.js"></script>
|
||||||
|
|
||||||
|
<style src="./admin_modal.scss" lang="scss"></style>
|
88
src/components/admin_modal/admin_modal_content.js
Normal file
88
src/components/admin_modal/admin_modal_content.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
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 NotificationsTab from './tabs/notifications_tab.vue'
|
||||||
|
import FilteringTab from './tabs/filtering_tab.vue'
|
||||||
|
import SecurityTab from './tabs/security_tab/security_tab.vue'
|
||||||
|
import ProfileTab from './tabs/profile_tab.vue'
|
||||||
|
import GeneralTab from './tabs/general_tab.vue'
|
||||||
|
import VersionTab from './tabs/version_tab.vue'
|
||||||
|
import ThemeTab from './tabs/theme_tab/theme_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 AdminModalContent = {
|
||||||
|
components: {
|
||||||
|
TabSwitcher,
|
||||||
|
|
||||||
|
DataImportExportTab,
|
||||||
|
MutesAndBlocksTab,
|
||||||
|
NotificationsTab,
|
||||||
|
FilteringTab,
|
||||||
|
SecurityTab,
|
||||||
|
ProfileTab,
|
||||||
|
GeneralTab,
|
||||||
|
VersionTab,
|
||||||
|
ThemeTab
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoggedIn () {
|
||||||
|
return !!this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
open () {
|
||||||
|
return this.$store.state.interface.AdminModalState !== 'hidden'
|
||||||
|
},
|
||||||
|
bodyLock () {
|
||||||
|
return this.$store.state.interface.AdminModalState === 'visible'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onOpen () {
|
||||||
|
const targetTab = this.$store.state.interface.AdminModalTargetTab
|
||||||
|
// 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 Admin is opened
|
||||||
|
// it doesn't force it.
|
||||||
|
this.$store.dispatch('clearAdminModalTargetTab')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.onOpen()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
open: function (value) {
|
||||||
|
if (value) this.onOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdminModalContent
|
56
src/components/admin_modal/admin_modal_content.scss
Normal file
56
src/components/admin_modal/admin_modal_content.scss
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
@import "src/variables";
|
||||||
|
|
||||||
|
.admin_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/components/admin_modal/tabs/general_tab.js
Normal file
33
src/components/admin_modal/tabs/general_tab.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import BooleanSetting from '../settings_modal/helpers/boolean_setting.vue'
|
||||||
|
import ChoiceSetting from '../settings_modal/helpers/choice_setting.vue'
|
||||||
|
import IntegerSetting from '../settings_modal/helpers/integer_setting.vue'
|
||||||
|
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faGlobe
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faGlobe
|
||||||
|
)
|
||||||
|
|
||||||
|
const GeneralTab = {
|
||||||
|
components: {
|
||||||
|
BooleanSetting,
|
||||||
|
ChoiceSetting,
|
||||||
|
IntegerSetting,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
mergedConfig () {
|
||||||
|
console.log(this.$store.state)
|
||||||
|
return this.$store.state
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeDefaultScope (value) {
|
||||||
|
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GeneralTab
|
@ -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"
|
||||||
|
@ -153,9 +153,9 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled expand-icon"
|
class="button-unstyled expand-icon"
|
||||||
@click.prevent="toggleStatusExpanded"
|
|
||||||
:title="$t('tool_tip.toggle_expand')"
|
:title="$t('tool_tip.toggle_expand')"
|
||||||
:aria-expanded="statusExpanded"
|
:aria-expanded="statusExpanded"
|
||||||
|
@click.prevent="toggleStatusExpanded"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="fa-scale-110"
|
class="fa-scale-110"
|
||||||
|
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
|
35
src/components/settings_modal/admin_tabs/instance_tab.vue
Normal file
35
src/components/settings_modal/admin_tabs/instance_tab.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div :label="$t('settings.general')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.instance') }}</h2>
|
||||||
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
<StringSetting source="admin" path=":pleroma.:instance.:name">
|
||||||
|
NAME
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting source="admin" path=":pleroma.:instance.:description">
|
||||||
|
DESCRIPTION
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./instance_tab.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.column-settings {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-settings .size-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -42,6 +42,8 @@ export default {
|
|||||||
switch (this.source) {
|
switch (this.source) {
|
||||||
case 'profile':
|
case 'profile':
|
||||||
return this.$store.state.profileConfig
|
return this.$store.state.profileConfig
|
||||||
|
case 'admin':
|
||||||
|
return this.$store.state.adminSettings.config
|
||||||
default:
|
default:
|
||||||
return this.$store.getters.mergedConfig
|
return this.$store.getters.mergedConfig
|
||||||
}
|
}
|
||||||
@ -50,6 +52,8 @@ export default {
|
|||||||
switch (this.source) {
|
switch (this.source) {
|
||||||
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':
|
||||||
|
return (k, v) => console.log(this.path, k, 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 })
|
||||||
}
|
}
|
||||||
@ -66,7 +70,15 @@ export default {
|
|||||||
return this.source === 'profile'
|
return this.source === 'profile'
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return !this.source === 'default' && this.state !== this.defaultState
|
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)
|
||||||
|
default:
|
||||||
|
return this.state !== this.defaultState
|
||||||
|
}
|
||||||
},
|
},
|
||||||
matchesExpertLevel () {
|
matchesExpertLevel () {
|
||||||
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
|
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
|
||||||
|
9
src/components/settings_modal/helpers/string_setting.js
Normal file
9
src/components/settings_modal/helpers/string_setting.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import ModifiedIndicator from './modified_indicator.vue'
|
||||||
|
import Setting from './setting.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ModifiedIndicator
|
||||||
|
},
|
||||||
|
...Setting
|
||||||
|
}
|
25
src/components/settings_modal/helpers/string_setting.vue
Normal file
25
src/components/settings_modal/helpers/string_setting.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<label
|
||||||
|
v-if="matchesExpertLevel"
|
||||||
|
class="StringSetting"
|
||||||
|
>
|
||||||
|
<label :for="path">
|
||||||
|
<slot />
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
:id="path"
|
||||||
|
class="string-input"
|
||||||
|
step="1"
|
||||||
|
:disabled="disabled"
|
||||||
|
:value="state"
|
||||||
|
@change="update"
|
||||||
|
>
|
||||||
|
{{ ' ' }}
|
||||||
|
<ModifiedIndicator
|
||||||
|
:changed="isChanged"
|
||||||
|
:onclick="reset"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./boolean_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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,76 @@
|
|||||||
|
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 { 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
|
||||||
|
},
|
||||||
|
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,21 @@
|
|||||||
|
<template>
|
||||||
|
<tab-switcher
|
||||||
|
ref="tabSwitcher"
|
||||||
|
class="settings_tab-switcher"
|
||||||
|
:side-tab-bar="true"
|
||||||
|
:scrollable-tabs="true"
|
||||||
|
:body-scroll-lock="bodyLock"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:label="$t('settings.general')"
|
||||||
|
icon="wrench"
|
||||||
|
data-tab-name="general"
|
||||||
|
>
|
||||||
|
<InstanceTab />
|
||||||
|
</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>
|
@ -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,7 +10,7 @@ export const newUserFlags = {
|
|||||||
...defaultState.flagStorage
|
...defaultState.flagStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverSideStorage = {
|
const adminSettingsStorage = {
|
||||||
state: {
|
state: {
|
||||||
...cloneDeep(defaultState)
|
...cloneDeep(defaultState)
|
||||||
},
|
},
|
||||||
@ -40,9 +40,10 @@ const serverSideStorage = {
|
|||||||
}
|
}
|
||||||
set(config, path, convert(c.value))
|
set(config, path, convert(c.value))
|
||||||
})
|
})
|
||||||
|
console.log(config)
|
||||||
commit('updateAdminSettings', { config, modifiedPaths })
|
commit('updateAdminSettings', { config, modifiedPaths })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default serverSideStorage
|
export default adminSettingsStorage
|
||||||
|
@ -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')
|
||||||
|
Loading…
Reference in New Issue
Block a user