From 1a852b96eff060c8eaab9477c85eb4a3bd91c748 Mon Sep 17 00:00:00 2001
From: tusooa <tusooa@kazv.moe>
Date: Thu, 2 Mar 2023 21:15:43 -0500
Subject: Give tab switcher a role

---
 src/components/tab_switcher/tab_switcher.jsx | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

(limited to 'src/components/tab_switcher/tab_switcher.jsx')

diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx
index c8d390bc..a7ef8560 100644
--- a/src/components/tab_switcher/tab_switcher.jsx
+++ b/src/components/tab_switcher/tab_switcher.jsx
@@ -117,6 +117,7 @@ export default {
                 onClick={this.clickTab(index)}
                 class={classesTab.join(' ')}
                 type="button"
+                role="tab"
               >
                 <img src={props.image} title={props['image-tooltip']}/>
                 {props.label ? '' : props.label}
@@ -131,6 +132,7 @@ export default {
               onClick={this.clickTab(index)}
               class={classesTab.join(' ')}
               type="button"
+              role="tab"
             >
               {!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)}
               <span class="text">
@@ -167,11 +169,15 @@ export default {
 
     return (
       <div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}>
-        <div class="tabs">
+        <div
+          class="tabs"
+          role="tablist"
+        >
           {tabs}
         </div>
         <div
           ref="contents"
+          role="tabpanel"
           class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
           v-body-scroll-lock={this.bodyScrollLock}
         >
-- 
cgit v1.2.3-70-g09d2


From 4d23d31fecf480abfccc4db3ac79c6640078dc3b Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 14 Mar 2023 21:50:43 +0200
Subject: initial admin settings prototype (WIP)

---
 src/components/admin_modal/admin_modal.js          |  68 ++++++++++++
 src/components/admin_modal/admin_modal.scss        |  80 ++++++++++++++
 src/components/admin_modal/admin_modal.vue         | 121 +++++++++++++++++++++
 src/components/admin_modal/admin_modal_content.js  |  88 +++++++++++++++
 .../admin_modal/admin_modal_content.scss           |  56 ++++++++++
 src/components/admin_modal/tabs/general_tab.js     |  33 ++++++
 src/components/desktop_nav/desktop_nav.js          |   5 +-
 src/components/desktop_nav/desktop_nav.vue         |   9 +-
 src/components/notification/notification.vue       |   2 +-
 .../settings_modal/admin_tabs/instance_tab.js      |  29 +++++
 .../settings_modal/admin_tabs/instance_tab.vue     |  35 ++++++
 src/components/settings_modal/helpers/setting.js   |  14 ++-
 .../settings_modal/helpers/string_setting.js       |   9 ++
 .../settings_modal/helpers/string_setting.vue      |  25 +++++
 src/components/settings_modal/settings_modal.js    |  23 +++-
 src/components/settings_modal/settings_modal.vue   |   3 +-
 .../settings_modal/settings_modal_admin_content.js |  76 +++++++++++++
 .../settings_modal_admin_content.scss              |  56 ++++++++++
 .../settings_modal_admin_content.vue               |  21 ++++
 .../settings_modal/settings_modal_content.js       |  88 ---------------
 .../settings_modal/settings_modal_content.scss     |  56 ----------
 .../settings_modal/settings_modal_content.vue      |  83 --------------
 .../settings_modal/settings_modal_user_content.js  |  88 +++++++++++++++
 .../settings_modal_user_content.scss               |  56 ++++++++++
 .../settings_modal/settings_modal_user_content.vue |  83 ++++++++++++++
 src/components/tab_switcher/tab_switcher.jsx       |   8 +-
 src/modules/adminSettings.js                       |   5 +-
 src/modules/interface.js                           |  21 +++-
 28 files changed, 985 insertions(+), 256 deletions(-)
 create mode 100644 src/components/admin_modal/admin_modal.js
 create mode 100644 src/components/admin_modal/admin_modal.scss
 create mode 100644 src/components/admin_modal/admin_modal.vue
 create mode 100644 src/components/admin_modal/admin_modal_content.js
 create mode 100644 src/components/admin_modal/admin_modal_content.scss
 create mode 100644 src/components/admin_modal/tabs/general_tab.js
 create mode 100644 src/components/settings_modal/admin_tabs/instance_tab.js
 create mode 100644 src/components/settings_modal/admin_tabs/instance_tab.vue
 create mode 100644 src/components/settings_modal/helpers/string_setting.js
 create mode 100644 src/components/settings_modal/helpers/string_setting.vue
 create mode 100644 src/components/settings_modal/settings_modal_admin_content.js
 create mode 100644 src/components/settings_modal/settings_modal_admin_content.scss
 create mode 100644 src/components/settings_modal/settings_modal_admin_content.vue
 delete mode 100644 src/components/settings_modal/settings_modal_content.js
 delete mode 100644 src/components/settings_modal/settings_modal_content.scss
 delete mode 100644 src/components/settings_modal/settings_modal_content.vue
 create mode 100644 src/components/settings_modal/settings_modal_user_content.js
 create mode 100644 src/components/settings_modal/settings_modal_user_content.scss
 create mode 100644 src/components/settings_modal/settings_modal_user_content.vue

(limited to 'src/components/tab_switcher/tab_switcher.jsx')

diff --git a/src/components/admin_modal/admin_modal.js b/src/components/admin_modal/admin_modal.js
new file mode 100644
index 00000000..525f09aa
--- /dev/null
+++ b/src/components/admin_modal/admin_modal.js
@@ -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
diff --git a/src/components/admin_modal/admin_modal.scss b/src/components/admin_modal/admin_modal.scss
new file mode 100644
index 00000000..0d916f32
--- /dev/null
+++ b/src/components/admin_modal/admin_modal.scss
@@ -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));
+      }
+    }
+  }
+}
diff --git a/src/components/admin_modal/admin_modal.vue b/src/components/admin_modal/admin_modal.vue
new file mode 100644
index 00000000..d7e5a80f
--- /dev/null
+++ b/src/components/admin_modal/admin_modal.vue
@@ -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>
diff --git a/src/components/admin_modal/admin_modal_content.js b/src/components/admin_modal/admin_modal_content.js
new file mode 100644
index 00000000..897cc163
--- /dev/null
+++ b/src/components/admin_modal/admin_modal_content.js
@@ -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
diff --git a/src/components/admin_modal/admin_modal_content.scss b/src/components/admin_modal/admin_modal_content.scss
new file mode 100644
index 00000000..2db7b2f8
--- /dev/null
+++ b/src/components/admin_modal/admin_modal_content.scss
@@ -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;
+    }
+  }
+}
diff --git a/src/components/admin_modal/tabs/general_tab.js b/src/components/admin_modal/tabs/general_tab.js
new file mode 100644
index 00000000..8c166f19
--- /dev/null
+++ b/src/components/admin_modal/tabs/general_tab.js
@@ -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
diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js
index 745b1a81..f6a2e294 100644
--- a/src/components/desktop_nav/desktop_nav.js
+++ b/src/components/desktop_nav/desktop_nav.js
@@ -107,7 +107,10 @@ export default {
       this.searchBarHidden = hidden
     },
     openSettingsModal () {
-      this.$store.dispatch('openSettingsModal')
+      this.$store.dispatch('openSettingsModal', 'user')
+    },
+    openAdminModal () {
+      this.$store.dispatch('openSettingsModal', 'admin')
     }
   }
 }
diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue
index dc8bbfd3..49382f8e 100644
--- a/src/components/desktop_nav/desktop_nav.vue
+++ b/src/components/desktop_nav/desktop_nav.vue
@@ -48,20 +48,19 @@
             icon="cog"
           />
         </button>
-        <a
+        <button
           v-if="currentUser && currentUser.role === 'admin'"
-          href="/pleroma/admin/#/login-pleroma"
-          class="nav-icon"
+          class="button-unstyled nav-icon"
           target="_blank"
           :title="$t('nav.administration')"
-          @click.stop
+          @click.stop="openAdminModal"
         >
           <FAIcon
             fixed-width
             class="fa-scale-110 fa-old-padding"
             icon="tachometer-alt"
           />
-        </a>
+        </button>
         <span class="spacer" />
         <button
           v-if="currentUser"
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index e1ea42ad..8d4cc2d1 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -153,9 +153,9 @@
             </router-link>
             <button
               class="button-unstyled expand-icon"
-              @click.prevent="toggleStatusExpanded"
               :title="$t('tool_tip.toggle_expand')"
               :aria-expanded="statusExpanded"
+              @click.prevent="toggleStatusExpanded"
             >
               <FAIcon
                 class="fa-scale-110"
diff --git a/src/components/settings_modal/admin_tabs/instance_tab.js b/src/components/settings_modal/admin_tabs/instance_tab.js
new file mode 100644
index 00000000..4a261659
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/instance_tab.js
@@ -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
diff --git a/src/components/settings_modal/admin_tabs/instance_tab.vue b/src/components/settings_modal/admin_tabs/instance_tab.vue
new file mode 100644
index 00000000..65b3d12f
--- /dev/null
+++ b/src/components/settings_modal/admin_tabs/instance_tab.vue
@@ -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>
diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js
index a766e6dc..9195d3e9 100644
--- a/src/components/settings_modal/helpers/setting.js
+++ b/src/components/settings_modal/helpers/setting.js
@@ -42,6 +42,8 @@ export default {
       switch (this.source) {
         case 'profile':
           return this.$store.state.profileConfig
+        case 'admin':
+          return this.$store.state.adminSettings.config
         default:
           return this.$store.getters.mergedConfig
       }
@@ -50,6 +52,8 @@ export default {
       switch (this.source) {
         case 'profile':
           return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
+        case 'admin':
+          return (k, v) => console.log(this.path, k, v)
         default:
           return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
       }
@@ -66,7 +70,15 @@ export default {
       return this.source === 'profile'
     },
     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 () {
       return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
diff --git a/src/components/settings_modal/helpers/string_setting.js b/src/components/settings_modal/helpers/string_setting.js
new file mode 100644
index 00000000..64f8772d
--- /dev/null
+++ b/src/components/settings_modal/helpers/string_setting.js
@@ -0,0 +1,9 @@
+import ModifiedIndicator from './modified_indicator.vue'
+import Setting from './setting.js'
+
+export default {
+  components: {
+    ModifiedIndicator
+  },
+  ...Setting
+}
diff --git a/src/components/settings_modal/helpers/string_setting.vue b/src/components/settings_modal/helpers/string_setting.vue
new file mode 100644
index 00000000..e4bd2de9
--- /dev/null
+++ b/src/components/settings_modal/helpers/string_setting.vue
@@ -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>
diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js
index 0a72dca1..e033d999 100644
--- a/src/components/settings_modal/settings_modal.js
+++ b/src/components/settings_modal/settings_modal.js
@@ -53,8 +53,16 @@ const SettingsModal = {
     Modal,
     Popover,
     Checkbox,
-    SettingsModalContent: getResettableAsyncComponent(
-      () => import('./settings_modal_content.vue'),
+    SettingsModalUserContent: getResettableAsyncComponent(
+      () => import('./settings_modal_user_content.vue'),
+      {
+        loadingComponent: PanelLoading,
+        errorComponent: AsyncComponentError,
+        delay: 0
+      }
+    ),
+    SettingsModalAdminContent: getResettableAsyncComponent(
+      () => import('./settings_modal_admin_content.vue'),
       {
         loadingComponent: PanelLoading,
         errorComponent: AsyncComponentError,
@@ -156,8 +164,14 @@ const SettingsModal = {
     modalActivated () {
       return this.$store.state.interface.settingsModalState !== 'hidden'
     },
-    modalOpenedOnce () {
-      return this.$store.state.interface.settingsModalLoaded
+    modalMode () {
+      return this.$store.state.interface.settingsModalMode
+    },
+    modalOpenedOnceUser () {
+      return this.$store.state.interface.settingsModalLoadedUser
+    },
+    modalOpenedOnceAdmin () {
+      return this.$store.state.interface.settingsModalLoadedAdmin
     },
     modalPeeked () {
       return this.$store.state.interface.settingsModalState === 'minimized'
@@ -167,7 +181,6 @@ const SettingsModal = {
         return this.$store.state.config.expertLevel > 0
       },
       set (value) {
-        console.log(value)
         this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
       }
     }
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index 7b457371..73e1524a 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -42,7 +42,8 @@
         </button>
       </div>
       <div class="panel-body">
-        <SettingsModalContent v-if="modalOpenedOnce" />
+        <SettingsModalUserContent v-if="modalMode === 'user' && modalOpenedOnceUser" />
+        <SettingsModalAdminContent v-if="modalMode === 'admin' && modalOpenedOnceAdmin" />
       </div>
       <div class="panel-footer settings-footer">
         <Popover
diff --git a/src/components/settings_modal/settings_modal_admin_content.js b/src/components/settings_modal/settings_modal_admin_content.js
new file mode 100644
index 00000000..88ba1755
--- /dev/null
+++ b/src/components/settings_modal/settings_modal_admin_content.js
@@ -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
diff --git a/src/components/settings_modal/settings_modal_admin_content.scss b/src/components/settings_modal/settings_modal_admin_content.scss
new file mode 100644
index 00000000..87df7982
--- /dev/null
+++ b/src/components/settings_modal/settings_modal_admin_content.scss
@@ -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;
+    }
+  }
+}
diff --git a/src/components/settings_modal/settings_modal_admin_content.vue b/src/components/settings_modal/settings_modal_admin_content.vue
new file mode 100644
index 00000000..9873b127
--- /dev/null
+++ b/src/components/settings_modal/settings_modal_admin_content.vue
@@ -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>
diff --git a/src/components/settings_modal/settings_modal_content.js b/src/components/settings_modal/settings_modal_content.js
deleted file mode 100644
index 9ac0301f..00000000
--- a/src/components/settings_modal/settings_modal_content.js
+++ /dev/null
@@ -1,88 +0,0 @@
-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 SettingsModalContent = {
-  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.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 SettingsModalContent
diff --git a/src/components/settings_modal/settings_modal_content.scss b/src/components/settings_modal/settings_modal_content.scss
deleted file mode 100644
index 87df7982..00000000
--- a/src/components/settings_modal/settings_modal_content.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-@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;
-    }
-  }
-}
diff --git a/src/components/settings_modal/settings_modal_content.vue b/src/components/settings_modal/settings_modal_content.vue
deleted file mode 100644
index 0be76d22..00000000
--- a/src/components/settings_modal/settings_modal_content.vue
+++ /dev/null
@@ -1,83 +0,0 @@
-<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"
-    >
-      <GeneralTab />
-    </div>
-    <div
-      v-if="isLoggedIn"
-      :label="$t('settings.profile_tab')"
-      icon="user"
-      data-tab-name="profile"
-    >
-      <ProfileTab />
-    </div>
-    <div
-      v-if="isLoggedIn"
-      :label="$t('settings.security_tab')"
-      icon="lock"
-      data-tab-name="security"
-    >
-      <SecurityTab />
-    </div>
-    <div
-      :label="$t('settings.filtering')"
-      icon="filter"
-      data-tab-name="filtering"
-    >
-      <FilteringTab />
-    </div>
-    <div
-      :label="$t('settings.theme')"
-      icon="paint-brush"
-      data-tab-name="theme"
-    >
-      <ThemeTab />
-    </div>
-    <div
-      v-if="isLoggedIn"
-      :label="$t('settings.notifications')"
-      icon="bell"
-      data-tab-name="notifications"
-    >
-      <NotificationsTab />
-    </div>
-    <div
-      v-if="isLoggedIn"
-      :label="$t('settings.data_import_export_tab')"
-      icon="download"
-      data-tab-name="dataImportExport"
-    >
-      <DataImportExportTab />
-    </div>
-    <div
-      v-if="isLoggedIn"
-      :label="$t('settings.mutes_and_blocks')"
-      :fullHeight="true"
-      icon="eye-slash"
-      data-tab-name="mutesAndBlocks"
-    >
-      <MutesAndBlocksTab />
-    </div>
-    <div
-      :label="$t('settings.version.title')"
-      icon="info"
-      data-tab-name="version"
-    >
-      <VersionTab />
-    </div>
-  </tab-switcher>
-</template>
-
-<script src="./settings_modal_content.js"></script>
-
-<style src="./settings_modal_content.scss" lang="scss"></style>
diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js
new file mode 100644
index 00000000..9ac0301f
--- /dev/null
+++ b/src/components/settings_modal/settings_modal_user_content.js
@@ -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 SettingsModalContent = {
+  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.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 SettingsModalContent
diff --git a/src/components/settings_modal/settings_modal_user_content.scss b/src/components/settings_modal/settings_modal_user_content.scss
new file mode 100644
index 00000000..87df7982
--- /dev/null
+++ b/src/components/settings_modal/settings_modal_user_content.scss
@@ -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;
+    }
+  }
+}
diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue
new file mode 100644
index 00000000..0221cccb
--- /dev/null
+++ b/src/components/settings_modal/settings_modal_user_content.vue
@@ -0,0 +1,83 @@
+<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"
+    >
+      <GeneralTab />
+    </div>
+    <div
+      v-if="isLoggedIn"
+      :label="$t('settings.profile_tab')"
+      icon="user"
+      data-tab-name="profile"
+    >
+      <ProfileTab />
+    </div>
+    <div
+      v-if="isLoggedIn"
+      :label="$t('settings.security_tab')"
+      icon="lock"
+      data-tab-name="security"
+    >
+      <SecurityTab />
+    </div>
+    <div
+      :label="$t('settings.filtering')"
+      icon="filter"
+      data-tab-name="filtering"
+    >
+      <FilteringTab />
+    </div>
+    <div
+      :label="$t('settings.theme')"
+      icon="paint-brush"
+      data-tab-name="theme"
+    >
+      <ThemeTab />
+    </div>
+    <div
+      v-if="isLoggedIn"
+      :label="$t('settings.notifications')"
+      icon="bell"
+      data-tab-name="notifications"
+    >
+      <NotificationsTab />
+    </div>
+    <div
+      v-if="isLoggedIn"
+      :label="$t('settings.data_import_export_tab')"
+      icon="download"
+      data-tab-name="dataImportExport"
+    >
+      <DataImportExportTab />
+    </div>
+    <div
+      v-if="isLoggedIn"
+      :label="$t('settings.mutes_and_blocks')"
+      :fullHeight="true"
+      icon="eye-slash"
+      data-tab-name="mutesAndBlocks"
+    >
+      <MutesAndBlocksTab />
+    </div>
+    <div
+      :label="$t('settings.version.title')"
+      icon="info"
+      data-tab-name="version"
+    >
+      <VersionTab />
+    </div>
+  </tab-switcher>
+</template>
+
+<script src="./settings_modal_user_content.js"></script>
+
+<style src="./settings_modal_user_content.scss" lang="scss"></style>
diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx
index a7ef8560..b444da43 100644
--- a/src/components/tab_switcher/tab_switcher.jsx
+++ b/src/components/tab_switcher/tab_switcher.jsx
@@ -60,13 +60,7 @@ export default {
         const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
         return this.$slots.default().findIndex(isWanted) === this.activeIndex
       }
-    },
-    settingsModalVisible () {
-      return this.settingsModalState === 'visible'
-    },
-    ...mapState({
-      settingsModalState: state => state.interface.settingsModalState
-    })
+    }
   },
   beforeUpdate () {
     const currentSlot = this.slots()[this.active]
diff --git a/src/modules/adminSettings.js b/src/modules/adminSettings.js
index 01e9a49e..8006717d 100644
--- a/src/modules/adminSettings.js
+++ b/src/modules/adminSettings.js
@@ -10,7 +10,7 @@ export const newUserFlags = {
   ...defaultState.flagStorage
 }
 
-const serverSideStorage = {
+const adminSettingsStorage = {
   state: {
     ...cloneDeep(defaultState)
   },
@@ -40,9 +40,10 @@ const serverSideStorage = {
         }
         set(config, path, convert(c.value))
       })
+      console.log(config)
       commit('updateAdminSettings', { config, modifiedPaths })
     }
   }
 }
 
