From 1f0ac68fcd3f382eedf69d1f5fdcffdc16c3885e Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 11 Mar 2021 16:55:14 +0200
Subject: implement ChoiceSetting for settings modal similar to BooleanSetting

---
 .../settings_modal/helpers/choice_setting.vue      | 29 ++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 src/components/settings_modal/helpers/choice_setting.vue

(limited to 'src/components/settings_modal/helpers/choice_setting.vue')

diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
new file mode 100644
index 00000000..1a8275b4
--- /dev/null
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -0,0 +1,29 @@
+<template>
+  <label
+    class="ChoiceSetting"
+  >
+    <slot />
+    <Select
+      :value="state"
+      :disabled="disabled"
+      @change="update"
+    >
+      <option
+        v-for="option in options"
+        :key="option.key"
+        :value="option.value"
+      >
+        {{ option.label }}
+        {{ option.value === defaultValue ? $t('settings.instance_default_simple') : '' }}
+      </option>
+    </Select>
+    <ModifiedIndicator :changed="isChanged" />
+  </label>
+</template>
+
+<script src="./choice_setting.js"></script>
+
+<style lang="scss">
+.ChoiceSetting {
+}
+</style>
-- 
cgit v1.2.3-70-g09d2


From 2da37f15ab6a7e0c6088a6e5a0b2c2885f1cb85a Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 11 Mar 2021 17:04:31 +0200
Subject: Cleanup boolean/choice setting

---
 .../settings_modal/helpers/boolean_setting.vue     | 38 +---------------------
 .../settings_modal/helpers/choice_setting.vue      |  2 +-
 2 files changed, 2 insertions(+), 38 deletions(-)

(limited to 'src/components/settings_modal/helpers/choice_setting.vue')

diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index 146ad6c1..c3ee6583 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -18,40 +18,4 @@
   </label>
 </template>
 
