From c6d4c20982261b55b16dda59c3e657eb1feb0041 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 11 Mar 2021 16:11:44 +0200
Subject: Made Select component to make using styled selects easier

---
 src/components/settings_modal/tabs/general_tab.vue | 72 ++++++++--------------
 1 file changed, 27 insertions(+), 45 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 9228c78e..a0413cfa 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -87,59 +87,41 @@
         <li>
           <div>
             {{ $t('settings.subject_line_behavior') }}
-            <label
-              for="subjectLineBehavior"
-              class="select"
+            <Select
+              id="subjectLineBehavior"
+              v-model="subjectLineBehavior"
             >
-              <select
-                id="subjectLineBehavior"
-                v-model="subjectLineBehavior"
-              >
-                <option value="email">
-                  {{ $t('settings.subject_line_email') }}
-                  {{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }}
-                </option>
-                <option value="masto">
-                  {{ $t('settings.subject_line_mastodon') }}
-                  {{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
-                </option>
-                <option value="noop">
-                  {{ $t('settings.subject_line_noop') }}
-                  {{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }}
-                </option>
-              </select>
-              <FAIcon
-                class="select-down-icon"
-                icon="chevron-down"
-              />
-            </label>
+              <option value="email">
+                {{ $t('settings.subject_line_email') }}
+                {{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }}
+              </option>
+              <option value="masto">
+                {{ $t('settings.subject_line_mastodon') }}
+                {{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
+              </option>
+              <option value="noop">
+                {{ $t('settings.subject_line_noop') }}
+                {{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }}
+              </option>
+            </Select>
           </div>
         </li>
         <li v-if="postFormats.length > 0">
           <div>
             {{ $t('settings.post_status_content_type') }}
-            <label
-              for="postContentType"
-              class="select"
+            <Select
+              id="postContentType"
+              v-model="postContentType"
             >
-              <select
-                id="postContentType"
-                v-model="postContentType"
+              <option
+                v-for="postFormat in postFormats"
+                :key="postFormat"
+                :value="postFormat"
               >
-                <option
-                  v-for="postFormat in postFormats"
-                  :key="postFormat"
-                  :value="postFormat"
-                >
-                  {{ $t(`post_status.content_type["${postFormat}"]`) }}
-                  {{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }}
-                </option>
-              </select>
-              <FAIcon
-                class="select-down-icon"
-                icon="chevron-down"
-              />
-            </label>
+                {{ $t(`post_status.content_type["${postFormat}"]`) }}
+                {{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }}
+              </option>
+            </Select>
           </div>
         </li>
         <li>
-- 
cgit v1.2.3-70-g09d2


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.js       | 34 ++++++++++++++++
 .../settings_modal/helpers/choice_setting.vue      | 29 ++++++++++++++
 .../settings_modal/tabs/filtering_tab.js           | 11 ++++--
 .../settings_modal/tabs/filtering_tab.vue          | 17 +++-----
 src/components/settings_modal/tabs/general_tab.js  | 18 +++++++--
 src/components/settings_modal/tabs/general_tab.vue | 46 ++++++----------------
 6 files changed, 103 insertions(+), 52 deletions(-)
 create mode 100644 src/components/settings_modal/helpers/choice_setting.js
 create mode 100644 src/components/settings_modal/helpers/choice_setting.vue

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js
new file mode 100644
index 00000000..042e8106
--- /dev/null
+++ b/src/components/settings_modal/helpers/choice_setting.js
@@ -0,0 +1,34 @@
+import { get, set } from 'lodash'
+import Select from 'src/components/select/select.vue'
+import ModifiedIndicator from './modified_indicator.vue'
+export default {
+  components: {
+    Select,
+    ModifiedIndicator
+  },
+  props: [
+    'path',
+    'disabled',
+    'options'
+  ],
+  computed: {
+    pathDefault () {
+      const [firstSegment, ...rest] = this.path.split('.')
+      return [firstSegment + 'DefaultValue', ...rest].join('.')
+    },
+    state () {
+      return get(this.$parent, this.path)
+    },
+    defaultState () {
+      return get(this.$parent, this.pathDefault)
+    },
+    isChanged () {
+      return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
+    }
+  },
+  methods: {
+    update (e) {
+      set(this.$parent, this.path, e)
+    }
+  }
+}
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>
diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js
index 89199d85..4eaf4217 100644
--- a/src/components/settings_modal/tabs/filtering_tab.js
+++ b/src/components/settings_modal/tabs/filtering_tab.js
@@ -1,18 +1,23 @@
 import { filter, trim } from 'lodash'
 import BooleanSetting from '../helpers/boolean_setting.vue'
-import Select from '../../select/select.vue'
+import ChoiceSetting from '../helpers/choice_setting.vue'
 
 import SharedComputedObject from '../helpers/shared_computed_object.js'
 
 const FilteringTab = {
   data () {
     return {
-      muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n')
+      muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n'),
+      replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.reply_visibility_${mode}`)
+      }))
     }
   },
   components: {
     BooleanSetting,
-    Select
+    ChoiceSetting
   },
   computed: {
     ...SharedComputedObject(),
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 4023fbe2..fb5fc375 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -36,20 +36,13 @@
           </li>
         </ul>
       </div>
-      <div>
-        {{ $t('settings.replies_in_timeline') }}
-        <Select
+      <ChoiceSetting
           id="replyVisibility"
-          v-model="replyVisibility"
+          path="replyVisibility"
+          :options="replyVisibilityOptions"
         >
-          <option
-            value="all"
-            selected
-          >{{ $t('settings.reply_visibility_all') }}</option>
-          <option value="following">{{ $t('settings.reply_visibility_following') }}</option>
-          <option value="self">{{ $t('settings.reply_visibility_self') }}</option>
-        </Select>
-      </div>
+        {{ $t('settings.replies_in_timeline') }}
+      </ChoiceSetting>
       <div>
         <BooleanSetting path="hidePostStats">
           {{ $t('settings.hide_post_stats') }}
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 03648d0b..07fccf57 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -1,5 +1,5 @@
 import BooleanSetting from '../helpers/boolean_setting.vue'
-import Select from '../../select/select.vue'
+import ChoiceSetting from '../helpers/choice_setting.vue'
 import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
 
 import SharedComputedObject from '../helpers/shared_computed_object.js'
@@ -15,6 +15,11 @@ library.add(
 const GeneralTab = {
   data () {
     return {
+      subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
+      })),
       loopSilentAvailable:
       // Firefox
       Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
@@ -26,13 +31,20 @@ const GeneralTab = {
   },
   components: {
     BooleanSetting,
-    InterfaceLanguageSwitcher,
-    Select
+    ChoiceSetting,
+    InterfaceLanguageSwitcher
   },
   computed: {
     postFormats () {
       return this.$store.state.instance.postFormats || []
     },
+    postContentOptions () {
+      return this.postFormats.map(format => ({
+        key: format,
+        value: format,
+        label: this.$t(`post_status.content_type["${format}"]`)
+      }))
+    },
     instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
     instanceWallpaperUsed () {
       return this.$store.state.instance.background &&
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index a0413cfa..7fb82c06 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -85,44 +85,22 @@
           </BooleanSetting>
         </li>
         <li>
-          <div>
+          <ChoiceSetting
+            id="subjectLineBehavior"
+            path="subjectLineBehavior"
+            :options="subjectLineOptions"
+          >
             {{ $t('settings.subject_line_behavior') }}
-            <Select
-              id="subjectLineBehavior"
-              v-model="subjectLineBehavior"
-            >
-              <option value="email">
-                {{ $t('settings.subject_line_email') }}
-                {{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }}
-              </option>
-              <option value="masto">
-                {{ $t('settings.subject_line_mastodon') }}
-                {{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
-              </option>
-              <option value="noop">
-                {{ $t('settings.subject_line_noop') }}
-                {{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }}
-              </option>
-            </Select>
-          </div>
+          </ChoiceSetting>
         </li>
         <li v-if="postFormats.length > 0">
-          <div>
+          <ChoiceSetting
+            id="postContentType"
+            path="postContentType"
+            :options="postContentOptions"
+          >
             {{ $t('settings.post_status_content_type') }}
-            <Select
-              id="postContentType"
-              v-model="postContentType"
-            >
-              <option
-                v-for="postFormat in postFormats"
-                :key="postFormat"
-                :value="postFormat"
-              >
-                {{ $t(`post_status.content_type["${postFormat}"]`) }}
-                {{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }}
-              </option>
-            </Select>
-          </div>
+          </ChoiceSetting>
         </li>
         <li>
           <BooleanSetting path="minimalScopesMode">
-- 
cgit v1.2.3-70-g09d2


From 7341b8a5516621985a76456e1eefc58cd23ff2e2 Mon Sep 17 00:00:00 2001
From: eris <femmediscord@gmail.com>
Date: Wed, 26 May 2021 22:22:55 +0000
Subject: Implement right sidebar option as user option

---
 CHANGELOG.md                                       | 1 +
 src/App.js                                         | 2 +-
 src/components/settings_modal/tabs/general_tab.vue | 5 +++++
 src/i18n/en.json                                   | 1 +
 src/modules/config.js                              | 1 +
 5 files changed, 9 insertions(+), 1 deletion(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cef6d401..905d9f65 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Added a quick settings to timeline header for easier access
 - Added option to mark posts as sensitive by default
 - Added quick filters for notifications
+- Implemented user option to change sidebar position to the right side
 
 
 ## [2.3.0] - 2021-03-01
diff --git a/src/App.js b/src/App.js
index 1ca029b6..231c6ae1 100644
--- a/src/App.js
+++ b/src/App.js
@@ -77,7 +77,7 @@ export default {
     privateMode () { return this.$store.state.instance.private },
     sidebarAlign () {
       return {
-        'order': this.$store.state.instance.sidebarRight ? 99 : 0
+        'order': this.$store.getters.mergedConfig.sidebarRight ? 99 : 0
       }
     },
     ...mapGetters(['mergedConfig'])
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 9228c78e..7bae4a32 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -11,6 +11,11 @@
             {{ $t('settings.hide_isp') }}
           </BooleanSetting>
         </li>
+        <li>
+          <BooleanSetting path="sidebarRight">
+            {{ $t('settings.right_sidebar') }}
+          </BooleanSetting>
+        </li>
         <li v-if="instanceWallpaperUsed">
           <BooleanSetting path="hideInstanceWallpaper">
             {{ $t('settings.hide_wallpaper') }}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index d6138482..1b38e29a 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -345,6 +345,7 @@
     "hide_all_muted_posts": "Hide muted posts",
     "max_thumbnails": "Maximum amount of thumbnails per post",
     "hide_isp": "Hide instance-specific panel",
+    "right_sidebar": "Show sidebar on the right side",
     "hide_wallpaper": "Hide instance wallpaper",
     "preload_images": "Preload images",
     "use_one_click_nsfw": "Open NSFW attachments with just one click",
diff --git a/src/modules/config.js b/src/modules/config.js
index 629588a8..0f596750 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -54,6 +54,7 @@ export const defaultState = {
   interfaceLanguage: browserLocale,
   hideScopeNotice: false,
   useStreamingApi: false,
+  sidebarRight: undefined, // instance default
   scopeCopy: undefined, // instance default
   subjectLineBehavior: undefined, // instance default
   alwaysShowSubjectInput: undefined, // instance default
-- 
cgit v1.2.3-70-g09d2


From b87a9d6675cf7cd8d37d45e62013fab27cbc46e6 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 8 Jun 2021 16:14:01 +0300
Subject: Rearranged settings, moved more stuff to filtering where apllicable.
 Changed how filering works.

---
 src/components/notification/notification.scss      |   6 +
 src/components/notification/notification.vue       |   1 +
 src/components/notifications/notifications.scss    |   5 -
 .../settings_modal/tabs/filtering_tab.vue          | 195 ++++++++++++++-------
 src/components/settings_modal/tabs/general_tab.vue | 154 ++++++----------
 src/components/status/status.js                    |  44 +++--
 src/components/timeline/timeline_quick_settings.js |   3 +-
 src/i18n/en.json                                   |  14 +-
 src/modules/config.js                              |   2 +
 src/modules/instance.js                            |   3 +
 10 files changed, 242 insertions(+), 185 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/notification/notification.scss b/src/components/notification/notification.scss
index f5905560..e075747b 100644
--- a/src/components/notification/notification.scss
+++ b/src/components/notification/notification.scss
@@ -2,6 +2,12 @@
 
 // TODO Copypaste from Status, should unify it somehow
 .Notification {
+  border-bottom: 1px solid;
+  border-color: $fallback--border;
+  border-color: var(--border, $fallback--border);
+  word-wrap: break-word;
+  word-break: break-word;
+
   &.-muted {
     padding: 0.25em 0.6em;
     height: 1.2em;
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index 0081dee4..fb324cec 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -1,6 +1,7 @@
 <template>
   <Status
     v-if="notification.type === 'mention'"
+    class="Notification"
     :compact="true"
     :statusoid="notification.status"
   />
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index 2bb627a8..b513d8b5 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -37,11 +37,6 @@
 
 .notification {
   box-sizing: border-box;
-  border-bottom: 1px solid;
-  border-color: $fallback--border;
-  border-color: var(--border, $fallback--border);
-  word-wrap: break-word;
-  word-break: break-word;
 
   &:hover .animated.Avatar {
     canvas {
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 6fc9ceaa..5d3c614a 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -1,73 +1,138 @@
 <template>
   <div :label="$t('settings.filtering')">
     <div class="setting-item">
-      <div 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>
-      </div>
-      <ChoiceSetting
-        id="replyVisibility"
-        path="replyVisibility"
-        :options="replyVisibilityOptions"
-      >
-        {{ $t('settings.replies_in_timeline') }}
-      </ChoiceSetting>
-      <div>
-        <BooleanSetting path="hidePostStats">
-          {{ $t('settings.hide_post_stats') }}
-        </BooleanSetting>
-      </div>
-      <div>
-        <BooleanSetting path="hideUserStats">
-          {{ $t('settings.hide_user_stats') }}
-        </BooleanSetting>
-      </div>
+      <h2>{{ $t('settings.posts') }}</h2>
+      <ul class="setting-list">
+        <li>
+          <BooleanSetting path="hideFilteredStatuses">
+            {{ $t('settings.hide_filtered_statuses') }}
+          </BooleanSetting>
+          <ul
+            class="setting-list suboptions"
+            :class="[{disabled: !streaming}]"
+          >
+            <li>
+              <BooleanSetting
+                :disabled="hideFilteredStatuses"
+                path="hideWordFilteredPosts"
+              >
+                {{ $t('settings.hide_wordfiltered_statuses') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting
+                :disabled="hideFilteredStatuses"
+                path="hideMutedThreads"
+              >
+                {{ $t('settings.hide_muted_threads') }}
+              </BooleanSetting>
+            </li>
+            <li>
+              <BooleanSetting
+                :disabled="hideFilteredStatuses"
+                path="hideMutedPosts"
+                >
+                {{ $t('settings.hide_muted_posts') }}
+              </BooleanSetting>
+            </li>
+          </ul>
+        </li>
+        <li>
+          <BooleanSetting path="hidePostStats">
+            {{ $t('settings.hide_post_stats') }}
+          </BooleanSetting>
+        </li>
+        <ChoiceSetting
+          id="replyVisibility"
+          path="replyVisibility"
+          :options="replyVisibilityOptions"
+        >
+          {{ $t('settings.replies_in_timeline') }}
+        </ChoiceSetting>
+        <li>
+          <h3>{{ $t('settings.wordfilter') }}</h3>
+          <textarea
+            id="muteWords"
+            v-model="muteWordsString"
+            class="resize-height"
+          />
+          <div>{{ $t('settings.filtering_explanation') }}</div>
+        </li>
+        <h3>{{ $t('settings.attachments') }}</h3>
+        <li>
+          <label for="maxThumbnails">
+            {{ $t('settings.max_thumbnails') }}
+          </label>
+          <input
+            id="maxThumbnails"
+            path.number="maxThumbnails"
+            class="number-input"
+            type="number"
+            min="0"
+            step="1"
+          >
+        </li>
+        <li>
+          <BooleanSetting path="hideAttachments">
+            {{ $t('settings.hide_attachments_in_tl') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="hideAttachmentsInConv">
+            {{ $t('settings.hide_attachments_in_convo') }}
+          </BooleanSetting>
+        </li>
+      </ul>
     </div>
     <div class="setting-item">
-      <div>
-        <p>{{ $t('settings.filtering_explanation') }}</p>
-        <textarea
-          id="muteWords"
-          v-model="muteWordsString"
-          class="resize-height"
-        />
-      </div>
-      <div>
-        <BooleanSetting path="hideFilteredStatuses">
-          {{ $t('settings.hide_filtered_statuses') }}
-        </BooleanSetting>
-      </div>
+      <h2>{{ $t('settings.user_profiles') }}</h2>
+      <ul class="setting-list">
+        <li>
+          <BooleanSetting path="hideUserStats">
+            {{ $t('settings.hide_user_stats') }}
+          </BooleanSetting>
+        </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>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index bdb29edf..5243d336 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -21,19 +21,9 @@
             {{ $t('settings.hide_wallpaper') }}
           </BooleanSetting>
         </li>
-      </ul>
-    </div>
-    <div class="setting-item">
-      <h2>{{ $t('nav.timeline') }}</h2>
-      <ul class="setting-list">
         <li>
-          <BooleanSetting path="hideMutedPosts">
-            {{ $t('settings.hide_muted_posts') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="collapseMessageWithSubject">
-            {{ $t('settings.collapse_subject') }}
+          <BooleanSetting path="stopGifs">
+            {{ $t('settings.stop_gifs') }}
           </BooleanSetting>
         </li>
         <li>
@@ -63,98 +53,36 @@
             </small>
           </BooleanSetting>
         </li>
-        <li>
-          <BooleanSetting path="emojiReactionsOnTimeline">
-            {{ $t('settings.emoji_reactions_on_timeline') }}
-          </BooleanSetting>
-        </li>
         <li>
           <BooleanSetting path="virtualScrolling">
             {{ $t('settings.virtual_scrolling') }}
           </BooleanSetting>
         </li>
-      </ul>
-    </div>
-
-    <div class="setting-item">
-      <h2>{{ $t('settings.composing') }}</h2>
-      <ul class="setting-list">
-        <li>
-          <BooleanSetting path="scopeCopy">
-            {{ $t('settings.scope_copy') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="alwaysShowSubjectInput">
-            {{ $t('settings.subject_input_always_show') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <ChoiceSetting
-            id="subjectLineBehavior"
-            path="subjectLineBehavior"
-            :options="subjectLineOptions"
-          >
-            {{ $t('settings.subject_line_behavior') }}
-          </ChoiceSetting>
-        </li>
-        <li v-if="postFormats.length > 0">
-          <ChoiceSetting
-            id="postContentType"
-            path="postContentType"
-            :options="postContentOptions"
-          >
-            {{ $t('settings.post_status_content_type') }}
-          </ChoiceSetting>
-        </li>
-        <li>
-          <BooleanSetting path="minimalScopesMode">
-            {{ $t('settings.minimal_scopes_mode') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="sensitiveByDefault">
-            {{ $t('settings.sensitive_by_default') }}
-          </BooleanSetting>
-        </li>
         <li>
           <BooleanSetting path="autohideFloatingPostButton">
             {{ $t('settings.autohide_floating_post_button') }}
           </BooleanSetting>
         </li>
-        <li>
-          <BooleanSetting path="padEmoji">
-            {{ $t('settings.pad_emoji') }}
-          </BooleanSetting>
-        </li>
       </ul>
     </div>
-
     <div class="setting-item">
-      <h2>{{ $t('settings.attachments') }}</h2>
+      <h2>{{ $t('settings.post_look_feel') }}</h2>
       <ul class="setting-list">
         <li>
-          <BooleanSetting path="hideAttachments">
-            {{ $t('settings.hide_attachments_in_tl') }}
+          <BooleanSetting path="collapseMessageWithSubject">
+            {{ $t('settings.collapse_subject') }}
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="hideAttachmentsInConv">
-            {{ $t('settings.hide_attachments_in_convo') }}
+          <BooleanSetting path="emojiReactionsOnTimeline">
+            {{ $t('settings.emoji_reactions_on_timeline') }}
           </BooleanSetting>
         </li>
+        <h3>{{ $t('settings.attachments') }}</h3>
         <li>
-          <label for="maxThumbnails">
-            {{ $t('settings.max_thumbnails') }}
-          </label>
-          <input
-            id="maxThumbnails"
-            path.number="maxThumbnails"
-            class="number-input"
-            type="number"
-            min="0"
-            step="1"
-          >
+          <BooleanSetting path="useContainFit">
+            {{ $t('settings.use_contain_fit') }}
+          </BooleanSetting>
         </li>
         <li>
           <BooleanSetting path="hideNsfw">
@@ -179,11 +107,6 @@
             </BooleanSetting>
           </li>
         </ul>
-        <li>
-          <BooleanSetting path="stopGifs">
-            {{ $t('settings.stop_gifs') }}
-          </BooleanSetting>
-        </li>
         <li>
           <BooleanSetting path="loopVideo">
             {{ $t('settings.loop_video') }}
@@ -213,31 +136,70 @@
             {{ $t('settings.play_videos_in_modal') }}
           </BooleanSetting>
         </li>
+        <h3>{{ $t('settings.fun') }}</h3>
         <li>
-          <BooleanSetting path="useContainFit">
-            {{ $t('settings.use_contain_fit') }}
+          <BooleanSetting path="greentext">
+            {{ $t('settings.greentext') }}
           </BooleanSetting>
         </li>
       </ul>
     </div>
 
     <div class="setting-item">
-      <h2>{{ $t('settings.notifications') }}</h2>
+      <h2>{{ $t('settings.composing') }}</h2>
       <ul class="setting-list">
         <li>
-          <BooleanSetting path="webPushNotifications">
-            {{ $t('settings.enable_web_push_notifications') }}
+          <BooleanSetting path="scopeCopy">
+            {{ $t('settings.scope_copy') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="alwaysShowSubjectInput">
+            {{ $t('settings.subject_input_always_show') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <ChoiceSetting
+            id="subjectLineBehavior"
+            path="subjectLineBehavior"
+            :options="subjectLineOptions"
+          >
+            {{ $t('settings.subject_line_behavior') }}
+          </ChoiceSetting>
+        </li>
+        <li v-if="postFormats.length > 0">
+          <ChoiceSetting
+            id="postContentType"
+            path="postContentType"
+            :options="postContentOptions"
+          >
+            {{ $t('settings.post_status_content_type') }}
+          </ChoiceSetting>
+        </li>
+        <li>
+          <BooleanSetting path="minimalScopesMode">
+            {{ $t('settings.minimal_scopes_mode') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="sensitiveByDefault">
+            {{ $t('settings.sensitive_by_default') }}
+          </BooleanSetting>
+        </li>
+        <li>
+          <BooleanSetting path="padEmoji">
+            {{ $t('settings.pad_emoji') }}
           </BooleanSetting>
         </li>
       </ul>
     </div>
 
     <div class="setting-item">
-      <h2>{{ $t('settings.fun') }}</h2>
+      <h2>{{ $t('settings.notifications') }}</h2>
       <ul class="setting-list">
         <li>
-          <BooleanSetting path="greentext">
-            {{ $t('settings.greentext') }}
+          <BooleanSetting path="webPushNotifications">
+            {{ $t('settings.enable_web_push_notifications') }}
           </BooleanSetting>
         </li>
       </ul>
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 470c01f1..5c7caa28 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -157,26 +157,32 @@ const Status = {
       return muteWordHits(this.status, this.muteWords)
     },
     muted () {
+      if (this.statusoid.user.id === this.currentUser.id) return false
+      const reasonsToMute = this.userIsMuted ||
+        // Thread is muted
+        status.thread_muted ||
+        // Wordfiltered
+        this.muteWordHits.length > 0
+      return !this.unmuted && !this.shouldNotMute && reasonsToMute
+    },
+    userIsMuted () {
       if (this.statusoid.user.id === this.currentUser.id) return false
       const { status } = this
       const { reblog } = status
       const relationship = this.$store.getters.relationship(status.user.id)
       const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id)
-      const reasonsToMute = (
-        // Post is muted according to BE
-        status.muted ||
+      return status.muted ||
         // Reprööt of a muted post according to BE
         (reblog && reblog.muted) ||
         // Muted user
         relationship.muting ||
         // Muted user of a reprööt
-        (relationshipReblog && relationshipReblog.muting) ||
-        // Thread is muted
-        status.thread_muted ||
-        // Wordfiltered
-        this.muteWordHits.length > 0
-      )
-      const excusesNotToMute = (
+        (relationshipReblog && relationshipReblog.muting)
+    },
+    shouldNotMute () {
+      const { status } = this
+      const { reblog } = status
+      return (
         (
           this.inProfile && (
             // Don't mute user's posts on user timeline (except reblogs)
@@ -189,14 +195,26 @@ const Status = {
         (this.inConversation && status.thread_muted)
         // No excuses if post has muted words
       ) && !this.muteWordHits.length > 0
-
-      return !this.unmuted && !excusesNotToMute && reasonsToMute
+    },
+    hideMutedUsers () {
+      return this.mergedConfig.hideMutedPosts
+    },
+    hideMutedThreads () {
+      return this.mergedConfig.hideMutedThreads
     },
     hideFilteredStatuses () {
       return this.mergedConfig.hideFilteredStatuses
     },
+    hideWordFilteredPosts () {
+      return this.mergedConfig.hideWordFilteredPosts
+    },
     hideStatus () {
-      return (this.muted && this.hideFilteredStatuses) || this.virtualHidden
+      return (this.virtualHidden || !this.shouldNotMute) && (
+        (this.muted && this.hideFilteredStatuses) ||
+        (this.userIsMuted && this.hideMutedUsers) ||
+        (this.status.thread_muted && this.hideMutedThreads) ||
+        (this.muteWordHits.length > 0 && this.hideWordFilteredPosts)
+      )
     },
     isFocused () {
       // retweet or root of an expanded conversation
diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js
index eae65a55..7b4931ce 100644
--- a/src/components/timeline/timeline_quick_settings.js
+++ b/src/components/timeline/timeline_quick_settings.js
@@ -48,10 +48,9 @@ const TimelineQuickSettings = {
       }
     },
     hideMutedPosts: {
-      get () { return this.mergedConfig.hideMutedPosts || this.mergedConfig.hideFilteredStatuses },
+      get () { return this.mergedConfig.hideFilteredStatuses },
       set () {
         const value = !this.hideMutedPosts
-        this.$store.dispatch('setOption', { name: 'hideMutedPosts', value })
         this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
       }
     }
diff --git a/src/i18n/en.json b/src/i18n/en.json
index aa440ac1..89e808aa 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -259,6 +259,7 @@
     "security": "Security",
     "setting_changed": "Setting is different from default",
     "enter_current_password_to_confirm": "Enter your current password to confirm your identity",
+    "post_look_feel": "Posts Look & Feel",
     "mfa": {
       "otp": "OTP",
       "setup_otp": "Setup OTP",
@@ -331,6 +332,7 @@
     "emoji_reactions_on_timeline": "Show emoji reactions on timeline",
     "export_theme": "Save preset",
     "filtering": "Filtering",
+    "wordfilter": "Wordfilter",
     "filtering_explanation": "All statuses containing these words will be muted, one per line",
     "word_filter": "Word filter",
     "follow_export": "Follow export",
@@ -346,7 +348,7 @@
     "hide_media_previews": "Hide media previews",
     "hide_muted_posts": "Hide posts of muted users",
     "hide_all_muted_posts": "Hide muted posts",
-    "max_thumbnails": "Maximum amount of thumbnails per post",
+    "max_thumbnails": "Maximum amount of thumbnails per post (empty = no limit)",
     "hide_isp": "Hide instance-specific panel",
     "right_sidebar": "Show sidebar on the right side",
     "hide_wallpaper": "Hide instance wallpaper",
@@ -354,7 +356,9 @@
     "use_one_click_nsfw": "Open NSFW attachments with just one click",
     "hide_post_stats": "Hide post statistics (e.g. the number of favorites)",
     "hide_user_stats": "Hide user statistics (e.g. the number of followers)",
-    "hide_filtered_statuses": "Hide filtered statuses",
+    "hide_filtered_statuses": "Hide all filtered posts",
+    "hide_wordfiltered_statuses": "Hide word-filtered statuses",
+    "hide_muted_threads": "Hide muted threads",
     "import_blocks_from_a_csv_file": "Import blocks from a csv file",
     "import_followers_from_a_csv_file": "Import follows from a csv file",
     "import_theme": "Load preset",
@@ -395,6 +399,8 @@
     "name_bio": "Name & bio",
     "new_email": "New email",
     "new_password": "New password",
+    "posts": "Posts",
+    "user_profiles": "User Profiles",
     "notification_visibility": "Types of notifications to show",
     "notification_visibility_follows": "Follows",
     "notification_visibility_likes": "Favorites",
@@ -411,7 +417,7 @@
     "hide_followers_count_description": "Don't show follower count",
     "show_admin_badge": "Show \"Admin\" badge in my profile",
     "show_moderator_badge": "Show \"Moderator\" badge in my profile",
-    "nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses",
+    "nsfw_clickthrough": "Censor NSFW content",
     "oauth_tokens": "OAuth tokens",
     "token": "Token",
     "refresh_token": "Refresh token",
@@ -455,7 +461,7 @@
     "subject_line_noop": "Do not copy",
     "post_status_content_type": "Post status content type",
     "sensitive_by_default": "Mark posts as sensitive by default",
-    "stop_gifs": "Play-on-hover GIFs",
+    "stop_gifs": "Pause animated images until you hover on them",
     "streaming": "Enable automatic streaming of new posts when scrolled to the top",
     "user_mutes": "Users",
     "useStreamingApi": "Receive posts and notifications real-time",
diff --git a/src/modules/config.js b/src/modules/config.js
index 0f596750..df66bfe6 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -23,6 +23,8 @@ export const defaultState = {
   hideInstanceWallpaper: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: undefined, // instance default
+  hideMutedThreads: undefined, // instance default
+  hideWordFilteredPosts: undefined, // instance default
   collapseMessageWithSubject: undefined, // instance default
   padEmoji: true,
   hideAttachments: false,
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 539b9c66..60038f08 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -21,7 +21,10 @@ const defaultState = {
   collapseMessageWithSubject: false,
   greentext: false,
   hideFilteredStatuses: false,
+  // bad name: actually hides posts of muted USERS
   hideMutedPosts: false,
+  hideMutedThreads: true,
+  hideWordFilteredPosts: false,
   hidePostStats: false,
   hideSitename: false,
   hideUserStats: false,
-- 
cgit v1.2.3-70-g09d2


From adfe56a3a31b3c4a8c2c206c06bebf9c4af675d1 Mon Sep 17 00:00:00 2001
From: Eris <femmediscord@gmail.com>
Date: Mon, 14 Jun 2021 17:54:40 +0000
Subject: New option: Always show floating New Post button

---
 .gitignore                                                       | 2 ++
 CHANGELOG.md                                                     | 2 +-
 CONTRIBUTORS.md                                                  | 1 +
 src/App.js                                                       | 3 +++
 src/App.vue                                                      | 1 +
 .../mobile_post_status_button/mobile_post_status_button.js       | 3 +++
 .../mobile_post_status_button/mobile_post_status_button.vue      | 4 ++--
 src/components/settings_modal/tabs/general_tab.vue               | 5 +++++
 src/components/shout_panel/shout_panel.vue                       | 9 ++++++++-
 src/i18n/en.json                                                 | 1 +
 src/modules/config.js                                            | 1 +
 11 files changed, 28 insertions(+), 4 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/.gitignore b/.gitignore
index 479d57c4..8b23774f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@ test/e2e/reports
 selenium-debug.log
 .idea/
 config/local.json
+.gitignore
+config/local.example.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 905d9f65..4651c01d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Added option to mark posts as sensitive by default
 - Added quick filters for notifications
 - Implemented user option to change sidebar position to the right side
-
+- Implemented user option to always show floating New Post button (normally mobile-only)
 
 ## [2.3.0] - 2021-03-01
 ### Fixed
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index d7c217ce..f666a4ef 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -3,6 +3,7 @@ Contributors of this project.
 - Constance Variable (lambadalambda@social.heldscal.la): Code
 - Coco Snuss (cocosnuss@social.heldscal.la): Code
 - wakarimasen (wakarimasen@shitposter.club): NSFW hiding image
+- eris (eris@disqordia.space): Code
 - dtluna (dtluna@social.heldscal.la): Code
 - sonyam (sonyam@social.heldscal.la): Background images
 - hakui (hakui@freezepeach.xyz): CSS and styling
diff --git a/src/App.js b/src/App.js
index fe4c30cb..e7ea3851 100644
--- a/src/App.js
+++ b/src/App.js
@@ -73,6 +73,9 @@ export default {
         this.$store.state.instance.instanceSpecificPanelContent
     },
     showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
+    shoutboxPosition () {
+      return this.$store.getters.mergedConfig.showNewPostButton || false
+    },
     isMobileLayout () { return this.$store.state.interface.mobileLayout },
     privateMode () { return this.$store.state.instance.private },
     sidebarAlign () {
diff --git a/src/App.vue b/src/App.vue
index 6c582c03..6b867498 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -53,6 +53,7 @@
       v-if="currentUser && shout"
       :floating="true"
       class="floating-shout mobile-hidden"
+      :class="{ 'left': shoutboxPosition }"
     />
     <MobilePostStatusButton />
     <UserReportingModal />
diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.js b/src/components/mobile_post_status_button/mobile_post_status_button.js
index 366ea89c..d27fb3b8 100644
--- a/src/components/mobile_post_status_button/mobile_post_status_button.js
+++ b/src/components/mobile_post_status_button/mobile_post_status_button.js
@@ -44,6 +44,9 @@ const MobilePostStatusButton = {
 
       return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
     },
+    isPersistent () {
+      return !!this.$store.getters.mergedConfig.showNewPostButton
+    },
     autohideFloatingPostButton () {
       return !!this.$store.getters.mergedConfig.autohideFloatingPostButton
     }
diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue
index 767f8244..37becf4c 100644
--- a/src/components/mobile_post_status_button/mobile_post_status_button.vue
+++ b/src/components/mobile_post_status_button/mobile_post_status_button.vue
@@ -2,7 +2,7 @@
   <div v-if="isLoggedIn">
     <button
       class="button-default new-status-button"
-      :class="{ 'hidden': isHidden }"
+      :class="{ 'hidden': isHidden, 'always-show': isPersistent }"
       @click="openPostForm"
     >
       <FAIcon icon="pen" />
@@ -47,7 +47,7 @@
 }
 
 @media all and (min-width: 801px) {
-  .new-status-button {
+  .new-status-button:not(.always-show) {
     display: none;
   }
 }
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index bdb29edf..4c09d047 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -117,6 +117,11 @@
             {{ $t('settings.sensitive_by_default') }}
           </BooleanSetting>
         </li>
+        <li>
+          <BooleanSetting path="showNewPostButton">
+            {{ $t('settings.showPostButton') }}
+          </BooleanSetting>
+        </li>
         <li>
           <BooleanSetting path="autohideFloatingPostButton">
             {{ $t('settings.autohide_floating_post_button') }}
diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue
index f90baf80..c88797d1 100644
--- a/src/components/shout_panel/shout_panel.vue
+++ b/src/components/shout_panel/shout_panel.vue
@@ -79,12 +79,19 @@
 
 .floating-shout {
   position: fixed;
-  right: 0px;
   bottom: 0px;
   z-index: 1000;
   max-width: 25em;
 }
 
+.floating-shout.left {
+  left: 0px;
+}
+
+.floating-shout:not(.left) {
+  right: 0px;
+}
+
 .shout-panel {
   .shout-heading {
     cursor: pointer;
diff --git a/src/i18n/en.json b/src/i18n/en.json
index aa440ac1..26f51cea 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -349,6 +349,7 @@
     "max_thumbnails": "Maximum amount of thumbnails per post",
     "hide_isp": "Hide instance-specific panel",
     "right_sidebar": "Show sidebar on the right side",
+    "showPostButton": "Always show floating New Post button",
     "hide_wallpaper": "Hide instance wallpaper",
     "preload_images": "Preload images",
     "use_one_click_nsfw": "Open NSFW attachments with just one click",
diff --git a/src/modules/config.js b/src/modules/config.js
index 0f596750..30c77a7b 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -34,6 +34,7 @@ export const defaultState = {
   loopVideoSilentOnly: true,
   streaming: false,
   emojiReactionsOnTimeline: true,
+  showNewPostButton: false,
   autohideFloatingPostButton: false,
   pauseOnUnfocused: true,
   stopGifs: false,
-- 
cgit v1.2.3-70-g09d2


From 0c1014524232907925bf5fa59079dd06d0ef0472 Mon Sep 17 00:00:00 2001
From: Eris <femmediscord@gmail.com>
Date: Mon, 14 Jun 2021 19:42:56 +0000
Subject: New option: Hide shoutbox

---
 src/App.js                                         | 3 +++
 src/App.vue                                        | 2 +-
 src/components/settings_modal/tabs/general_tab.js  | 1 +
 src/components/settings_modal/tabs/general_tab.vue | 5 +++++
 src/i18n/en.json                                   | 1 +
 src/modules/config.js                              | 1 +
 6 files changed, 12 insertions(+), 1 deletion(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/App.js b/src/App.js
index fe4c30cb..8d9ec8c7 100644
--- a/src/App.js
+++ b/src/App.js
@@ -73,6 +73,9 @@ export default {
         this.$store.state.instance.instanceSpecificPanelContent
     },
     showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
+    hideSB () {
+      return this.$store.getters.mergedConfig.hideShoutbox
+    },
     isMobileLayout () { return this.$store.state.interface.mobileLayout },
     privateMode () { return this.$store.state.instance.private },
     sidebarAlign () {
diff --git a/src/App.vue b/src/App.vue
index 6c582c03..39decde9 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -50,7 +50,7 @@
       <media-modal />
     </div>
     <shout-panel
-      v-if="currentUser && shout"
+      v-if="currentUser && shout && !hideSB"
       :floating="true"
       class="floating-shout mobile-hidden"
     />
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 07fccf57..0e838e59 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -50,6 +50,7 @@ const GeneralTab = {
       return this.$store.state.instance.background &&
         !this.$store.state.users.currentUser.background_image
     },
+    shout () { return this.$store.state.shout.channel.state === 'joined' }, 
     ...SharedComputedObject()
   }
 }
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index bdb29edf..3a9af1ea 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -21,6 +21,11 @@
             {{ $t('settings.hide_wallpaper') }}
           </BooleanSetting>
         </li>
+        <li v-if="shout">
+          <BooleanSetting path="hideShoutbox">
+            {{ $t('settings.hide_shoutbox') }} {{ $t('shoutbox.title') }}
+          </BooleanSetting>
+        </li>
       </ul>
     </div>
     <div class="setting-item">
diff --git a/src/i18n/en.json b/src/i18n/en.json
index aa440ac1..79dcb919 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -349,6 +349,7 @@
     "max_thumbnails": "Maximum amount of thumbnails per post",
     "hide_isp": "Hide instance-specific panel",
     "right_sidebar": "Show sidebar on the right side",
+    "hide_shoutbox": "Hide ",
     "hide_wallpaper": "Hide instance wallpaper",
     "preload_images": "Preload images",
     "use_one_click_nsfw": "Open NSFW attachments with just one click",
diff --git a/src/modules/config.js b/src/modules/config.js
index 0f596750..bdab3f4d 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -21,6 +21,7 @@ export const defaultState = {
   customThemeSource: undefined,
   hideISP: false,
   hideInstanceWallpaper: false,
+  hideShoutbox: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: undefined, // instance default
   collapseMessageWithSubject: undefined, // instance default
-- 
cgit v1.2.3-70-g09d2


From 4639e30cb8e6086a57acbe35d864e8b453e197ba Mon Sep 17 00:00:00 2001
From: Eris <femmediscord@gmail.com>
Date: Mon, 14 Jun 2021 20:41:34 +0000
Subject: Fix config naming for consistency

---
 src/components/settings_modal/tabs/general_tab.vue | 4 ++--
 src/i18n/en.json                                   | 2 +-
 src/modules/config.js                              | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 4c09d047..44f6fa3e 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -118,8 +118,8 @@
           </BooleanSetting>
         </li>
         <li>
-          <BooleanSetting path="showNewPostButton">
-            {{ $t('settings.showPostButton') }}
+          <BooleanSetting path="alwaysShowNewPostButton">
+            {{ $t('settings.always_show_post_button') }}
           </BooleanSetting>
         </li>
         <li>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 26f51cea..0ee871de 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -349,7 +349,7 @@
     "max_thumbnails": "Maximum amount of thumbnails per post",
     "hide_isp": "Hide instance-specific panel",
     "right_sidebar": "Show sidebar on the right side",
-    "showPostButton": "Always show floating New Post button",
+    "always_show_post_button": "Always show floating New Post button",
     "hide_wallpaper": "Hide instance wallpaper",
     "preload_images": "Preload images",
     "use_one_click_nsfw": "Open NSFW attachments with just one click",
diff --git a/src/modules/config.js b/src/modules/config.js
index 30c77a7b..28ee651b 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -34,7 +34,7 @@ export const defaultState = {
   loopVideoSilentOnly: true,
   streaming: false,
   emojiReactionsOnTimeline: true,
-  showNewPostButton: false,
+  alwaysShowNewPostButton: false,
   autohideFloatingPostButton: false,
   pauseOnUnfocused: true,
   stopGifs: false,
-- 
cgit v1.2.3-70-g09d2


From 5047663c513841ea708f58e672bf193a55a96a82 Mon Sep 17 00:00:00 2001
From: Eris <femmediscord@gmail.com>
Date: Tue, 15 Jun 2021 00:25:09 +0000
Subject: Make locale language cleaner

---
 src/components/settings_modal/tabs/general_tab.vue | 2 +-
 src/i18n/en.json                                   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 3a9af1ea..8c255dd3 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -23,7 +23,7 @@
         </li>
         <li v-if="shout">
           <BooleanSetting path="hideShoutbox">
-            {{ $t('settings.hide_shoutbox') }} {{ $t('shoutbox.title') }}
+            {{ $t('general.hide') }} {{ $t('shoutbox.title') }}
           </BooleanSetting>
         </li>
       </ul>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 79dcb919..247b7167 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -75,6 +75,7 @@
     "confirm": "Confirm",
     "verify": "Verify",
     "close": "Close",
+    "hide": "Hide",
     "peek": "Peek",
     "role": {
       "admin": "Admin",
@@ -349,7 +350,6 @@
     "max_thumbnails": "Maximum amount of thumbnails per post",
     "hide_isp": "Hide instance-specific panel",
     "right_sidebar": "Show sidebar on the right side",
-    "hide_shoutbox": "Hide ",
     "hide_wallpaper": "Hide instance wallpaper",
     "preload_images": "Preload images",
     "use_one_click_nsfw": "Open NSFW attachments with just one click",
-- 
cgit v1.2.3-70-g09d2


From 7e3393b5a2eda7f696781853123de8df965ffbf2 Mon Sep 17 00:00:00 2001
From: Eris <femmediscord@gmail.com>
Date: Tue, 15 Jun 2021 00:59:36 +0000
Subject: Use cleaner instance config check for shoutbox setting

---
 src/components/settings_modal/tabs/general_tab.js  | 2 +-
 src/components/settings_modal/tabs/general_tab.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index be0fc768..eeda61bf 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -50,7 +50,7 @@ const GeneralTab = {
       return this.$store.state.instance.background &&
         !this.$store.state.users.currentUser.background_image
     },
-    shout () { return this.$store.state.shout.channel.state === 'joined' },
+    instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
     ...SharedComputedObject()
   }
 }
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 8c255dd3..84f8ef8d 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -21,7 +21,7 @@
             {{ $t('settings.hide_wallpaper') }}
           </BooleanSetting>
         </li>
-        <li v-if="shout">
+        <li v-if="instanceShoutboxPresent">
           <BooleanSetting path="hideShoutbox">
             {{ $t('general.hide') }} {{ $t('shoutbox.title') }}
           </BooleanSetting>
-- 
cgit v1.2.3-70-g09d2


From d7a53aec613c6da7f67906b16f052ec4e7441960 Mon Sep 17 00:00:00 2001
From: Eris <femmediscord@gmail.com>
Date: Tue, 15 Jun 2021 18:09:00 +0000
Subject: Use proper setting name

---
 src/components/settings_modal/tabs/general_tab.vue | 2 +-
 src/i18n/en.json                                   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 84f8ef8d..d3e71b31 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -23,7 +23,7 @@
         </li>
         <li v-if="instanceShoutboxPresent">
           <BooleanSetting path="hideShoutbox">
-            {{ $t('general.hide') }} {{ $t('shoutbox.title') }}
+            {{ $t('settings.hide_shoutbox') }}
           </BooleanSetting>
         </li>
       </ul>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 247b7167..6511019c 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -75,7 +75,6 @@
     "confirm": "Confirm",
     "verify": "Verify",
     "close": "Close",
-    "hide": "Hide",
     "peek": "Peek",
     "role": {
       "admin": "Admin",
@@ -349,6 +348,7 @@
     "hide_all_muted_posts": "Hide muted posts",
     "max_thumbnails": "Maximum amount of thumbnails per post",
     "hide_isp": "Hide instance-specific panel",
+    "hide_shoutbox": "Hide instance shoutbox",
     "right_sidebar": "Show sidebar on the right side",
     "hide_wallpaper": "Hide instance wallpaper",
     "preload_images": "Preload images",
-- 
cgit v1.2.3-70-g09d2


From 76547fe66d1771f5bff732a34b0547f890f4621a Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 10 Jan 2022 00:37:39 -0500
Subject: Add a pref for whether to display mention as icon or text

---
 src/components/mention_link/mention_link.js        | 3 +++
 src/components/mention_link/mention_link.vue       | 9 +++++----
 src/components/settings_modal/tabs/general_tab.vue | 5 +++++
 src/modules/config.js                              | 1 +
 src/modules/instance.js                            | 1 +
 5 files changed, 15 insertions(+), 4 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
index 65c62baa..637641af 100644
--- a/src/components/mention_link/mention_link.js
+++ b/src/components/mention_link/mention_link.js
@@ -85,6 +85,9 @@ const MentionLink = {
         this.highlightType
       ]
     },
+    useAtIcon () {
+      return this.mergedConfig.useAtIcon
+    },
     ...mapGetters(['mergedConfig']),
     ...mapState({
       currentUser: state => state.users.currentUser
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
index a22b486c..f633bf50 100644
--- a/src/components/mention_link/mention_link.vue
+++ b/src/components/mention_link/mention_link.vue
@@ -24,13 +24,14 @@
       >
         <!-- eslint-disable vue/no-v-html -->
         <FAIcon
+          v-if="useAtIcon"
           size="sm"
           icon="at"
           class="at"
-        /><span class="shortName"><span
-          class="userName"
-          v-html="userName"
-        /></span>
+        /><span class="shortName">{{ !useAtIcon ? '@' : '' }}<span
+            class="userName"
+            v-html="userName"
+          /></span>
         <span
           v-if="isYou"
           class="you"
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index bd3ee84e..5fec2d89 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -147,6 +147,11 @@
             {{ $t('settings.greentext') }}
           </BooleanSetting>
         </li>
+        <li>
+          <BooleanSetting path="useAtIcon">
+            {{ $t('settings.use_at_icon') }}
+          </BooleanSetting>
+        </li>
       </ul>
     </div>
 
diff --git a/src/modules/config.js b/src/modules/config.js
index c79302b5..43c8b92f 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -70,6 +70,7 @@ export const defaultState = {
   useOneClickNsfw: false,
   useContainFit: false,
   greentext: undefined, // instance default
+  useAtIcon: undefined, // instance default
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 60038f08..aaaf7acf 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -20,6 +20,7 @@ const defaultState = {
   background: '/static/aurora_borealis.jpg',
   collapseMessageWithSubject: false,
   greentext: false,
+  useAtIcon: false,
   hideFilteredStatuses: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: false,
-- 
cgit v1.2.3-70-g09d2


From 1d4b1b296e8ee37f119f419df49791d99fef4774 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 10 Jan 2022 01:16:33 -0500
Subject: Add pref for whether to display full user names and tooltips

---
 src/components/mention_link/mention_link.js        | 20 ++++++++++++++++++++
 src/components/mention_link/mention_link.vue       | 16 +++++++++++-----
 src/components/settings_modal/tabs/general_tab.js  |  5 +++++
 src/components/settings_modal/tabs/general_tab.vue | 19 +++++++++++++++++++
 src/modules/config.js                              |  5 ++++-
 src/modules/instance.js                            |  5 +++++
 6 files changed, 64 insertions(+), 6 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
index 637641af..f71c7716 100644
--- a/src/components/mention_link/mention_link.js
+++ b/src/components/mention_link/mention_link.js
@@ -50,6 +50,10 @@ const MentionLink = {
     userName () {
       return this.user && this.userNameFullUi.split('@')[0]
     },
+    serverName () {
+      // XXX assumed that domain does not contain @
+      return this.user && (this.userNameFullUi.split('@')[1] || this.$store.getters.instanceDomain)
+    },
     userNameFull () {
       return this.user && this.user.screen_name
     },
@@ -88,6 +92,22 @@ const MentionLink = {
     useAtIcon () {
       return this.mergedConfig.useAtIcon
     },
+    isRemote () {
+      return this.userName !== this.userNameFull
+    },
+    shouldShowFullUserName () {
+      const conf = this.mergedConfig.mentionLinkDisplay
+      if (conf === 'short') {
+        return false
+      } else if (conf === 'full') {
+        return true
+      } else { // full_for_remote
+        return this.isRemote
+      }
+    },
+    shouldShowTooltip () {
+      return this.mergedConfig.mentionLinkShowTooltip && this.mergedConfig.mentionLinkDisplay === 'short' && this.isRemote
+    },
     ...mapGetters(['mergedConfig']),
     ...mapState({
       currentUser: state => state.users.currentUser
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
index f633bf50..fe16cbf5 100644
--- a/src/components/mention_link/mention_link.vue
+++ b/src/components/mention_link/mention_link.vue
@@ -28,10 +28,16 @@
           size="sm"
           icon="at"
           class="at"
-        /><span class="shortName">{{ !useAtIcon ? '@' : '' }}<span
-            class="userName"
-            v-html="userName"
-          /></span>
+        /><span
+          class="shortName"
+        >{{ !useAtIcon ? '@' : '' }}<span
+          class="userName"
+          v-html="userName"
+        /><span
+          v-if="shouldShowFullUserName"
+          class="serverName"
+          v-html="'@' + serverName"
+        /></span>
         <span
           v-if="isYou"
           class="you"
@@ -39,7 +45,7 @@
         <!-- eslint-enable vue/no-v-html -->
       </a>
       <span
-        v-if="userName !== userNameFull"
+        v-if="shouldShowTooltip"
         class="full popover-default"
         :class="[highlightType]"
       >
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index eeda61bf..952c328d 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -20,6 +20,11 @@ const GeneralTab = {
         value: mode,
         label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
       })),
+      mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.mention_link_display_${mode}`)
+      })),
       loopSilentAvailable:
       // Firefox
       Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 5fec2d89..8fce1eee 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -152,6 +152,25 @@
             {{ $t('settings.use_at_icon') }}
           </BooleanSetting>
         </li>
+        <li>
+          <ChoiceSetting
+            id="mentionLinkDisplay"
+            path="mentionLinkDisplay"
+            :options="mentionLinkDisplayOptions"
+          >
+            {{ $t('settings.mention_link_display') }}
+          </ChoiceSetting>
+        </li>
+        <ul
+          v-if="mentionLinkDisplay === 'short'"
+          class="setting-list suboptions"
+        >
+          <li>
+            <BooleanSetting path="mentionLinkShowTooltip">
+              {{ $t('settings.mention_link_show_tooltip') }}
+            </BooleanSetting>
+          </li>
+        </ul>
       </ul>
     </div>
 
diff --git a/src/modules/config.js b/src/modules/config.js
index 43c8b92f..e1a49a7d 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -11,7 +11,8 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0]
  */
 export const multiChoiceProperties = [
   'postContentType',
-  'subjectLineBehavior'
+  'subjectLineBehavior',
+  'mentionLinkDisplay' // short | full_for_remote | full
 ]
 
 export const defaultState = {
@@ -71,6 +72,8 @@ export const defaultState = {
   useContainFit: false,
   greentext: undefined, // instance default
   useAtIcon: undefined, // instance default
+  mentionLinkDisplay: undefined, // instance default
+  mentionLinkShowTooltip: undefined, // instance default
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
diff --git a/src/modules/instance.js b/src/modules/instance.js
index aaaf7acf..200a7a6f 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -21,6 +21,8 @@ const defaultState = {
   collapseMessageWithSubject: false,
   greentext: false,
   useAtIcon: false,
+  mentionLinkDisplay: 'short',
+  mentionLinkShowTooltip: true,
   hideFilteredStatuses: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: false,
@@ -101,6 +103,9 @@ const instance = {
       return instanceDefaultProperties
         .map(key => [key, state[key]])
         .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
+    },
+    instanceDomain (state) {
+      return new URL(state.server).hostname
     }
   },
   actions: {
-- 
cgit v1.2.3-70-g09d2


From 9fde13c9685e934c8f610c96d481457b3d37c389 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 10 Jan 2022 02:05:41 -0500
Subject: Add option to display user avatar in mention link

---
 src/components/mention_link/mention_link.js        | 7 +++++++
 src/components/mention_link/mention_link.scss      | 9 +++++++++
 src/components/mention_link/mention_link.vue       | 5 +++++
 src/components/settings_modal/tabs/general_tab.vue | 5 +++++
 src/modules/config.js                              | 1 +
 src/modules/instance.js                            | 1 +
 6 files changed, 28 insertions(+)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
index f71c7716..426dbe97 100644
--- a/src/components/mention_link/mention_link.js
+++ b/src/components/mention_link/mention_link.js
@@ -1,6 +1,7 @@
 import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
 import { mapGetters, mapState } from 'vuex'
 import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
+import UserAvatar from '../user_avatar/user_avatar.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
   faAt
@@ -12,6 +13,9 @@ library.add(
 
 const MentionLink = {
   name: 'MentionLink',
+  components: {
+    UserAvatar
+  },
   props: {
     url: {
       required: true,
@@ -108,6 +112,9 @@ const MentionLink = {
     shouldShowTooltip () {
       return this.mergedConfig.mentionLinkShowTooltip && this.mergedConfig.mentionLinkDisplay === 'short' && this.isRemote
     },
+    shouldShowAvatar () {
+      return this.mergedConfig.mentionLinkShowAvatar
+    },
     ...mapGetters(['mergedConfig']),
     ...mapState({
       currentUser: state => state.users.currentUser
diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss
index 5f467582..be00093a 100644
--- a/src/components/mention_link/mention_link.scss
+++ b/src/components/mention_link/mention_link.scss
@@ -1,3 +1,5 @@
+@import '../../_variables.scss';
+
 .MentionLink {
   position: relative;
   white-space: normal;
@@ -10,6 +12,13 @@
     border-radius: 2px;
   }
 
+  .mention-avatar {
+    border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
+    width: 1.5em;
+    height: 1.5em;
+    vertical-align: middle;
+  }
+
   .full {
     position: absolute;
     display: inline-block;
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
index c9baee10..8e78243a 100644
--- a/src/components/mention_link/mention_link.vue
+++ b/src/components/mention_link/mention_link.vue
@@ -23,6 +23,11 @@
         :href="url"
         @click.prevent="onClick"
       >
+        <UserAvatar
+          v-if="shouldShowAvatar"
+          class="mention-avatar"
+          :user="user"
+        />
         <!-- eslint-disable vue/no-v-html -->
         <span
           class="shortName"
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 8fce1eee..b4495941 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -171,6 +171,11 @@
             </BooleanSetting>
           </li>
         </ul>
+        <li>
+          <BooleanSetting path="mentionLinkShowAvatar">
+            {{ $t('settings.mention_link_show_avatar') }}
+          </BooleanSetting>
+        </li>
       </ul>
     </div>
 
diff --git a/src/modules/config.js b/src/modules/config.js
index e1a49a7d..cfb753ce 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -74,6 +74,7 @@ export const defaultState = {
   useAtIcon: undefined, // instance default
   mentionLinkDisplay: undefined, // instance default
   mentionLinkShowTooltip: undefined, // instance default
+  mentionLinkShowAvatar: undefined, // instance default
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 200a7a6f..6c8823c3 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -23,6 +23,7 @@ const defaultState = {
   useAtIcon: false,
   mentionLinkDisplay: 'short',
   mentionLinkShowTooltip: true,
+  mentionLinkShowAvatar: true,
   hideFilteredStatuses: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: false,
-- 
cgit v1.2.3-70-g09d2


From 0c60b31eee3e31810ebd41bc186a9825a39cd821 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 10 Jan 2022 02:31:26 -0500
Subject: Add option to fade domains in mention link

---
 src/components/mention_link/mention_link.js        |  3 +++
 src/components/mention_link/mention_link.scss      |  8 ++++++++
 src/components/mention_link/mention_link.vue       | 15 +++++++++++++--
 src/components/settings_modal/tabs/general_tab.vue |  5 +++++
 src/modules/config.js                              |  1 +
 src/modules/instance.js                            |  1 +
 6 files changed, 31 insertions(+), 2 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
index 426dbe97..5209907d 100644
--- a/src/components/mention_link/mention_link.js
+++ b/src/components/mention_link/mention_link.js
@@ -115,6 +115,9 @@ const MentionLink = {
     shouldShowAvatar () {
       return this.mergedConfig.mentionLinkShowAvatar
     },
+    shouldFadeDomain () {
+      return this.mergedConfig.mentionLinkFadeDomain
+    },
     ...mapGetters(['mergedConfig']),
     ...mapState({
       currentUser: state => state.users.currentUser
diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss
index 9a285ad8..03306dcc 100644
--- a/src/components/mention_link/mention_link.scss
+++ b/src/components/mention_link/mention_link.scss
@@ -100,4 +100,12 @@
     opacity: 1;
     pointer-events: initial;
   }
+
+  .serverName.-faded {
+    color: var(--faintLink, $fallback--link);
+  }
+
+  .full .-faded {
+    color: var(--faint, $fallback--faint);
+  }
 }
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
index c0d71322..ac20eb5a 100644
--- a/src/components/mention_link/mention_link.vue
+++ b/src/components/mention_link/mention_link.vue
@@ -41,6 +41,7 @@
         /><span
           v-if="shouldShowFullUserName"
           class="serverName"
+          :class="{ '-faded': shouldFadeDomain }"
           v-html="'@' + serverName"
         /></span>
         <span
@@ -56,8 +57,18 @@
       >
         <span
           class="userNameFull"
-          v-text="'@' + userNameFull"
-        />
+        >
+          <!-- eslint-disable vue/no-v-html -->
+          @<span
+            class="userName"
+            v-html="userName"
+          /><span
+            class="serverName"
+            :class="{ '-faded': shouldFadeDomain }"
+            v-html="'@' + serverName"
+          />
+          <!-- eslint-enable vue/no-v-html -->
+        </span>
       </span>
     </span>
   </span>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index b4495941..50599eae 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -176,6 +176,11 @@
             {{ $t('settings.mention_link_show_avatar') }}
           </BooleanSetting>
         </li>
+        <li>
+          <BooleanSetting path="mentionLinkFadeDomain">
+            {{ $t('settings.mention_link_fade_domain') }}
+          </BooleanSetting>
+        </li>
       </ul>
     </div>
 
diff --git a/src/modules/config.js b/src/modules/config.js
index cfb753ce..9f2d4ef3 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -75,6 +75,7 @@ export const defaultState = {
   mentionLinkDisplay: undefined, // instance default
   mentionLinkShowTooltip: undefined, // instance default
   mentionLinkShowAvatar: undefined, // instance default
+  mentionLinkFadeDomain: undefined, // instance default
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 6c8823c3..2859b00b 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -24,6 +24,7 @@ const defaultState = {
   mentionLinkDisplay: 'short',
   mentionLinkShowTooltip: true,
   mentionLinkShowAvatar: true,
+  mentionLinkFadeDomain: true,
   hideFilteredStatuses: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: false,
-- 
cgit v1.2.3-70-g09d2


From c8983d56063bcb6970c7ba459288049d5d4d2d81 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 10 Jan 2022 02:53:27 -0500
Subject: Make mention link prefs ui more intuitive

---
 src/components/settings_modal/tabs/general_tab.vue | 35 +++++++++++-----------
 1 file changed, 18 insertions(+), 17 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 50599eae..44b1ac92 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -147,11 +147,6 @@
             {{ $t('settings.greentext') }}
           </BooleanSetting>
         </li>
-        <li>
-          <BooleanSetting path="useAtIcon">
-            {{ $t('settings.use_at_icon') }}
-          </BooleanSetting>
-        </li>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
@@ -162,25 +157,31 @@
           </ChoiceSetting>
         </li>
         <ul
-          v-if="mentionLinkDisplay === 'short'"
           class="setting-list suboptions"
         >
-          <li>
+          <li
+            v-if="mentionLinkDisplay === 'short'"
+          >
             <BooleanSetting path="mentionLinkShowTooltip">
               {{ $t('settings.mention_link_show_tooltip') }}
             </BooleanSetting>
           </li>
+          <li>
+            <BooleanSetting path="useAtIcon">
+              {{ $t('settings.use_at_icon') }}
+            </BooleanSetting>
+          </li>
+          <li>
+            <BooleanSetting path="mentionLinkShowAvatar">
+              {{ $t('settings.mention_link_show_avatar') }}
+            </BooleanSetting>
+          </li>
+          <li>
+            <BooleanSetting path="mentionLinkFadeDomain">
+              {{ $t('settings.mention_link_fade_domain') }}
+            </BooleanSetting>
+          </li>
         </ul>
-        <li>
-          <BooleanSetting path="mentionLinkShowAvatar">
-            {{ $t('settings.mention_link_show_avatar') }}
-          </BooleanSetting>
-        </li>
-        <li>
-          <BooleanSetting path="mentionLinkFadeDomain">
-            {{ $t('settings.mention_link_fade_domain') }}
-          </BooleanSetting>
-        </li>
       </ul>
     </div>
 
-- 
cgit v1.2.3-70-g09d2


From bfb3a4364be3f71f7c46056aeb8972f04a072703 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 3 Feb 2022 22:34:57 +0200
Subject: options to disable (You)s and highlighting of yourself

---
 src/components/mention_link/mention_link.js        |  8 +++++++-
 src/components/mention_link/mention_link.vue       |  4 ++--
 src/components/settings_modal/tabs/general_tab.vue | 10 ++++++++++
 src/i18n/en.json                                   |  2 ++
 src/modules/config.js                              |  2 ++
 src/modules/instance.js                            |  2 ++
 6 files changed, 25 insertions(+), 3 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
index 5209907d..55eea195 100644
--- a/src/components/mention_link/mention_link.js
+++ b/src/components/mention_link/mention_link.js
@@ -87,7 +87,7 @@ const MentionLink = {
     classnames () {
       return [
         {
-          '-you': this.isYou,
+          '-you': this.isYou && this.shouldBoldenYou,
           '-highlighted': this.highlight
         },
         this.highlightType
@@ -115,6 +115,12 @@ const MentionLink = {
     shouldShowAvatar () {
       return this.mergedConfig.mentionLinkShowAvatar
     },
+    shouldShowYous () {
+      return this.mergedConfig.mentionLinkShowYous
+    },
+    shouldBoldenYou () {
+      return this.mergedConfig.mentionLinkBoldenYou
+    },
     shouldFadeDomain () {
       return this.mergedConfig.mentionLinkFadeDomain
     },
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
index ac20eb5a..d8ab79fb 100644
--- a/src/components/mention_link/mention_link.vue
+++ b/src/components/mention_link/mention_link.vue
@@ -45,8 +45,8 @@
           v-html="'@' + serverName"
         /></span>
         <span
-          v-if="isYou"
-          class="you"
+          v-if="isYou && shouldShowYous"
+          :class="{ '-you': shouldBoldenYou }"
         >{{ $t('status.you') }}</span>
         <!-- eslint-enable vue/no-v-html -->
       </a>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 44b1ac92..eba3b268 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -147,6 +147,11 @@
             {{ $t('settings.greentext') }}
           </BooleanSetting>
         </li>
+        <li>
+          <BooleanSetting path="mentionLinkShowYous">
+            {{ $t('settings.show_yous') }}
+          </BooleanSetting>
+        </li>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
@@ -181,6 +186,11 @@
               {{ $t('settings.mention_link_fade_domain') }}
             </BooleanSetting>
           </li>
+          <li>
+            <BooleanSetting path="mentionLinkBoldenYou">
+              {{ $t('settings.mention_link_bolden_you') }}
+            </BooleanSetting>
+          </li>
         </ul>
       </ul>
     </div>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 209fd184..8fd189c6 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -493,8 +493,10 @@
     "mention_link_show_tooltip": "Show full user names as tooltip for remote users",
     "mention_link_show_avatar": "Show user avatar beside the link",
     "mention_link_fade_domain": "Fade domains (e.g. @example.org in @foo@example.org)",
+    "mention_link_bolden_you": "Bolden text of your mention",
     "fun": "Fun",
     "greentext": "Meme arrows",
+    "show_yous": "Show (You)s",
     "notifications": "Notifications",
     "notification_setting_filters": "Filters",
     "notification_setting_block_from_strangers": "Block notifications from users who you do not follow",
diff --git a/src/modules/config.js b/src/modules/config.js
index 9f2d4ef3..20979174 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -76,6 +76,8 @@ export const defaultState = {
   mentionLinkShowTooltip: undefined, // instance default
   mentionLinkShowAvatar: undefined, // instance default
   mentionLinkFadeDomain: undefined, // instance default
+  mentionLinkShowYous: undefined, // instance default
+  mentionLinkBoldenYou: undefined, // instance default
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
diff --git a/src/modules/instance.js b/src/modules/instance.js
index d686f258..1abd784f 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -25,6 +25,8 @@ const defaultState = {
   mentionLinkShowTooltip: true,
   mentionLinkShowAvatar: false,
   mentionLinkFadeDomain: true,
+  mentionLinkShowYous: false,
+  mentionLinkBoldenYou: true,
   hideFilteredStatuses: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: false,
-- 
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/tabs/general_tab.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 f626da838a7abeb3a2d3cd5f71ee4eb2ca272361 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 24 Feb 2022 15:00:08 +0200
Subject: revert to using local setting for default nsfw since backend is
 broken

---
 src/components/post_status_form/post_status_form.js | 1 +
 src/components/settings_modal/tabs/general_tab.vue  | 3 ++-
 src/modules/serverSideConfig.js                     | 2 +-
 3 files changed, 4 insertions(+), 2 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index fe07309f..9d7bbd75 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -117,6 +117,7 @@ const PostStatusForm = {
       ? this.copyMessageScope
       : this.$store.state.users.currentUser.default_scope
 
+    // const { defaultNSFW: sensitiveByDefault } = this.$store.state.serverSideConfig
     const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
 
     return {
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 4accf0c1..5db70d77 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -221,7 +221,8 @@
           </label>
         </li>
         <li>
-          <BooleanSetting path="serverSide_defaultNSFW">
+          <!-- <BooleanSetting path="serverSide_defaultNSFW"> -->
+          <BooleanSetting path="sensitiveByDefault">
             {{ $t('settings.sensitive_by_default') }}
           </BooleanSetting>
         </li>
diff --git a/src/modules/serverSideConfig.js b/src/modules/serverSideConfig.js
index 75ea91be..ea2dc5e3 100644
--- a/src/modules/serverSideConfig.js
+++ b/src/modules/serverSideConfig.js
@@ -2,7 +2,7 @@ import { get, set } from 'lodash'
 
 export const settingsMapGet = {
   'defaultScope': 'source.privacy',
-  'defaultNSFW': 'source.sensitive',
+  'defaultNSFW': 'source.sensitive', // BROKEN: pleroma/pleroma#2837
   'stripRichContent': 'source.pleroma.no_rich_content',
   // Privacy
   'locked': 'locked',
-- 
cgit v1.2.3-70-g09d2


From e3d602fdccb0dfe211fd3950210cdf8fda43bf95 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 28 Feb 2022 17:45:07 +0200
Subject: revert changes related to streaming/firehose setting, reword it so
 it's not confused with websocket streaming

---
 src/components/settings_modal/tabs/general_tab.vue | 3 +--
 src/i18n/en.json                                   | 4 ++--
 src/modules/config.js                              | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 5db70d77..7944b03c 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" expert="1">
+          <BooleanSetting path="streaming" >
             {{ $t('settings.streaming') }}
           </BooleanSetting>
           <ul
@@ -38,7 +38,6 @@
               <BooleanSetting
                 path="pauseOnUnfocused"
                 :disabled="!streaming"
-                expert="1"
               >
                 {{ $t('settings.pause_on_unfocused') }}
               </BooleanSetting>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index a66aaa0b..da5e662d 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -435,7 +435,7 @@
     "valid_until": "Valid until",
     "revoke_token": "Revoke",
     "panelRadius": "Panels",
-    "pause_on_unfocused": "Pause streaming when tab is not focused",
+    "pause_on_unfocused": "Pause when tab is not focused",
     "presets": "Presets",
     "profile_background": "Profile background",
     "profile_banner": "Profile banner",
@@ -473,7 +473,7 @@
     "post_status_content_type": "Post status content type",
     "sensitive_by_default": "Mark posts as sensitive by default",
     "stop_gifs": "Pause animated images until you hover on them",
-    "streaming": "Enable automatic streaming of new posts when scrolled to the top",
+    "streaming": "Automatically show new posts when scrolled to the top",
     "user_mutes": "Users",
     "useStreamingApi": "Receive posts and notifications real-time",
     "useStreamingApiWarning": "(Not recommended, experimental, known to skip posts)",
diff --git a/src/modules/config.js b/src/modules/config.js
index e5321507..86601564 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -37,7 +37,7 @@ export const defaultState = {
   preloadImage: true,
   loopVideo: true,
   loopVideoSilentOnly: true,
-  streaming: true,
+  streaming: false,
   emojiReactionsOnTimeline: true,
   alwaysShowNewPostButton: false,
   autohideFloatingPostButton: false,
-- 
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/tabs/general_tab.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


From 0582f19e7c2c6f916b427d5ecfbbb571178ce841 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 6 Aug 2021 20:18:27 -0400
Subject: Add tree-style thread display

---
 src/components/conversation/conversation.js        | 60 +++++++++++++++++++++-
 src/components/conversation/conversation.vue       | 59 ++++++++++++++-------
 src/components/settings_modal/tabs/general_tab.js  |  5 ++
 src/components/settings_modal/tabs/general_tab.vue |  9 ++++
 src/components/thread_tree/thread_tree.js          | 52 +++++++++++++++++++
 src/components/thread_tree/thread_tree.vue         | 55 ++++++++++++++++++++
 src/modules/config.js                              |  4 +-
 src/modules/instance.js                            |  1 +
 8 files changed, 224 insertions(+), 21 deletions(-)
 create mode 100644 src/components/thread_tree/thread_tree.js
 create mode 100644 src/components/thread_tree/thread_tree.vue

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 069c0b40..e663ba15 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -1,5 +1,8 @@
 import { reduce, filter, findIndex, clone, get } from 'lodash'
 import Status from '../status/status.vue'
+import ThreadTree from '../thread_tree/thread_tree.vue'
+
+const debug = console.log
 
 const sortById = (a, b) => {
   const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id
@@ -53,6 +56,15 @@ const conversation = {
     }
   },
   computed: {
+    displayStyle () {
+      return this.$store.state.config.conversationDisplay
+    },
+    isTreeView () {
+      return this.displayStyle === 'tree'
+    },
+    isLinearView () {
+      return this.displayStyle === 'linear'
+    },
     hideStatus () {
       if (this.$refs.statusComponent && this.$refs.statusComponent[0]) {
         return this.virtualHidden && this.$refs.statusComponent[0].suspendable
@@ -90,6 +102,49 @@ const conversation = {
 
       return sortAndFilterConversation(conversation, this.status)
     },
+    threadTree () {
+      const reverseLookupTable = this.conversation.reduce((table, status, index) => {
+        table[status.id] = index
+        return table
+      }, {})
+
+      const threads = this.conversation.reduce((a, cur) => {
+        const id = cur.id
+        a.forest[id] = this.getReplies(id)
+          .map(s => s.id)
+          .sort((a, b) => reverseLookupTable[a] - reverseLookupTable[b])
+
+        a.topLevel = a.topLevel.filter(k => a.forest[id].contains(k))
+        return a
+      }, {
+        forest: {},
+        topLevel: this.conversation.map(s => s.id)
+      })
+
+      const walk = (forest, topLevel, depth = 0, processed = {}) => topLevel.map(id => {
+        if (processed[id]) {
+          return []
+        }
+
+        processed[id] = true
+        return [{
+          status: this.conversation[reverseLookupTable[id]],
+          id,
+          depth
+        }, walk(forest, forest[child], depth + 1, processed)].reduce((a, b) => a.concat(b), [])
+      }).reduce((a, b) => a.concat(b), [])
+
+      const linearized = walk(threads.forest, threads.topLevel)
+
+      return linearized
+    },
+    topLevel () {
+      const topLevel = this.conversation.reduce((tl, cur) =>
+        tl.filter(k => this.getReplies(cur.id).map(v => v.id).indexOf(k.id) === -1), this.conversation)
+      debug("toplevel =", topLevel)
+      debug("toplevel =", topLevel)
+      return topLevel
+    },
     replies () {
       let i = 1
       // eslint-disable-next-line camelcase
@@ -109,7 +164,7 @@ const conversation = {
       }, {})
     },
     isExpanded () {
-      return this.expanded || this.isPage
+      return !!(this.expanded || this.isPage)
     },
     hiddenStyle () {
       const height = (this.status && this.status.virtualHeight) || '120px'
@@ -117,7 +172,8 @@ const conversation = {
     }
   },
   components: {
-    Status
+    Status,
+    ThreadTree
   },
   watch: {
     statusId (newVal, oldVal) {
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index 3fb26d92..cea5f88f 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -18,24 +18,47 @@
         {{ $t('timeline.collapse') }}
       </button>
     </div>
-    <status
-      v-for="status in conversation"
-      :key="status.id"
-      ref="statusComponent"
-      :inline-expanded="collapsable && isExpanded"
-      :statusoid="status"
-      :expandable="!isExpanded"
-      :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
-      :focused="focused(status.id)"
-      :in-conversation="isExpanded"
-      :highlight="getHighlight()"
-      :replies="getReplies(status.id)"
-      :in-profile="inProfile"
-      :profile-user-id="profileUserId"
-      class="conversation-status status-fadein panel-body"
-      @goto="setHighlight"
-      @toggleExpanded="toggleExpanded"
-    />
+    <div v-if="isTreeView">
+      <thread-tree
+        v-for="status in topLevel"
+        :key="status.id"
+        ref="statusComponent"
+
+        :status="status"
+        :in-profile="inProfile"
+        :conversation="conversation"
+        :collapsable="collapsable"
+        :is-expanded="isExpanded"
+        :pinned-status-ids-object="pinnedStatusIdsObject"
+        :profile-user-id="profileUserId"
+
+        :focused="focused"
+        :get-replies="getReplies"
+        :get-highlight="getHighlight"
+        :set-highlight="setHighlight"
+        :toggle-expanded="toggleExpanded"
+      />
+    </div>
+    <div v-if="isLinearView">
+      <status
+        v-for="status in conversation"
+        :key="status.id"
+        ref="statusComponent"
+        :inline-expanded="collapsable && isExpanded"
+        :statusoid="status"
+        :expandable="!isExpanded"
+        :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
+        :focused="focused(status.id)"
+        :in-conversation="isExpanded"
+        :highlight="getHighlight()"
+        :replies="getReplies(status.id)"
+        :in-profile="inProfile"
+        :profile-user-id="profileUserId"
+        class="conversation-status status-fadein panel-body"
+        @goto="setHighlight"
+        @toggleExpanded="toggleExpanded"
+      />
+    </div>
   </div>
   <div
     v-else
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 952c328d..abe8f6f7 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -20,6 +20,11 @@ const GeneralTab = {
         value: mode,
         label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
       })),
+      conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.conversation_display_${mode}`)
+      })),
       mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
         key: mode,
         value: mode,
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index eba3b268..8951c021 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -152,6 +152,15 @@
             {{ $t('settings.show_yous') }}
           </BooleanSetting>
         </li>
+        <li>
+          <ChoiceSetting
+            id="conversationDisplay"
+            path="conversationDisplay"
+            :options="conversationDisplayOptions"
+          >
+            {{ $t('settings.conversation_display') }}
+          </ChoiceSetting>
+        </li>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js
new file mode 100644
index 00000000..4f95122a
--- /dev/null
+++ b/src/components/thread_tree/thread_tree.js
@@ -0,0 +1,52 @@
+import Status from '../status/status.vue'
+
+const debug = console.log
+
+const ThreadTree = {
+  components: {
+    Status
+  },
+  name: 'ThreadTree',
+  props: {
+    depth: Number,
+    status: Object,
+    inProfile: Boolean,
+    conversation: Array,
+    collapsable: Boolean,
+    isExpanded: Boolean,
+    pinnedStatusIdsObject: Object,
+    profileUserId: String,
+
+    focused: Function,
+    getHighlight: Function,
+    getReplies: Function,
+    setHighlight: Function,
+    toggleExpanded: Function
+  },
+  computed: {
+    reverseLookupTable () {
+      return this.conversation.reduce((table, status, index) => {
+        table[status.id] = index
+        return table
+      }, {})
+    },
+    currentReplies () {
+      debug('status:', this.status)
+      debug('getReplies:', this.getReplies(this.status.id))
+      return this.getReplies(this.status.id).map(({ id }) => this.statusById(id))
+    },
+  },
+  methods: {
+    statusById (id) {
+      return this.conversation[this.reverseLookupTable[id]]
+    },
+    collapseThread () {
+    },
+    showThread () {
+    },
+    showAllSubthreads () {
+    }
+  }
+}
+
+export default ThreadTree
diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue
new file mode 100644
index 00000000..8256eee6
--- /dev/null
+++ b/src/components/thread_tree/thread_tree.vue
@@ -0,0 +1,55 @@
+<template>
+  <div class="thread-tree panel-body">
+    <status
+      :key="status.id"
+      ref="statusComponent"
+      :inline-expanded="collapsable && isExpanded"
+      :statusoid="status"
+      :expandable="!isExpanded"
+      :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
+      :focused="focused(status.id)"
+      :in-conversation="isExpanded"
+      :highlight="getHighlight()"
+      :replies="getReplies(status.id)"
+      :in-profile="inProfile"
+      :profile-user-id="profileUserId"
+      class="conversation-status status-fadein panel-body"
+      @goto="setHighlight"
+      @toggleExpanded="toggleExpanded"
+    />
+    <div
+      v-if="currentReplies.length"
+      class="thread-tree-replies"
+    >
+      <thread-tree
+        v-for="replyStatus in currentReplies"
+        :key="replyStatus.id"
+        ref="childComponent"
+        :status="replyStatus"
+
+        :in-profile="inProfile"
+        :conversation="conversation"
+        :collapsable="collapsable"
+        :is-expanded="isExpanded"
+        :pinned-status-ids-object="pinnedStatusIdsObject"
+        :profile-user-id="profileUserId"
+
+        :focused="focused"
+        :get-replies="getReplies"
+        :get-highlight="getHighlight"
+        :set-highlight="setHighlight"
+        :toggle-expanded="toggleExpanded"
+
+        class="conversation-status status-fadein panel-body"
+      />
+    </div>
+  </div>
+</template>
+
+<script src="./thread_tree.js"></script>
+
+<style lang="scss">
+.thread-tree-replies {
+  margin-left: 1em;
+}
+</style>
diff --git a/src/modules/config.js b/src/modules/config.js
index 20979174..ec75dbfb 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -12,6 +12,7 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0]
 export const multiChoiceProperties = [
   'postContentType',
   'subjectLineBehavior',
+  'conversationDisplay', // tree | linear
   'mentionLinkDisplay' // short | full_for_remote | full
 ]
 
@@ -81,7 +82,8 @@ export const defaultState = {
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
-  sensitiveByDefault: undefined // instance default
+  sensitiveByDefault: undefined, // instance default
+  conversationDisplay: undefined // instance default
 }
 
 // caching the instance default properties
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 1abd784f..a160a557 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -53,6 +53,7 @@ const defaultState = {
   theme: 'pleroma-dark',
   virtualScrolling: true,
   sensitiveByDefault: false,
+  conversationDisplay: 'tree',
 
   // Nasty stuff
   customEmoji: [],
-- 
cgit v1.2.3-70-g09d2


From 2e54cf12c76e94f980fbe49f58c080a4a3d17ca4 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sat, 7 Aug 2021 20:14:14 -0400
Subject: Add settings for max depth in thread

---
 src/components/conversation/conversation.js        | 15 ++++++++++-----
 src/components/settings_modal/tabs/general_tab.vue | 13 +++++++++++++
 src/modules/config.js                              |  3 ++-
 src/modules/instance.js                            |  1 +
 4 files changed, 26 insertions(+), 6 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 8e4eda51..853161df 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -74,7 +74,11 @@ const conversation = {
   },
   computed: {
     maxDepthToShowByDefault () {
-      return 4
+      // maxDepthInThread = max number of depths that is *visible*
+      // since our depth starts with 0 and "showing" means "showing children"
+      // there is a -2 here
+      const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2
+      return maxDepth >= 1 ? maxDepth : 1
     },
     displayStyle () {
       return this.$store.getters.mergedConfig.conversationDisplay
@@ -351,7 +355,8 @@ const conversation = {
         }
         this._diven = true
         const parentOrSelf = this.parentOrSelf(this.originalStatusId)
-        if (this.threadDisplayStatus[this.statusId] === 'hidden') {
+        // If current status is not visible
+        if (this.threadDisplayStatus[parentOrSelf] === 'hidden') {
           this.diveIntoStatus(parentOrSelf, /* preventScroll */ true)
           this.tryScrollTo(this.statusId)
         }
@@ -429,7 +434,7 @@ const conversation = {
     toggleStatusContentProperty (id, name) {
       this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name])
     },
-    leastShowingAncestor (id) {
+    leastVisibleAncestor (id) {
       let cur = id
       let parent = this.parentOf(cur)
       while (cur) {
@@ -453,14 +458,14 @@ const conversation = {
       const oldHighlight = this.highlight
       this.diveHistory = [...this.diveHistory.slice(0, this.diveHistory.length - 1)]
       if (oldHighlight) {
-        this.tryScrollTo(this.leastShowingAncestor(oldHighlight))
+        this.tryScrollTo(this.leastVisibleAncestor(oldHighlight))
       }
     },
     undive () {
       const oldHighlight = this.highlight
       this.diveHistory = []
       if (oldHighlight) {
-        this.tryScrollTo(this.leastShowingAncestor(oldHighlight))
+        this.tryScrollTo(this.leastVisibleAncestor(oldHighlight))
       } else {
         this.goToCurrent()
       }
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 8951c021..f97d92c3 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -161,6 +161,19 @@
             {{ $t('settings.conversation_display') }}
           </ChoiceSetting>
         </li>
+        <li v-if="conversationDisplay !== 'linear'">
+          <label for="maxDepthInThread">
+            {{ $t('settings.max_depth_in_thread') }}
+          </label>
+          <input
+            id="maxDepthInThread"
+            path.number="maxDepthInThread"
+            class="number-input"
+            type="number"
+            min="3"
+            step="1"
+          >
+        </li>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
diff --git a/src/modules/config.js b/src/modules/config.js
index ec75dbfb..b3c7e5ab 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -83,7 +83,8 @@ export const defaultState = {
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
   sensitiveByDefault: undefined, // instance default
-  conversationDisplay: undefined // instance default
+  conversationDisplay: undefined, // instance default
+  maxDepthInThread: 6
 }
 
 // caching the instance default properties
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 8a5bbbd1..853be737 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -54,6 +54,7 @@ const defaultState = {
   virtualScrolling: true,
   sensitiveByDefault: false,
   conversationDisplay: 'simple_tree',
+  maxDepthInThread: 6,
 
   // Nasty stuff
   customEmoji: [],
-- 
cgit v1.2.3-70-g09d2


From 863255d52fdcbbabe45c86c7e36ebafc0f7e1c53 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sun, 5 Sep 2021 16:35:47 -0400
Subject: Make position of other replies button a pref

---
 src/components/conversation/conversation.js        |  9 +++++
 src/components/conversation/conversation.vue       |  7 ++--
 src/components/settings_modal/tabs/general_tab.js  |  5 +++
 src/components/settings_modal/tabs/general_tab.vue | 40 +++++++++++++++-------
 src/components/status/status.js                    |  1 +
 src/components/status/status.vue                   | 15 +++++++-
 src/modules/config.js                              |  2 ++
 src/modules/instance.js                            |  1 +
 8 files changed, 63 insertions(+), 17 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 423930af..d4972fbc 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -92,6 +92,15 @@ const conversation = {
     isLinearView () {
       return this.displayStyle === 'linear'
     },
+    otherRepliesButtonPosition () {
+      return this.$store.getters.mergedConfig.conversationOtherRepliesButton
+    },
+    showOtherRepliesButtonBelowStatus () {
+      return this.otherRepliesButtonPosition === 'below'
+    },
+    showOtherRepliesButtonInsideStatus () {
+      return this.otherRepliesButtonPosition === 'inside'
+    },
     hideStatus () {
       if (this.$refs.statusComponent && this.$refs.statusComponent[0]) {
         return this.virtualHidden && this.$refs.statusComponent[0].suspendable
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index f0eb88c1..b3d97075 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -66,13 +66,14 @@
               :profile-user-id="profileUserId"
               class="conversation-status status-fadein panel-body"
 
-              :simple="treeViewIsSimple"
+              :simple-tree="treeViewIsSimple"
               :toggle-thread-display="toggleThreadDisplay"
               :thread-display-status="threadDisplayStatus"
               :show-thread-recursively="showThreadRecursively"
               :total-reply-count="totalReplyCount"
               :total-reply-depth="totalReplyDepth"
-              :dive="(!treeViewIsSimple) ? () => diveIntoStatus(status.id) : null"
+              :show-other-replies-as-button="showOtherRepliesButtonInsideStatus"
+              :dive="() => diveIntoStatus(status.id)"
 
               :controlled-showing-tall="statusContentProperties[status.id].showingTall"
               :controlled-expanding-subject="statusContentProperties[status.id].expandingSubject"
@@ -85,7 +86,7 @@
               @toggleExpanded="toggleExpanded"
             />
             <div
-              v-if="getReplies(status.id).length > 1"
+              v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1"
               class="thread-ancestor-dive-box"
             >
               <div
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index c43abe8a..a963d204 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -25,6 +25,11 @@ const GeneralTab = {
         value: mode,
         label: this.$t(`settings.conversation_display_${mode}`)
       })),
+      conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.conversation_other_replies_button_${mode}`)
+      })),
       mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
         key: mode,
         value: mode,
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index f97d92c3..d5ae7810 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -161,19 +161,33 @@
             {{ $t('settings.conversation_display') }}
           </ChoiceSetting>
         </li>
-        <li v-if="conversationDisplay !== 'linear'">
-          <label for="maxDepthInThread">
-            {{ $t('settings.max_depth_in_thread') }}
-          </label>
-          <input
-            id="maxDepthInThread"
-            path.number="maxDepthInThread"
-            class="number-input"
-            type="number"
-            min="3"
-            step="1"
-          >
-        </li>
+        <ul
+          v-if="conversationDisplay !== 'linear'"
+          class="setting-list suboptions"
+        >
+          <li>
+            <label for="maxDepthInThread">
+              {{ $t('settings.max_depth_in_thread') }}
+            </label>
+            <input
+              id="maxDepthInThread"
+              path.number="maxDepthInThread"
+              class="number-input"
+              type="number"
+              min="3"
+              step="1"
+            >
+          </li>
+          <li>
+            <ChoiceSetting
+              id="conversationOtherRepliesButton"
+              path="conversationOtherRepliesButton"
+              :options="conversationOtherRepliesButtonOptions"
+            >
+              {{ $t('settings.conversation_other_replies_button') }}
+            </ChoiceSetting>
+          </li>
+        </ul>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 15c2d029..700b9764 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -100,6 +100,7 @@ const Status = {
     'simpleTree',
     'controlledThreadDisplayStatus',
     'controlledToggleThreadDisplay',
+    'showOtherRepliesAsButton',
 
     'controlledShowingTall',
     'controlledToggleShowingTall',
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index e4812711..bc0aeaf0 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -346,7 +346,20 @@
             v-if="inConversation && !isPreview && replies && replies.length"
             class="replies"
           >
-            <span class="faint">{{ $t('status.replies_list') }}</span>
+            <button
+              v-if="showOtherRepliesAsButton"
+              class="button-unstyled -link faint"
+              :title="$tc('status.ancestor_follow', replies.length - 1, { numReplies: replies.length - 1 })"
+              @click.prevent="dive"
+            >
+              {{ $t('status.replies_list') }}
+            </button>
+            <span
+              v-else
+              class="faint"
+            >
+              {{ $t('status.replies_list') }}
+            </span>
             <StatusPopover
               v-for="reply in replies"
               :key="reply.id"
diff --git a/src/modules/config.js b/src/modules/config.js
index b3c7e5ab..a111abca 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -13,6 +13,7 @@ export const multiChoiceProperties = [
   'postContentType',
   'subjectLineBehavior',
   'conversationDisplay', // tree | linear
+  'conversationOtherRepliesButton', // below | inside
   'mentionLinkDisplay' // short | full_for_remote | full
 ]
 
@@ -84,6 +85,7 @@ export const defaultState = {
   virtualScrolling: undefined, // instance default
   sensitiveByDefault: undefined, // instance default
   conversationDisplay: undefined, // instance default
+  conversationOtherRepliesButton: undefined, // instance default
   maxDepthInThread: 6
 }
 
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 853be737..cd17c23d 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -54,6 +54,7 @@ const defaultState = {
   virtualScrolling: true,
   sensitiveByDefault: false,
   conversationDisplay: 'simple_tree',
+  conversationOtherRepliesButton: 'below',
   maxDepthInThread: 6,
 
   // Nasty stuff
-- 
cgit v1.2.3-70-g09d2


From e7f5033c7d05038b116f7170fedc352a662cc646 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sun, 6 Mar 2022 13:57:48 -0500
Subject: Split conversation display style into two different settings

linear => linear (now default)
simple_tree => tree / conversationTreeAdvanced=false
tree => tree / conversationTreeAdvanced=true
---
 src/components/conversation/conversation.js        | 4 ++--
 src/components/settings_modal/tabs/general_tab.js  | 2 +-
 src/components/settings_modal/tabs/general_tab.vue | 5 +++++
 src/modules/config.js                              | 1 +
 src/modules/instance.js                            | 3 ++-
 5 files changed, 11 insertions(+), 4 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index b9ebe7eb..46228e37 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -81,10 +81,10 @@ const conversation = {
       return this.$store.getters.mergedConfig.conversationDisplay
     },
     isTreeView () {
-      return this.displayStyle === 'tree' || this.displayStyle === 'simple_tree'
+      return !this.isLinearView
     },
     treeViewIsSimple () {
-      return this.displayStyle === 'simple_tree'
+      return !this.$store.getters.mergedConfig.conversationTreeAdvanced
     },
     isLinearView () {
       return this.displayStyle === 'linear'
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index a963d204..8ae0021c 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -20,7 +20,7 @@ const GeneralTab = {
         value: mode,
         label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
       })),
-      conversationDisplayOptions: ['tree', 'simple_tree', 'linear'].map(mode => ({
+      conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
         key: mode,
         value: mode,
         label: this.$t(`settings.conversation_display_${mode}`)
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index d5ae7810..28b39d7b 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -165,6 +165,11 @@
           v-if="conversationDisplay !== 'linear'"
           class="setting-list suboptions"
         >
+          <li>
+            <BooleanSetting path="conversationTreeAdvanced">
+              {{ $t('settings.tree_advanced') }}
+            </BooleanSetting>
+          </li>
           <li>
             <label for="maxDepthInThread">
               {{ $t('settings.max_depth_in_thread') }}
diff --git a/src/modules/config.js b/src/modules/config.js
index a111abca..8cb3097c 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -85,6 +85,7 @@ export const defaultState = {
   virtualScrolling: undefined, // instance default
   sensitiveByDefault: undefined, // instance default
   conversationDisplay: undefined, // instance default
+  conversationTreeAdvanced: undefined, // instance default
   conversationOtherRepliesButton: undefined, // instance default
   maxDepthInThread: 6
 }
diff --git a/src/modules/instance.js b/src/modules/instance.js
index cd17c23d..6c5cb4b1 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -53,7 +53,8 @@ const defaultState = {
   theme: 'pleroma-dark',
   virtualScrolling: true,
   sensitiveByDefault: false,
-  conversationDisplay: 'simple_tree',
+  conversationDisplay: 'linear',
+  conversationTreeAdvanced: false,
   conversationOtherRepliesButton: 'below',
   maxDepthInThread: 6,
 
-- 
cgit v1.2.3-70-g09d2


From d157f6738195325c59870fe02df8fa16985c84af Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sun, 6 Mar 2022 14:13:35 -0500
Subject: Make fading ancestors optional

---
 src/components/conversation/conversation.js        | 3 +++
 src/components/conversation/conversation.vue       | 4 ++--
 src/components/settings_modal/tabs/general_tab.vue | 5 +++++
 src/modules/config.js                              | 1 +
 src/modules/instance.js                            | 1 +
 5 files changed, 12 insertions(+), 2 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 46228e37..89ff2f6f 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -89,6 +89,9 @@ const conversation = {
     isLinearView () {
       return this.displayStyle === 'linear'
     },
+    shouldFadeAncestors () {
+      return this.$store.getters.mergedConfig.conversationTreeFadeAncestors
+    },
     otherRepliesButtonPosition () {
       return this.$store.getters.mergedConfig.conversationOtherRepliesButton
     },
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index 73c613b9..3e96fba2 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -50,7 +50,7 @@
             v-for="status in ancestorsOf(diveRoot)"
             :key="status.id"
             class="thread-ancestor"
-            :class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1}"
+            :class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1, '-faded': shouldFadeAncestors}"
           >
             <status
               ref="statusComponent"
@@ -210,7 +210,7 @@
     border-left: 2px solid var(--border, $fallback--border);
   }
 
-  .thread-ancestor .StatusContent {
+  .thread-ancestor.-faded .StatusContent {
     --link: var(--faintLink);
     --text: var(--faint);
     color: var(--text);
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 28b39d7b..6c108292 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -170,6 +170,11 @@
               {{ $t('settings.tree_advanced') }}
             </BooleanSetting>
           </li>
+          <li>
+            <BooleanSetting path="conversationTreeFadeAncestors">
+              {{ $t('settings.tree_fade_ancestors') }}
+            </BooleanSetting>
+          </li>
           <li>
             <label for="maxDepthInThread">
               {{ $t('settings.max_depth_in_thread') }}
diff --git a/src/modules/config.js b/src/modules/config.js
index 8cb3097c..acc71b80 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -87,6 +87,7 @@ export const defaultState = {
   conversationDisplay: undefined, // instance default
   conversationTreeAdvanced: undefined, // instance default
   conversationOtherRepliesButton: undefined, // instance default
+  conversationTreeFadeAncestors: undefined, // instance default
   maxDepthInThread: 6
 }
 
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 6c5cb4b1..f09dd0b3 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -56,6 +56,7 @@ const defaultState = {
   conversationDisplay: 'linear',
   conversationTreeAdvanced: false,
   conversationOtherRepliesButton: 'below',
+  conversationTreeFadeAncestors: false,
   maxDepthInThread: 6,
 
   // Nasty stuff
-- 
cgit v1.2.3-70-g09d2


From 551b8f3690bab1f53ec22ccf6963009a30fa45d0 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 7 Mar 2022 20:02:53 -0500
Subject: Fix "max depth in thread" setting

---
 .../settings_modal/helpers/integer_setting.js      | 37 ++++++++++++++++++++++
 .../settings_modal/helpers/integer_setting.vue     | 20 ++++++++++++
 .../settings_modal/tabs/filtering_tab.js           |  4 ++-
 .../settings_modal/tabs/filtering_tab.vue          | 15 +++------
 src/components/settings_modal/tabs/general_tab.js  |  2 ++
 src/components/settings_modal/tabs/general_tab.vue | 15 +++------
 6 files changed, 72 insertions(+), 21 deletions(-)
 create mode 100644 src/components/settings_modal/helpers/integer_setting.js
 create mode 100644 src/components/settings_modal/helpers/integer_setting.vue

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/helpers/integer_setting.js b/src/components/settings_modal/helpers/integer_setting.js
new file mode 100644
index 00000000..fa6569ab
--- /dev/null
+++ b/src/components/settings_modal/helpers/integer_setting.js
@@ -0,0 +1,37 @@
+import { get, set } from 'lodash'
+import ModifiedIndicator from './modified_indicator.vue'
+export default {
+  components: {
+    ModifiedIndicator
+  },
+  props: {
+    path: String,
+    disabled: Boolean,
+    min: Number
+  },
+  computed: {
+    pathDefault () {
+      const [firstSegment, ...rest] = this.path.split('.')
+      return [firstSegment + 'DefaultValue', ...rest].join('.')
+    },
+    state () {
+      const value = get(this.$parent, this.path)
+      if (value === undefined) {
+        return this.defaultState
+      } else {
+        return value
+      }
+    },
+    defaultState () {
+      return get(this.$parent, this.pathDefault)
+    },
+    isChanged () {
+      return this.state !== this.defaultState
+    }
+  },
+  methods: {
+    update (e) {
+      set(this.$parent, this.path, parseInt(e.target.value))
+    }
+  }
+}
diff --git a/src/components/settings_modal/helpers/integer_setting.vue b/src/components/settings_modal/helpers/integer_setting.vue
new file mode 100644
index 00000000..8284abcc
--- /dev/null
+++ b/src/components/settings_modal/helpers/integer_setting.vue
@@ -0,0 +1,20 @@
+<template>
+  <span class="IntegerSetting">
+    <label :for="path">
+      <slot />
+    </label>
+    <input
+      :id="path"
+      class="number-input"
+      type="number"
+      step="1"
+      :disabled="disabled"
+      :min="min || 0"
+      :value="state"
+      @change="update"
+    >
+    <ModifiedIndicator :changed="isChanged" />
+  </span>
+</template>
+
+<script src="./integer_setting.js"></script>
diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js
index 4eaf4217..73413b48 100644
--- a/src/components/settings_modal/tabs/filtering_tab.js
+++ b/src/components/settings_modal/tabs/filtering_tab.js
@@ -1,6 +1,7 @@
 import { filter, trim } from 'lodash'
 import BooleanSetting from '../helpers/boolean_setting.vue'
 import ChoiceSetting from '../helpers/choice_setting.vue'
+import IntegerSetting from '../helpers/integer_setting.vue'
 
 import SharedComputedObject from '../helpers/shared_computed_object.js'
 
@@ -17,7 +18,8 @@ const FilteringTab = {
   },
   components: {
     BooleanSetting,
-    ChoiceSetting
+    ChoiceSetting,
+    IntegerSetting
   },
   computed: {
     ...SharedComputedObject(),
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 50ee20e0..eb1705e7 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -60,17 +60,12 @@
         </li>
         <h3>{{ $t('settings.attachments') }}</h3>
         <li>
-          <label for="maxThumbnails">
-            {{ $t('settings.max_thumbnails') }}
-          </label>
-          <input
-            id="maxThumbnails"
-            path.number="maxThumbnails"
-            class="number-input"
-            type="number"
-            min="0"
-            step="1"
+          <IntegerSetting
+            path="maxThumbnails"
+            :min="0"
           >
+            {{ $t('settings.max_thumbnails') }}
+          </IntegerSetting>
         </li>
         <li>
           <BooleanSetting path="hideAttachments">
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 8ae0021c..03ca99fa 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -1,5 +1,6 @@
 import BooleanSetting from '../helpers/boolean_setting.vue'
 import ChoiceSetting from '../helpers/choice_setting.vue'
+import IntegerSetting from '../helpers/integer_setting.vue'
 import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
 
 import SharedComputedObject from '../helpers/shared_computed_object.js'
@@ -47,6 +48,7 @@ const GeneralTab = {
   components: {
     BooleanSetting,
     ChoiceSetting,
+    IntegerSetting,
     InterfaceLanguageSwitcher
   },
   computed: {
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 6c108292..ba01fdeb 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -176,17 +176,12 @@
             </BooleanSetting>
           </li>
           <li>
-            <label for="maxDepthInThread">
-              {{ $t('settings.max_depth_in_thread') }}
-            </label>
-            <input
-              id="maxDepthInThread"
-              path.number="maxDepthInThread"
-              class="number-input"
-              type="number"
-              min="3"
-              step="1"
+            <IntegerSetting
+              path="maxDepthInThread"
+              :min="3"
             >
+              {{ $t('settings.max_depth_in_thread') }}
+            </IntegerSetting>
           </li>
           <li>
             <ChoiceSetting
-- 
cgit v1.2.3-70-g09d2


From 726fdbea15009e930ce47faed1e4001b16234a46 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 13 Mar 2022 10:51:13 +0200
Subject: remove "experimental" label from streaming api

---
 src/components/settings_modal/tabs/general_tab.vue | 4 ----
 src/i18n/en.json                                   | 1 -
 2 files changed, 5 deletions(-)

(limited to 'src/components/settings_modal/tabs/general_tab.vue')

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index d4196c3d..ceb61655 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -50,10 +50,6 @@
             expert="1"
           >
             {{ $t('settings.useStreamingApi') }}
-            <br>
-            <small>
-              {{ $t('settings.useStreamingApiWarning') }}
-            </small>
           </BooleanSetting>
         </li>
         <li>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index da5e662d..05549682 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -476,7 +476,6 @@
     "streaming": "Automatically show new posts when scrolled to the top",
     "user_mutes": "Users",
     "useStreamingApi": "Receive posts and notifications real-time",
-    "useStreamingApiWarning": "(Not recommended, experimental, known to skip posts)",
     "text": "Text",
     "theme": "Theme",
     "theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
-- 
cgit v1.2.3-70-g09d2