-export default serverSideStorage
+export default adminSettingsStorage
diff --git a/src/modules/interface.js b/src/modules/interface.js
index a86193ea..2d012f1b 100644
--- a/src/modules/interface.js
+++ b/src/modules/interface.js
@@ -1,7 +1,9 @@
 const defaultState = {
   settingsModalState: 'hidden',
-  settingsModalLoaded: false,
+  settingsModalLoadedUser: false,
+  settingsModalLoadedAdmin: false,
   settingsModalTargetTab: null,
+  settingsModalMode: 'user',
   settings: {
     currentSaveStateNotice: null,
     noticeClearTimeout: null,
@@ -54,10 +56,17 @@ const interfaceMod = {
           throw new Error('Illegal minimization state of settings modal')
       }
     },
-    openSettingsModal (state) {
+    openSettingsModal (state, value) {
+      state.settingsModalMode = value
       state.settingsModalState = 'visible'
-      if (!state.settingsModalLoaded) {
-        state.settingsModalLoaded = true
+      if (value === 'user') {
+        if (!state.settingsModalLoadedUser) {
+          state.settingsModalLoadedUser = true
+        }
+      } else if (value === 'admin') {
+        if (!state.settingsModalLoadedAdmin) {
+          state.settingsModalLoadedAdmin = true
+        }
       }
     },
     setSettingsModalTargetTab (state, value) {
@@ -92,8 +101,8 @@ const interfaceMod = {
     closeSettingsModal ({ commit }) {
       commit('closeSettingsModal')
     },
-    openSettingsModal ({ commit }) {
-      commit('openSettingsModal')
+    openSettingsModal ({ commit }, value = 'user') {
+      commit('openSettingsModal', value)
     },
     togglePeekSettingsModal ({ commit }) {
       commit('togglePeekSettingsModal')
-- 
cgit v1.2.3-70-g09d2