-<script>
-import { get, set } from 'lodash'
-import Checkbox from 'src/components/checkbox/checkbox.vue'
-import ModifiedIndicator from './modified_indicator.vue'
-export default {
-  components: {
-    Checkbox,
-    ModifiedIndicator
-  },
-  props: [
-    'path',
-    'disabled'
-  ],
-  computed: {
-    pathDefault () {
-      const [firstSegment, ...rest] = this.path.split('.')
-      return [firstSegment + 'DefaultValue', ...rest].join('.')
-    },
-    state () {
-      return get(this.$parent, this.path)
-    },
-    isChanged () {
-      return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
-    }
-  },
-  methods: {
-    update (e) {
-      set(this.$parent, this.path, e)
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-.BooleanSetting {
-}
-</style>
+<script src="./boolean_setting.js"></script>
diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
index 1a8275b4..fa17661b 100644
--- a/src/components/settings_modal/helpers/choice_setting.vue
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -14,7 +14,7 @@
         :value="option.value"
       >
         {{ option.label }}
-        {{ option.value === defaultValue ? $t('settings.instance_default_simple') : '' }}
+        {{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }}
       </option>
     </Select>
     <ModifiedIndicator :changed="isChanged" />
-- 
cgit v1.2.3-70-g09d2


From 9c1814d12243f45cb67a797780a8c393f301080c Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 22 Feb 2022 23:31:40 +0200
Subject: expert settings toggle + server-side settings

---
 .../settings_modal/helpers/boolean_setting.js      |  15 ++-
 .../settings_modal/helpers/boolean_setting.vue     |   2 +
 .../settings_modal/helpers/choice_setting.js       |   8 +-
 .../settings_modal/helpers/choice_setting.vue      |   2 +
 .../helpers/server_side_indicator.vue              |  51 +++++++++
 .../helpers/shared_computed_object.js              |   9 ++
 src/components/settings_modal/settings_modal.js    |  11 ++
 src/components/settings_modal/settings_modal.scss  |   7 ++
 src/components/settings_modal/settings_modal.vue   |   6 +-
 .../settings_modal/tabs/filtering_tab.vue          |  46 +-------
 src/components/settings_modal/tabs/general_tab.js  |  11 +-
 src/components/settings_modal/tabs/general_tab.vue | 122 +++++++++++---------
 .../settings_modal/tabs/notifications_tab.js       |   8 +-
 .../settings_modal/tabs/notifications_tab.vue      |  72 +++++++++---
 src/components/settings_modal/tabs/profile_tab.js  |  23 +---
 src/components/settings_modal/tabs/profile_tab.vue | 124 ++++++++++-----------
 src/i18n/en.json                                   |   5 +
 src/main.js                                        |   2 +
 src/modules/config.js                              |   1 +
 src/modules/serverSideConfig.js                    | 111 ++++++++++++++++++
 .../entity_normalizer/entity_normalizer.service.js |   1 +
 21 files changed, 433 insertions(+), 204 deletions(-)
 create mode 100644 src/components/settings_modal/helpers/server_side_indicator.vue
 create mode 100644 src/modules/serverSideConfig.js

(limited to 'src/components/settings_modal/helpers/choice_setting.vue')

diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js
index 5c52f697..353e551c 100644
--- a/src/components/settings_modal/helpers/boolean_setting.js
+++ b/src/components/settings_modal/helpers/boolean_setting.js
@@ -1,14 +1,17 @@
 import { get, set } from 'lodash'
 import Checkbox from 'src/components/checkbox/checkbox.vue'
 import ModifiedIndicator from './modified_indicator.vue'
+import ServerSideIndicator from './server_side_indicator.vue'
 export default {
   components: {
     Checkbox,
-    ModifiedIndicator
+    ModifiedIndicator,
+    ServerSideIndicator
   },
   props: [
     'path',
-    'disabled'
+    'disabled',
+    'expert'
   ],
   computed: {
     pathDefault () {
@@ -26,8 +29,14 @@ export default {
     defaultState () {
       return get(this.$parent, this.pathDefault)
     },
+    isServerSide () {
+      return this.path.startsWith('serverSide_')
+    },
     isChanged () {
-      return this.state !== this.defaultState
+      return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
+    },
+    matchesExpertLevel () {
+      return (this.expert || 0) <= this.$parent.expertLevel
     }
   },
   methods: {
diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index c3ee6583..c82cf23f 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -1,6 +1,7 @@
 <template>
   <label
     class="BooleanSetting"
+    v-if="matchesExpertLevel"
   >
     <Checkbox
       :checked="state"
@@ -14,6 +15,7 @@
         <slot />
       </span>
       <ModifiedIndicator :changed="isChanged" />
+      <ServerSideIndicator :serverSide="isServerSide" />
     </Checkbox>
   </label>
 </template>
diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js
index a15f6bac..07d0f76d 100644
--- a/src/components/settings_modal/helpers/choice_setting.js
+++ b/src/components/settings_modal/helpers/choice_setting.js
@@ -9,7 +9,8 @@ export default {
   props: [
     'path',
     'disabled',
-    'options'
+    'options',
+    'expert'
   ],
   computed: {
     pathDefault () {
@@ -28,7 +29,10 @@ export default {
       return get(this.$parent, this.pathDefault)
     },
     isChanged () {
-      return this.state !== this.defaultState
+      return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
+    },
+    matchesExpertLevel () {
+      return (this.expert || 0) <= this.$parent.expertLevel
     }
   },
   methods: {
diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
index fa17661b..845886ca 100644
--- a/src/components/settings_modal/helpers/choice_setting.vue
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -1,6 +1,7 @@
 <template>
   <label
     class="ChoiceSetting"
+    v-if="matchesExpertLevel"
   >
     <slot />
     <Select
@@ -18,6 +19,7 @@
       </option>
     </Select>
     <ModifiedIndicator :changed="isChanged" />
+    <ServerSideIndicator :serverSide="isServerSide" />
   </label>
 </template>
 
diff --git a/src/components/settings_modal/helpers/server_side_indicator.vue b/src/components/settings_modal/helpers/server_side_indicator.vue
new file mode 100644
index 00000000..143a86a1
--- /dev/null
+++ b/src/components/settings_modal/helpers/server_side_indicator.vue
@@ -0,0 +1,51 @@
+<template>
+  <span
+    v-if="serverSide"
+    class="ServerSideIndicator"
+  >
+    <Popover
+      trigger="hover"
+    >
+      <template v-slot:trigger>
+        &nbsp;
+        <FAIcon
+          icon="server"
+          :aria-label="$t('settings.setting_server_side')"
+        />
+      </template>
+      <template v-slot:content>
+        <div class="serverside-tooltip">
+          {{ $t('settings.setting_server_side') }}
+        </div>
+      </template>
+    </Popover>
+  </span>
+</template>
+
+<script>
+import Popover from 'src/components/popover/popover.vue'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { faServer } from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+  faServer
+)
+
+export default {
+  components: { Popover },
+  props: ['serverSide']
+}
+</script>
+
+<style lang="scss">
+.ServerSideIndicator {
+  display: inline-block;
+  position: relative;
+
+  .serverside-tooltip {
+    margin: 0.5em 1em;
+    min-width: 10em;
+    text-align: center;
+  }
+}
+</style>
diff --git a/src/components/settings_modal/helpers/shared_computed_object.js b/src/components/settings_modal/helpers/shared_computed_object.js
index 2c833c0c..12431dca 100644
--- a/src/components/settings_modal/helpers/shared_computed_object.js
+++ b/src/components/settings_modal/helpers/shared_computed_object.js
@@ -1,4 +1,5 @@
 import { defaultState as configDefaultState } from 'src/modules/config.js'
+import { defaultState as serverSideConfigDefaultState } from 'src/modules/serverSideConfig.js'
 
 const SharedComputedObject = () => ({
   user () {
@@ -22,6 +23,14 @@ const SharedComputedObject = () => ({
       }
     }])
     .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
+  ...Object.keys(serverSideConfigDefaultState)
+    .map(key => ['serverSide_' + key, {
+      get () { return this.$store.state.serverSideConfig[key] },
+      set (value) {
+        this.$store.dispatch('setServerSideOption', { name: key, value })
+      }
+    }])
+    .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
   // Special cases (need to transform values or perform actions first)
   useStreamingApi: {
     get () { return this.$store.getters.mergedConfig.useStreamingApi },
diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js
index 04043483..82ea410e 100644
--- a/src/components/settings_modal/settings_modal.js
+++ b/src/components/settings_modal/settings_modal.js
@@ -3,6 +3,7 @@ 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 { cloneDeep } from 'lodash'
 import {
@@ -51,6 +52,7 @@ const SettingsModal = {
   components: {
     Modal,
     Popover,
+    Checkbox,
     SettingsModalContent: getResettableAsyncComponent(
       () => import('./settings_modal_content.vue'),
       {
@@ -159,6 +161,15 @@ const SettingsModal = {
     },
     modalPeeked () {
       return this.$store.state.interface.settingsModalState === 'minimized'
+    },
+    expertLevel: {
+      get () {
+        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.scss b/src/components/settings_modal/settings_modal.scss
index 90446b36..fb466f2f 100644
--- a/src/components/settings_modal/settings_modal.scss
+++ b/src/components/settings_modal/settings_modal.scss
@@ -48,4 +48,11 @@
       }
     }
   }
+
+  .settings-footer {
+    display: flex;
+    >* {
+      margin-right: 0.5em;
+    }
+  }
 }
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index 583c2ecc..0aad1abb 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -53,7 +53,7 @@
       <div class="panel-body">
         <SettingsModalContent v-if="modalOpenedOnce" />
       </div>
-      <div class="panel-footer">
+      <div class="panel-footer settings-footer">
         <Popover
           class="export"
           trigger="click"
@@ -108,6 +108,10 @@
             </div>
           </template>
         </Popover>
+
+        <Checkbox v-model="expertLevel" >
+          {{ $t("settings.expert_mode")}}
+        </Checkbox>
       </div>
     </div>
   </Modal>
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 50ee20e0..cd7f0bc4 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -38,7 +38,7 @@
           </ul>
         </li>
         <li>
-          <BooleanSetting path="hidePostStats">
+          <BooleanSetting path="hidePostStats" expert="1">
             {{ $t('settings.hide_post_stats') }}
           </BooleanSetting>
         </li>
@@ -59,7 +59,7 @@
           <div>{{ $t('settings.filtering_explanation') }}</div>
         </li>
         <h3>{{ $t('settings.attachments') }}</h3>
-        <li>
+        <li v-if="expertLevel > 0">
           <label for="maxThumbnails">
             {{ $t('settings.max_thumbnails') }}
           </label>
@@ -84,7 +84,7 @@
         </li>
       </ul>
     </div>
-    <div class="setting-item">
+    <div class="setting-item" v-if="expertLevel > 0">
       <h2>{{ $t('settings.user_profiles') }}</h2>
       <ul class="setting-list">
         <li>
@@ -94,46 +94,6 @@
         </li>
       </ul>
     </div>
-    <div class="setting-item">
-      <h2>{{ $t('settings.notifications') }}</h2>
-      <ul class="setting-list">
-        <li class="select-multiple">
-          <span class="label">{{ $t('settings.notification_visibility') }}</span>
-          <ul class="option-list">
-            <li>
-              <BooleanSetting path="notificationVisibility.likes">
-                {{ $t('settings.notification_visibility_likes') }}
-              </BooleanSetting>
-            </li>
-            <li>
-              <BooleanSetting path="notificationVisibility.repeats">
-                {{ $t('settings.notification_visibility_repeats') }}
-              </BooleanSetting>
-            </li>
-            <li>
-              <BooleanSetting path="notificationVisibility.follows">
-                {{ $t('settings.notification_visibility_follows') }}
-              </BooleanSetting>
-            </li>
-            <li>
-              <BooleanSetting path="notificationVisibility.mentions">
-                {{ $t('settings.notification_visibility_mentions') }}
-              </BooleanSetting>
-            </li>
-            <li>
-              <BooleanSetting path="notificationVisibility.moves">
-                {{ $t('settings.notification_visibility_moves') }}
-              </BooleanSetting>
-            </li>
-            <li>
-              <BooleanSetting path="notificationVisibility.emojiReactions">
-                {{ $t('settings.notification_visibility_emoji_reactions') }}
-              </BooleanSetting>
-            </li>
-          </ul>
-        </li>
-      </ul>
-    </div>
   </div>
 </template>
 <script src="./filtering_tab.js"></script>
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 952c328d..9e4e282f 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -1,8 +1,10 @@
 import BooleanSetting from '../helpers/boolean_setting.vue'
 import ChoiceSetting from '../helpers/choice_setting.vue'
+import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
 import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
 
 import SharedComputedObject from '../helpers/shared_computed_object.js'
+import ServerSideIndicator from '../helpers/server_side_indicator.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
   faGlobe
@@ -37,7 +39,9 @@ const GeneralTab = {
   components: {
     BooleanSetting,
     ChoiceSetting,
-    InterfaceLanguageSwitcher
+    InterfaceLanguageSwitcher,
+    ScopeSelector,
+    ServerSideIndicator,
   },
   computed: {
     postFormats () {
@@ -57,6 +61,11 @@ const GeneralTab = {
     },
     instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
     ...SharedComputedObject()
+  },
+  methods: {
+    changeDefaultScope (value) {
+      this.$store.dispatch('setServerSideOption', { name: 'defaultScope', value })
+    }
   }
 }
 
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index eba3b268..4accf0c1 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -27,7 +27,7 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="streaming">
+          <BooleanSetting path="streaming" expert="1">
             {{ $t('settings.streaming') }}
           </BooleanSetting>
           <ul
@@ -38,6 +38,7 @@
               <BooleanSetting
                 path="pauseOnUnfocused"
                 :disabled="!streaming"
+                expert="1"
               >
                 {{ $t('settings.pause_on_unfocused') }}
               </BooleanSetting>
@@ -45,7 +46,7 @@
           </ul>
         </li>
         <li>
-          <BooleanSetting path="useStreamingApi">
+          <BooleanSetting path="useStreamingApi" expert="1">
             {{ $t('settings.useStreamingApi') }}
             <br>
             <small>
@@ -54,17 +55,22 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="virtualScrolling">
+          <BooleanSetting path="virtualScrolling" expert="1">
             {{ $t('settings.virtual_scrolling') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="autohideFloatingPostButton">
+          <BooleanSetting path="alwaysShowNewPostButton" expert="1">
+            {{ $t('settings.always_show_post_button') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="autohideFloatingPostButton" expert="1">
             {{ $t('settings.autohide_floating_post_button') }}
           </BooleanSetting>
         </li>
         <li v-if="instanceShoutboxPresent">
-          <BooleanSetting path="hideShoutbox">
+          <BooleanSetting path="hideShoutbox" expert="1">
             {{ $t('settings.hide_shoutbox') }}
           </BooleanSetting>
         </li>
@@ -79,13 +85,18 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="emojiReactionsOnTimeline">
+          <BooleanSetting path="emojiReactionsOnTimeline" expert="1">
             {{ $t('settings.emoji_reactions_on_timeline') }}
           </BooleanSetting>
         </li>
+        <li>
+          <BooleanSetting path="serverSide_stripRichContent" expert="1" v-if="user">
+            {{ $t('settings.no_rich_text_description') }}
+          </BooleanSetting>
+        </li>
         <h3>{{ $t('settings.attachments') }}</h3>
         <li>
-          <BooleanSetting path="useContainFit">
+          <BooleanSetting path="useContainFit" expert="1">
             {{ $t('settings.use_contain_fit') }}
           </BooleanSetting>
         </li>
@@ -97,7 +108,7 @@
         <ul class="setting-list suboptions">
           <li>
             <BooleanSetting
-              path="preloadImage"
+              path="preloadImage" expert="1"
               :disabled="!hideNsfw"
             >
               {{ $t('settings.preload_images') }}
@@ -105,7 +116,7 @@
           </li>
           <li>
             <BooleanSetting
-              path="useOneClickNsfw"
+              path="useOneClickNsfw" expert="1"
               :disabled="!hideNsfw"
             >
               {{ $t('settings.use_one_click_nsfw') }}
@@ -113,7 +124,7 @@
           </li>
         </ul>
         <li>
-          <BooleanSetting path="loopVideo">
+          <BooleanSetting path="loopVideo" expert="1">
             {{ $t('settings.loop_video') }}
           </BooleanSetting>
           <ul
@@ -122,7 +133,7 @@
           >
             <li>
               <BooleanSetting
-                path="loopVideoSilentOnly"
+                path="loopVideoSilentOnly" expert="1"
                 :disabled="!loopVideo || !loopSilentAvailable"
               >
                 {{ $t('settings.loop_video_silent_only') }}
@@ -137,21 +148,11 @@
           </ul>
         </li>
         <li>
-          <BooleanSetting path="playVideosInModal">
+          <BooleanSetting path="playVideosInModal" expert="1">
             {{ $t('settings.play_videos_in_modal') }}
           </BooleanSetting>
         </li>
-        <h3>{{ $t('settings.fun') }}</h3>
-        <li>
-          <BooleanSetting path="greentext">
-            {{ $t('settings.greentext') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="mentionLinkShowYous">
-            {{ $t('settings.show_yous') }}
-          </BooleanSetting>
-        </li>
+        <h3>{{ $t('settings.mention_links') }}</h3>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
@@ -164,15 +165,14 @@
         <ul
           class="setting-list suboptions"
         >
-          <li
-            v-if="mentionLinkDisplay === 'short'"
-          >
-            <BooleanSetting path="mentionLinkShowTooltip">
+          <li v-if="mentionLinkDisplay === 'short'">
+            <BooleanSetting path="mentionLinkShowTooltip" expert="1">
               {{ $t('settings.mention_link_show_tooltip') }}
             </BooleanSetting>
           </li>
+        </ul>
           <li>
-            <BooleanSetting path="useAtIcon">
+            <BooleanSetting path="useAtIcon" expert="1">
               {{ $t('settings.use_at_icon') }}
             </BooleanSetting>
           </li>
@@ -182,29 +182,56 @@
             </BooleanSetting>
           </li>
           <li>
-            <BooleanSetting path="mentionLinkFadeDomain">
+            <BooleanSetting path="mentionLinkFadeDomain" expert="1">
               {{ $t('settings.mention_link_fade_domain') }}
             </BooleanSetting>
           </li>
-          <li>
-            <BooleanSetting path="mentionLinkBoldenYou">
+          <li v-if="user">
+            <BooleanSetting path="mentionLinkBoldenYou" expert="1">
               {{ $t('settings.mention_link_bolden_you') }}
             </BooleanSetting>
           </li>
-        </ul>
+        <h3 v-if="expertLevel > 0">{{ $t('settings.fun') }}</h3>
+        <li>
+          <BooleanSetting path="greentext" expert="1">
+            {{ $t('settings.greentext') }}
+          </BooleanSetting>
+        </li>
+        <li v-if="user">
+          <BooleanSetting path="mentionLinkShowYous" expert="1">
+            {{ $t('settings.show_yous') }}
+          </BooleanSetting>
+        </li>
       </ul>
     </div>
 
-    <div class="setting-item">
+    <div class="setting-item" v-if="user">
       <h2>{{ $t('settings.composing') }}</h2>
       <ul class="setting-list">
         <li>
-          <BooleanSetting path="scopeCopy">
+          <label for="default-vis">
+            {{ $t('settings.default_vis') }} <ServerSideIndicator :serverSide="true"/>
+            <ScopeSelector
+              class="scope-selector"
+              :show-all="true"
+              :user-default="serverSide_defaultScope"
+              :initial-scope="serverSide_defaultScope"
+              :on-scope-change="changeDefaultScope"
+            />
+          </label>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_defaultNSFW">
+            {{ $t('settings.sensitive_by_default') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="scopeCopy" expert="1">
             {{ $t('settings.scope_copy') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="alwaysShowSubjectInput">
+          <BooleanSetting path="alwaysShowSubjectInput" expert="1">
             {{ $t('settings.subject_input_always_show') }}
           </BooleanSetting>
         </li>
@@ -213,6 +240,7 @@
             id="subjectLineBehavior"
             path="subjectLineBehavior"
             :options="subjectLineOptions"
+            expert="1"
           >
             {{ $t('settings.subject_line_behavior') }}
           </ChoiceSetting>
@@ -227,43 +255,27 @@
           </ChoiceSetting>
         </li>
         <li>
-          <BooleanSetting path="minimalScopesMode">
+          <BooleanSetting path="minimalScopesMode" expert="1">
             {{ $t('settings.minimal_scopes_mode') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="sensitiveByDefault">
-            {{ $t('settings.sensitive_by_default') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="alwaysShowNewPostButton">
+          <BooleanSetting path="alwaysShowNewPostButton" expert="1">
             {{ $t('settings.always_show_post_button') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="autohideFloatingPostButton">
+          <BooleanSetting path="autohideFloatingPostButton" expert="1">
             {{ $t('settings.autohide_floating_post_button') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="padEmoji">
+          <BooleanSetting path="padEmoji" expert="1">
             {{ $t('settings.pad_emoji') }}
           </BooleanSetting>
         </li>
       </ul>
     </div>
-
-    <div class="setting-item">
-      <h2>{{ $t('settings.notifications') }}</h2>
-      <ul class="setting-list">
-        <li>
-          <BooleanSetting path="webPushNotifications">
-            {{ $t('settings.enable_web_push_notifications') }}
-          </BooleanSetting>
-        </li>
-      </ul>
-    </div>
   </div>
 </template>
 
diff --git a/src/components/settings_modal/tabs/notifications_tab.js b/src/components/settings_modal/tabs/notifications_tab.js
index 3e44c95d..3c6ab87f 100644
--- a/src/components/settings_modal/tabs/notifications_tab.js
+++ b/src/components/settings_modal/tabs/notifications_tab.js
@@ -1,4 +1,5 @@
-import Checkbox from 'src/components/checkbox/checkbox.vue'
+import BooleanSetting from '../helpers/boolean_setting.vue'
+import SharedComputedObject from '../helpers/shared_computed_object.js'
 
 const NotificationsTab = {
   data () {
@@ -9,12 +10,13 @@ const NotificationsTab = {
     }
   },
   components: {
-    Checkbox
+    BooleanSetting
   },
   computed: {
     user () {
       return this.$store.state.users.currentUser
-    }
+    },
+    ...SharedComputedObject()
   },
   methods: {
     updateNotificationSettings () {
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index 7e0568ea..5e9ce91e 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -2,30 +2,68 @@
   <div :label="$t('settings.notifications')">
     <div class="setting-item">
       <h2>{{ $t('settings.notification_setting_filters') }}</h2>
-      <p>
-        <Checkbox v-model="notificationSettings.block_from_strangers">
-          {{ $t('settings.notification_setting_block_from_strangers') }}
-        </Checkbox>
-      </p>
+      <ul class="setting-list">
+        <li>
+          <BooleanSetting path="serverSide_blockNotificationsFromStrangers">
+            {{ $t('settings.notification_setting_block_from_strangers') }}
+          </BooleanSetting>
+        </li>
+        <li class="select-multiple">
+          <span class="label">{{ $t('settings.notification_visibility') }}</span>
+          <ul class="option-list">
+            <li>
+              <BooleanSetting path="notificationVisibility.likes">
+                {{ $t('settings.notification_visibility_likes') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting path="notificationVisibility.repeats">
+                {{ $t('settings.notification_visibility_repeats') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting path="notificationVisibility.follows">
+                {{ $t('settings.notification_visibility_follows') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting path="notificationVisibility.mentions">
+                {{ $t('settings.notification_visibility_mentions') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting path="notificationVisibility.moves">
+                {{ $t('settings.notification_visibility_moves') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting path="notificationVisibility.emojiReactions">
+                {{ $t('settings.notification_visibility_emoji_reactions') }}
+              </BooleanSetting>
+            </li>
+          </ul>
+        </li>
+      </ul>
     </div>
 
-    <div class="setting-item">
+    <div class="setting-item" v-if="expertLevel > 0">
       <h2>{{ $t('settings.notification_setting_privacy') }}</h2>
-      <p>
-        <Checkbox v-model="notificationSettings.hide_notification_contents">
-          {{ $t('settings.notification_setting_hide_notification_contents') }}
-        </Checkbox>
-      </p>
+      <ul class="setting-list">
+        <li>
+          <BooleanSetting path="webPushNotifications" expert="1">
+            {{ $t('settings.enable_web_push_notifications') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_webPushHideContents" expert="1">
+            {{ $t('settings.notification_setting_hide_notification_contents') }}
+          </BooleanSetting>
+        </li>
+      </ul>
     </div>
     <div class="setting-item">
       <p>{{ $t('settings.notification_mutes') }}</p>
       <p>{{ $t('settings.notification_blocks') }}</p>
-      <button
-        class="btn button-default"
-        @click="updateNotificationSettings"
-      >
-        {{ $t('settings.save') }}
-      </button>
     </div>
   </div>
 </template>
diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js
index 64079fcd..bee8a7bb 100644
--- a/src/components/settings_modal/tabs/profile_tab.js
+++ b/src/components/settings_modal/tabs/profile_tab.js
@@ -8,6 +8,9 @@ import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
 import suggestor from 'src/components/emoji_input/suggestor.js'
 import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
 import Checkbox from 'src/components/checkbox/checkbox.vue'
+import BooleanSetting from '../helpers/boolean_setting.vue'
+import SharedComputedObject from '../helpers/shared_computed_object.js'
+
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
   faTimes,
@@ -27,18 +30,10 @@ const ProfileTab = {
       newName: this.$store.state.users.currentUser.name_unescaped,
       newBio: unescape(this.$store.state.users.currentUser.description),
       newLocked: this.$store.state.users.currentUser.locked,
-      newNoRichText: this.$store.state.users.currentUser.no_rich_text,
-      newDefaultScope: this.$store.state.users.currentUser.default_scope,
       newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })),
-      hideFollows: this.$store.state.users.currentUser.hide_follows,
-      hideFollowers: this.$store.state.users.currentUser.hide_followers,
-      hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count,
-      hideFollowersCount: this.$store.state.users.currentUser.hide_followers_count,
       showRole: this.$store.state.users.currentUser.show_role,
       role: this.$store.state.users.currentUser.role,
-      discoverable: this.$store.state.users.currentUser.discoverable,
       bot: this.$store.state.users.currentUser.bot,
-      allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
       pickAvatarBtnVisible: true,
       bannerUploading: false,
       backgroundUploading: false,
@@ -54,12 +49,14 @@ const ProfileTab = {
     EmojiInput,
     Autosuggest,
     ProgressButton,
-    Checkbox
+    Checkbox,
+    BooleanSetting
   },
   computed: {
     user () {
       return this.$store.state.users.currentUser
     },
+    ...SharedComputedObject(),
     emojiUserSuggestor () {
       return suggestor({
         emoji: [
@@ -123,15 +120,7 @@ const ProfileTab = {
             /* eslint-disable camelcase */
             display_name: this.newName,
             fields_attributes: this.newFields.filter(el => el != null),
-            default_scope: this.newDefaultScope,
-            no_rich_text: this.newNoRichText,
-            hide_follows: this.hideFollows,
-            hide_followers: this.hideFollowers,
-            discoverable: this.discoverable,
             bot: this.bot,
-            allow_following_move: this.allowFollowingMove,
-            hide_follows_count: this.hideFollowsCount,
-            hide_followers_count: this.hideFollowersCount,
             show_role: this.showRole
             /* eslint-enable camelcase */
           } }).then((user) => {
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index bb3c301d..2cf3e8be 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -25,61 +25,6 @@
           class="bio resize-height"
         />
       </EmojiInput>
-      <p>
-        <Checkbox v-model="newLocked">
-          {{ $t('settings.lock_account_description') }}
-        </Checkbox>
-      </p>
-      <div>
-        <label for="default-vis">{{ $t('settings.default_vis') }}</label>
-        <div
-          id="default-vis"
-          class="visibility-tray"
-        >
-          <scope-selector
-            :show-all="true"
-            :user-default="newDefaultScope"
-            :initial-scope="newDefaultScope"
-            :on-scope-change="changeVis"
-          />
-        </div>
-      </div>
-      <p>
-        <Checkbox v-model="newNoRichText">
-          {{ $t('settings.no_rich_text_description') }}
-        </Checkbox>
-      </p>
-      <p>
-        <Checkbox v-model="hideFollows">
-          {{ $t('settings.hide_follows_description') }}
-        </Checkbox>
-      </p>
-      <p class="setting-subitem">
-        <Checkbox
-          v-model="hideFollowsCount"
-          :disabled="!hideFollows"
-        >
-          {{ $t('settings.hide_follows_count_description') }}
-        </Checkbox>
-      </p>
-      <p>
-        <Checkbox v-model="hideFollowers">
-          {{ $t('settings.hide_followers_description') }}
-        </Checkbox>
-      </p>
-      <p class="setting-subitem">
-        <Checkbox
-          v-model="hideFollowersCount"
-          :disabled="!hideFollowers"
-        >
-          {{ $t('settings.hide_followers_count_description') }}
-        </Checkbox>
-      </p>
-      <p>
-        <Checkbox v-model="allowFollowingMove">
-          {{ $t('settings.allow_following_move') }}
-        </Checkbox>
-      </p>
       <p v-if="role === 'admin' || role === 'moderator'">
         <Checkbox v-model="showRole">
           <template v-if="role === 'admin'">
@@ -90,11 +35,6 @@
           </template>
         </Checkbox>
       </p>
-      <p>
-        <Checkbox v-model="discoverable">
-          {{ $t('settings.discoverable') }}
-        </Checkbox>
-      </p>
       <div v-if="maxFields > 0">
         <p>{{ $t('settings.profile_fields.label') }}</p>
         <div
@@ -161,8 +101,7 @@
       <p class="visibility-notice">
         {{ $t('settings.avatar_size_instruction') }}
       </p>
-      <div class="current-avatar-container">
-        <img
+      <div class="current-avatar-container"> <img
           :src="user.profile_image_url_original"
           class="current-avatar"
         >
@@ -269,6 +208,67 @@
         {{ $t('settings.save') }}
       </button>
     </div>
+    <div class="setting-item">
+      <h2>{{ $t('settings.account_privacy') }}</h2>
+      <ul class="setting-list">
+        <li>
+          <BooleanSetting path="serverSide_locked">
+            {{ $t('settings.lock_account_description') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_discoverable">
+            {{ $t('settings.discoverable') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_allowFollowingMove">
+            {{ $t('settings.allow_following_move') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_hideFavorites">
+            {{ $t('settings.hide_favorites_description') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_hideFollowers">
+            {{ $t('settings.hide_followers_description') }}
+          </BooleanSetting>
+          <ul
+            class="setting-list suboptions"
+            :class="[{disabled: !serverSide_hideFollowers}]"
+          >
+            <li>
+              <BooleanSetting
+                path="serverSide_hideFollowersCount"
+                :disabled="!severSide_hideFollowers"
+                >
+                {{ $t('settings.hide_followers_count_description') }}
+              </BooleanSetting>
+            </li>
+          </ul>
+        </li>
+        <li>
+          <BooleanSetting path="serverSide_hideFollows">
+            {{ $t('settings.hide_follows_description') }}
+          </BooleanSetting>
+          <ul
+            class="setting-list suboptions"
+            :class="[{disabled: !serverSide_hideFollows}]"
+          >
+            <li>
+              <BooleanSetting
+                path="serverSide_hideFollowsCount"
+                :disabled="!severSide_hideFollows"
+                >
+                {{ $t('settings.hide_follows_count_description') }}
+              </BooleanSetting>
+            </li>
+          </ul>
+        </li>
+      </ul>
+    </div>
   </div>
 </template>
 
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 8eb7fcc6..932e8af5 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -259,11 +259,14 @@
   },
   "settings": {
     "app_name": "App name",
+    "expert_mode": "Expert mode",
     "save": "Save changes",
     "security": "Security",
     "setting_changed": "Setting is different from default",
+    "setting_server_side": "This setting is tied to your profile and affects all sessions and clients",
     "enter_current_password_to_confirm": "Enter your current password to confirm your identity",
     "post_look_feel": "Posts Look & Feel",
+    "mention_links": "Mention links",
     "mfa": {
       "otp": "OTP",
       "setup_otp": "Setup OTP",
@@ -400,6 +403,7 @@
       "name": "Label",
       "value": "Content"
     },
+    "account_privacy": "Privacy",
     "use_contain_fit": "Don't crop the attachment in thumbnails",
     "name": "Name",
     "name_bio": "Name & bio",
@@ -417,6 +421,7 @@
     "no_rich_text_description": "Strip rich text formatting from all posts",
     "no_blocks": "No blocks",
     "no_mutes": "No mutes",
+    "hide_favorites_description": "Don't show list of my favorites (people still get notified)",
     "hide_follows_description": "Don't show who I'm following",
     "hide_followers_description": "Don't show who's following me",
     "hide_follows_count_description": "Don't show follow count",
diff --git a/src/main.js b/src/main.js
index 3895da89..73bd49ed 100644
--- a/src/main.js
+++ b/src/main.js
@@ -11,6 +11,7 @@ import statusesModule from './modules/statuses.js'
 import usersModule from './modules/users.js'
 import apiModule from './modules/api.js'
 import configModule from './modules/config.js'
+import serverSideConfigModule from './modules/serverSideConfig.js'
 import shoutModule from './modules/shout.js'
 import oauthModule from './modules/oauth.js'
 import authFlowModule from './modules/auth_flow.js'
@@ -88,6 +89,7 @@ const persistedStateOptions = {
       users: usersModule,
       api: apiModule,
       config: configModule,
+      serverSideConfig: serverSideConfigModule,
       shout: shoutModule,
       oauth: oauthModule,
       authFlow: authFlowModule,
diff --git a/src/modules/config.js b/src/modules/config.js
index 20979174..775ee398 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -16,6 +16,7 @@ export const multiChoiceProperties = [
 ]
 
 export const defaultState = {
+  expertLevel: 0, // used to track which settings to show and hide
   colors: {},
   theme: undefined,
   customTheme: undefined,
diff --git a/src/modules/serverSideConfig.js b/src/modules/serverSideConfig.js
new file mode 100644
index 00000000..75ea91be
--- /dev/null
+++ b/src/modules/serverSideConfig.js
@@ -0,0 +1,111 @@
+import { get, set } from 'lodash'
+
+export const settingsMapGet = {
+  'defaultScope': 'source.privacy',
+  'defaultNSFW': 'source.sensitive',
+  'stripRichContent': 'source.pleroma.no_rich_content',
+  // Privacy
+  'locked': 'locked',
+  'acceptChatMessages': 'pleroma.accepts_chat_messages',
+  'allowFollowingMove': 'pleroma.allow_following_move',
+  'discoverable': 'source.discoverable',
+  'hideFavorites': 'pleroma.hide_favorites',
+  'hideFollowers': 'pleroma.hide_followers',
+  'hideFollows': 'pleroma.hide_follows',
+  'hideFollowersCount': 'pleroma.hide_followers_count',
+  'hideFollowsCount': 'pleroma.hide_follows_count',
+  // NotificationSettingsAPIs
+  'webPushHideContents': 'pleroma.notification_settings.hide_notification_contents',
+  'blockNotificationsFromStrangers': 'pleroma.notification_settings.block_from_strangers'
+}
+
+export const settingsMapSet = {
+  'defaultScope': 'source.privacy',
+  'defaultNSFW': 'source.sensitive',
+  'stripRichContent': 'source.pleroma.no_rich_content',
+  // Privacy
+  'locked': 'locked',
+  'acceptChatMessages': 'accepts_chat_messages',
+  'allowFollowingMove': 'allow_following_move',
+  'discoverable': 'source.discoverable',
+  'hideFavorites': 'hide_favorites',
+  'hideFollowers': 'hide_followers',
+  'hideFollows': 'hide_follows',
+  'hideFollowersCount': 'hide_followers_count',
+  'hideFollowsCount': 'hide_follows_count',
+  // NotificationSettingsAPIs
+  'webPushHideContents': 'hide_notification_contents',
+  'blockNotificationsFromStrangers': 'block_from_strangers'
+}
+
+export const customAPIs = {
+  __defaultApi: 'updateProfile',
+  'webPushHideContents': 'updateNotificationSettings',
+  'blockNotificationsFromStrangers': 'updateNotificationSettings'
+}
+
+export const defaultState = Object.fromEntries(Object.keys(settingsMapGet).map(key => [key, undefined]))
+
+const serverSideConfig = {
+  state: { ...defaultState },
+  mutations: {
+    confirmServerSideOption (state, { name, value }) {
+      set(state, name, value)
+    },
+    wipeServerSideOption (state, { name }) {
+      set(state, name, undefined)
+    },
+    // Set the settings based on their path location
+    setCurrentUser (state, user) {
+      Object.entries(settingsMapGet).forEach(([name, path]) => {
+        set(state, name, get(user._original, path))
+      })
+    }
+  },
+  actions: {
+    setServerSideOption ({ rootState, state, commit, dispatch }, { name, value }) {
+      const oldValue = get(state, name)
+      const params = {}
+      const path = settingsMapSet[name]
+      if (!path) throw new Error('Invalid server-side setting')
+      commit('wipeServerSideOption', { name })
+      const customAPIName = customAPIs[name] || customAPIs.__defaultApi
+      const api = rootState.api.backendInteractor[customAPIName]
+      let prefix = ''
+      switch (customAPIName) {
+        case 'updateNotificationSettings':
+          prefix = 'settings.'
+          break
+        default:
+          prefix = 'params.'
+          break
+      }
+
+      set(params, prefix + path, value)
+      api(params)
+        .then((result) => {
+          switch (customAPIName) {
+            case 'updateNotificationSettings':
+              console.log(result)
+              if (result.status === 'success') {
+                commit('confirmServerSideOption', { name, value })
+              } else {
+                commit('confirmServerSideOption', { name, value: oldValue })
+              }
+              break
+            default:
+              commit('addNewUsers', [result])
+              commit('setCurrentUser', result)
+              break
+          }
+          console.log(state)
+        })
+        .catch((e) => {
+          console.warn('Error setting server-side option:', e)
+          commit('confirmServerSideOption', { name, value: oldValue })
+        })
+    }
+  }
+}
+
+export default serverSideConfig
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 7025d803..f219c161 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -44,6 +44,7 @@ export const parseUser = (data) => {
   const mastoShort = masto && !data.hasOwnProperty('avatar')
 
   output.id = String(data.id)
+  output._original = data // used for server-side settings
 
   if (masto) {
     output.screen_name = data.acct
-- 
cgit v1.2.3-70-g09d2


From 77bb0b553062b91a69fbf92ce7540565f098951f Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 28 Feb 2022 18:23:32 +0200
Subject: lint

---
 src/components/attachment/attachment.vue           |  19 ++-
 src/components/gallery/gallery.vue                 |   4 +-
 .../settings_modal/helpers/boolean_setting.vue     |   4 +-
 .../settings_modal/helpers/choice_setting.vue      |   4 +-
 src/components/settings_modal/settings_modal.vue   |   4 +-
 .../settings_modal/tabs/filtering_tab.vue          |  16 +-
 src/components/settings_modal/tabs/general_tab.js  |   2 +-
 src/components/settings_modal/tabs/general_tab.vue | 167 +++++++++++++++------
 .../settings_modal/tabs/notifications_tab.vue      |  15 +-
 src/components/settings_modal/tabs/profile_tab.vue |   7 +-
 10 files changed, 168 insertions(+), 74 deletions(-)

(limited to 'src/components/settings_modal/helpers/choice_setting.vue')

diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 59173759..c4399f30 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -81,56 +81,56 @@
         <button
           v-if="type === 'flash' && flashLoaded"
           class="button-unstyled attachment-button"
-          @click.prevent="stopFlash"
           :title="$t('status.attachment_stop_flash')"
+          @click.prevent="stopFlash"
         >
           <FAIcon icon="stop" />
         </button>
         <button
           v-if="attachment.description && size !== 'small' && !edit && type !== 'unknown'"
           class="button-unstyled attachment-button"
-          @click.prevent="toggleDescription"
           :title="$t('status.show_attachment_description')"
+          @click.prevent="toggleDescription"
         >
           <FAIcon icon="align-right" />
         </button>
         <button
           v-if="!useModal && type !== 'unknown'"
           class="button-unstyled attachment-button"
-          @click.prevent="openModalForce"
           :title="$t('status.show_attachment_in_modal')"
+          @click.prevent="openModalForce"
         >
           <FAIcon icon="search-plus" />
         </button>
         <button
           v-if="nsfw && hideNsfwLocal"
           class="button-unstyled attachment-button"
-          @click.prevent="toggleHidden"
           :title="$t('status.hide_attachment')"
+          @click.prevent="toggleHidden"
         >
           <FAIcon icon="times" />
         </button>
         <button
           v-if="shiftUp"
           class="button-unstyled attachment-button"
-          @click.prevent="onShiftUp"
           :title="$t('status.move_up')"
+          @click.prevent="onShiftUp"
         >
           <FAIcon icon="chevron-left" />
         </button>
         <button
           v-if="shiftDn"
           class="button-unstyled attachment-button"
-          @click.prevent="onShiftDn"
           :title="$t('status.move_down')"
+          @click.prevent="onShiftDn"
         >
           <FAIcon icon="chevron-right" />
         </button>
         <button
           v-if="remove"
           class="button-unstyled attachment-button"
-          @click.prevent="onRemove"
           :title="$t('status.remove_attachment')"
+          @click.prevent="onRemove"
         >
           <FAIcon icon="trash-alt" />
         </button>
@@ -160,7 +160,10 @@
         :href="attachment.url"
         target="_blank"
       >
-        <FAIcon size="5x" :icon="placeholderIconClass" />
+        <FAIcon
+          size="5x"
+          :icon="placeholderIconClass"
+        />
         <p>
           {{ localDescription }}
         </p>
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index f9cad8a9..f2e1b5ce 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -26,8 +26,8 @@
             :size="size"
             :editable="editable"
             :remove="removeAttachment"
-            :shiftUp="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment"
-            :shiftDn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment"
+            :shift-up="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment"
+            :shift-dn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment"
             :edit="editAttachment"
             :description="descriptions && descriptions[attachment.id]"
             :hide-description="size === 'small' || tooManyAttachments && hidingLong"
diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index 3a7bd805..e0d825f2 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -1,7 +1,7 @@
 <template>
   <label
-    class="BooleanSetting"
     v-if="matchesExpertLevel"
+    class="BooleanSetting"
   >
     <Checkbox
       :checked="state"
@@ -14,7 +14,7 @@
       >
         <slot />
       </span>
-      <ModifiedIndicator :changed="isChanged" /><ServerSideIndicator :serverSide="isServerSide" /> </Checkbox>
+      <ModifiedIndicator :changed="isChanged" /><ServerSideIndicator :server-side="isServerSide" /> </Checkbox>
   </label>
 </template>
 
diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
index 845886ca..54f5d0a7 100644
--- a/src/components/settings_modal/helpers/choice_setting.vue
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -1,7 +1,7 @@
 <template>
   <label
-    class="ChoiceSetting"
     v-if="matchesExpertLevel"
+    class="ChoiceSetting"
   >
     <slot />
     <Select
@@ -19,7 +19,7 @@
       </option>
     </Select>
     <ModifiedIndicator :changed="isChanged" />
-    <ServerSideIndicator :serverSide="isServerSide" />
+    <ServerSideIndicator :server-side="isServerSide" />
   </label>
 </template>
 
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index 0aad1abb..1805c77f 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -109,8 +109,8 @@
           </template>
         </Popover>
 
-        <Checkbox v-model="expertLevel" >
-          {{ $t("settings.expert_mode")}}
+        <Checkbox v-model="expertLevel">
+          {{ $t("settings.expert_mode") }}
         </Checkbox>
       </div>
     </div>
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 0352e887..e60a8a85 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -21,8 +21,8 @@
             </li>
             <li>
               <BooleanSetting
-                :disabled="hideFilteredStatuses"
                 v-if="user"
+                :disabled="hideFilteredStatuses"
                 path="hideMutedThreads"
               >
                 {{ $t('settings.hide_muted_threads') }}
@@ -30,8 +30,8 @@
             </li>
             <li>
               <BooleanSetting
-                :disabled="hideFilteredStatuses"
                 v-if="user"
+                :disabled="hideFilteredStatuses"
                 path="hideMutedPosts"
               >
                 {{ $t('settings.hide_muted_posts') }}
@@ -40,15 +40,18 @@
           </ul>
         </li>
         <li>
-          <BooleanSetting path="hidePostStats" expert="1">
+          <BooleanSetting
+            path="hidePostStats"
+            expert="1"
+          >
             {{ $t('settings.hide_post_stats') }}
           </BooleanSetting>
         </li>
         <ChoiceSetting
+          v-if="user"
           id="replyVisibility"
           path="replyVisibility"
           :options="replyVisibilityOptions"
-          v-if="user"
         >
           {{ $t('settings.replies_in_timeline') }}
         </ChoiceSetting>
@@ -87,7 +90,10 @@
         </li>
       </ul>
     </div>
-    <div class="setting-item" v-if="expertLevel > 0">
+    <div
+      v-if="expertLevel > 0"
+      class="setting-item"
+    >
       <h2>{{ $t('settings.user_profiles') }}</h2>
       <ul class="setting-list">
         <li>
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 9e4e282f..fe7deb6e 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -41,7 +41,7 @@ const GeneralTab = {
     ChoiceSetting,
     InterfaceLanguageSwitcher,
     ScopeSelector,
-    ServerSideIndicator,
+    ServerSideIndicator
   },
   computed: {
     postFormats () {
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 7944b03c..d4196c3d 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -27,7 +27,7 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="streaming" >
+          <BooleanSetting path="streaming">
             {{ $t('settings.streaming') }}
           </BooleanSetting>
           <ul
@@ -45,7 +45,10 @@
           </ul>
         </li>
         <li>
-          <BooleanSetting path="useStreamingApi" expert="1">
+          <BooleanSetting
+            path="useStreamingApi"
+            expert="1"
+          >
             {{ $t('settings.useStreamingApi') }}
             <br>
             <small>
@@ -54,22 +57,34 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="virtualScrolling" expert="1">
+          <BooleanSetting
+            path="virtualScrolling"
+            expert="1"
+          >
             {{ $t('settings.virtual_scrolling') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="alwaysShowNewPostButton" expert="1">
+          <BooleanSetting
+            path="alwaysShowNewPostButton"
+            expert="1"
+          >
             {{ $t('settings.always_show_post_button') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="autohideFloatingPostButton" expert="1">
+          <BooleanSetting
+            path="autohideFloatingPostButton"
+            expert="1"
+          >
             {{ $t('settings.autohide_floating_post_button') }}
           </BooleanSetting>
         </li>
         <li v-if="instanceShoutboxPresent">
-          <BooleanSetting path="hideShoutbox" expert="1">
+          <BooleanSetting
+            path="hideShoutbox"
+            expert="1"
+          >
             {{ $t('settings.hide_shoutbox') }}
           </BooleanSetting>
         </li>
@@ -84,18 +99,28 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="emojiReactionsOnTimeline" expert="1">
+          <BooleanSetting
+            path="emojiReactionsOnTimeline"
+            expert="1"
+          >
             {{ $t('settings.emoji_reactions_on_timeline') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="serverSide_stripRichContent" expert="1" v-if="user">
+          <BooleanSetting
+            v-if="user"
+            path="serverSide_stripRichContent"
+            expert="1"
+          >
             {{ $t('settings.no_rich_text_description') }}
           </BooleanSetting>
         </li>
         <h3>{{ $t('settings.attachments') }}</h3>
         <li>
-          <BooleanSetting path="useContainFit" expert="1">
+          <BooleanSetting
+            path="useContainFit"
+            expert="1"
+          >
             {{ $t('settings.use_contain_fit') }}
           </BooleanSetting>
         </li>
@@ -107,7 +132,8 @@
         <ul class="setting-list suboptions">
           <li>
             <BooleanSetting
-              path="preloadImage" expert="1"
+              path="preloadImage"
+              expert="1"
               :disabled="!hideNsfw"
             >
               {{ $t('settings.preload_images') }}
@@ -115,7 +141,8 @@
           </li>
           <li>
             <BooleanSetting
-              path="useOneClickNsfw" expert="1"
+              path="useOneClickNsfw"
+              expert="1"
               :disabled="!hideNsfw"
             >
               {{ $t('settings.use_one_click_nsfw') }}
@@ -123,7 +150,10 @@
           </li>
         </ul>
         <li>
-          <BooleanSetting path="loopVideo" expert="1">
+          <BooleanSetting
+            path="loopVideo"
+            expert="1"
+          >
             {{ $t('settings.loop_video') }}
           </BooleanSetting>
           <ul
@@ -132,7 +162,8 @@
           >
             <li>
               <BooleanSetting
-                path="loopVideoSilentOnly" expert="1"
+                path="loopVideoSilentOnly"
+                expert="1"
                 :disabled="!loopVideo || !loopSilentAvailable"
               >
                 {{ $t('settings.loop_video_silent_only') }}
@@ -147,7 +178,10 @@
           </ul>
         </li>
         <li>
-          <BooleanSetting path="playVideosInModal" expert="1">
+          <BooleanSetting
+            path="playVideosInModal"
+            expert="1"
+          >
             {{ $t('settings.play_videos_in_modal') }}
           </BooleanSetting>
         </li>
@@ -165,51 +199,74 @@
           class="setting-list suboptions"
         >
           <li v-if="mentionLinkDisplay === 'short'">
-            <BooleanSetting path="mentionLinkShowTooltip" expert="1">
+            <BooleanSetting
+              path="mentionLinkShowTooltip"
+              expert="1"
+            >
               {{ $t('settings.mention_link_show_tooltip') }}
             </BooleanSetting>
           </li>
         </ul>
-          <li>
-            <BooleanSetting path="useAtIcon" expert="1">
-              {{ $t('settings.use_at_icon') }}
-            </BooleanSetting>
-          </li>
-          <li>
-            <BooleanSetting path="mentionLinkShowAvatar">
-              {{ $t('settings.mention_link_show_avatar') }}
-            </BooleanSetting>
-          </li>
-          <li>
-            <BooleanSetting path="mentionLinkFadeDomain" expert="1">
-              {{ $t('settings.mention_link_fade_domain') }}
-            </BooleanSetting>
-          </li>
-          <li v-if="user">
-            <BooleanSetting path="mentionLinkBoldenYou" expert="1">
-              {{ $t('settings.mention_link_bolden_you') }}
-            </BooleanSetting>
-          </li>
-        <h3 v-if="expertLevel > 0">{{ $t('settings.fun') }}</h3>
         <li>
-          <BooleanSetting path="greentext" expert="1">
+          <BooleanSetting
+            path="useAtIcon"
+            expert="1"
+          >
+            {{ $t('settings.use_at_icon') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="mentionLinkShowAvatar">
+            {{ $t('settings.mention_link_show_avatar') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting
+            path="mentionLinkFadeDomain"
+            expert="1"
+          >
+            {{ $t('settings.mention_link_fade_domain') }}
+          </BooleanSetting>
+        </li>
+        <li v-if="user">
+          <BooleanSetting
+            path="mentionLinkBoldenYou"
+            expert="1"
+          >
+            {{ $t('settings.mention_link_bolden_you') }}
+          </BooleanSetting>
+        </li>
+        <h3 v-if="expertLevel > 0">
+          {{ $t('settings.fun') }}
+        </h3>
+        <li>
+          <BooleanSetting
+            path="greentext"
+            expert="1"
+          >
             {{ $t('settings.greentext') }}
           </BooleanSetting>
         </li>
         <li v-if="user">
-          <BooleanSetting path="mentionLinkShowYous" expert="1">
+          <BooleanSetting
+            path="mentionLinkShowYous"
+            expert="1"
+          >
             {{ $t('settings.show_yous') }}
           </BooleanSetting>
         </li>
       </ul>
     </div>
 
-    <div class="setting-item" v-if="user">
+    <div
+      v-if="user"
+      class="setting-item"
+    >
       <h2>{{ $t('settings.composing') }}</h2>
       <ul class="setting-list">
         <li>
           <label for="default-vis">
-            {{ $t('settings.default_vis') }} <ServerSideIndicator :serverSide="true"/>
+            {{ $t('settings.default_vis') }} <ServerSideIndicator :server-side="true" />
             <ScopeSelector
               class="scope-selector"
               :show-all="true"
@@ -226,12 +283,18 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="scopeCopy" expert="1">
+          <BooleanSetting
+            path="scopeCopy"
+            expert="1"
+          >
             {{ $t('settings.scope_copy') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="alwaysShowSubjectInput" expert="1">
+          <BooleanSetting
+            path="alwaysShowSubjectInput"
+            expert="1"
+          >
             {{ $t('settings.subject_input_always_show') }}
           </BooleanSetting>
         </li>
@@ -255,22 +318,34 @@
           </ChoiceSetting>
         </li>
         <li>
-          <BooleanSetting path="minimalScopesMode" expert="1">
+          <BooleanSetting
+            path="minimalScopesMode"
+            expert="1"
+          >
             {{ $t('settings.minimal_scopes_mode') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="alwaysShowNewPostButton" expert="1">
+          <BooleanSetting
+            path="alwaysShowNewPostButton"
+            expert="1"
+          >
             {{ $t('settings.always_show_post_button') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="autohideFloatingPostButton" expert="1">
+          <BooleanSetting
+            path="autohideFloatingPostButton"
+            expert="1"
+          >
             {{ $t('settings.autohide_floating_post_button') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="padEmoji" expert="1">
+          <BooleanSetting
+            path="padEmoji"
+            expert="1"
+          >
             {{ $t('settings.pad_emoji') }}
           </BooleanSetting>
         </li>
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index 5e9ce91e..86be6095 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -46,16 +46,25 @@
       </ul>
     </div>
 
-    <div class="setting-item" v-if="expertLevel > 0">
+    <div
+      v-if="expertLevel > 0"
+      class="setting-item"
+    >
       <h2>{{ $t('settings.notification_setting_privacy') }}</h2>
       <ul class="setting-list">
         <li>
-          <BooleanSetting path="webPushNotifications" expert="1">
+          <BooleanSetting
+            path="webPushNotifications"
+            expert="1"
+          >
             {{ $t('settings.enable_web_push_notifications') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="serverSide_webPushHideContents" expert="1">
+          <BooleanSetting
+            path="serverSide_webPushHideContents"
+            expert="1"
+          >
             {{ $t('settings.notification_setting_hide_notification_contents') }}
           </BooleanSetting>
         </li>
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index 699fdcf4..e00f6e5b 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -101,7 +101,8 @@
       <p class="visibility-notice">
         {{ $t('settings.avatar_size_instruction') }}
       </p>
-      <div class="current-avatar-container"> <img
+      <div class="current-avatar-container">
+        <img
           :src="user.profile_image_url_original"
           class="current-avatar"
         >
@@ -243,7 +244,7 @@
               <BooleanSetting
                 path="serverSide_hideFollowersCount"
                 :disabled="!serverSide_hideFollowers"
-                >
+              >
                 {{ $t('settings.hide_followers_count_description') }}
               </BooleanSetting>
             </li>
@@ -261,7 +262,7 @@
               <BooleanSetting
                 path="serverSide_hideFollowsCount"
                 :disabled="!serverSide_hideFollows"
-                >
+              >
                 {{ $t('settings.hide_follows_count_description') }}
               </BooleanSetting>
             </li>
-- 
cgit v1.2.3-70-g09d2

