From 94d640f9f16c40a2586a00ecd774adbc977c06e0 Mon Sep 17 00:00:00 2001
From: Denys Nykula <vegan@libre.net.ua>
Date: Mon, 1 May 2023 20:50:31 +0300
Subject: [PATCH 01/10] fix dropdown-item-icon and form controls using missing
 variables

---
 src/App.scss                       | 2 +-
 src/components/popover/popover.vue | 2 +-
 src/components/select/select.vue   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/App.scss b/src/App.scss
index 38574cab..36176b35 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -469,7 +469,7 @@ textarea,
   color: $fallback--lightText;
   color: var(--inputText, $fallback--lightText);
   font-family: sans-serif;
-  font-family: var(--inputFont, sans-serif);
+  font-family: var(--interfaceFont, sans-serif);
   font-size: 1em;
   margin: 0;
   box-sizing: border-box;
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index c2a3e801..bd6284b3 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -114,7 +114,7 @@
       svg {
         width: 22px;
         margin-right: 0.75rem;
-        color: var(--menuPopoverIcon, $fallback--icon)
+        color: var(--popoverIcon, $fallback--icon)
       }
     }
 
diff --git a/src/components/select/select.vue b/src/components/select/select.vue
index 92493b0b..7b877ba4 100644
--- a/src/components/select/select.vue
+++ b/src/components/select/select.vue
@@ -38,7 +38,7 @@ label.Select {
     margin: 0;
     padding: 0 2em 0 .2em;
     font-family: sans-serif;
-    font-family: var(--inputFont, sans-serif);
+    font-family: var(--interfaceFont, sans-serif);
     font-size: 1em;
     width: 100%;
     z-index: 1;

From 661a98d38dedbd2075d6d39b245f85b681fee80f Mon Sep 17 00:00:00 2001
From: Denys Nykula <vegan@libre.net.ua>
Date: Mon, 1 May 2023 20:53:29 +0300
Subject: [PATCH 02/10] order bubble after public in sidebar like in other two
 menus

---
 .../timeline_menu/timeline_menu_content.vue   | 32 +++++++++----------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue
index bb170b82..7c351721 100644
--- a/src/components/timeline_menu/timeline_menu_content.vue
+++ b/src/components/timeline_menu/timeline_menu_content.vue
@@ -16,22 +16,6 @@
         >{{ $t("nav.home_timeline") }}</span>
       </router-link>
     </li>
-    <li v-if="bubbleTimelineVisible">
-      <router-link
-        class="menu-item"
-        :to="{ name: 'bubble-timeline' }"
-      >
-        <FAIcon
-          fixed-width
-          class="fa-scale-110 fa-old-padding "
-          icon="circle"
-        />
-        <span
-          :title="$t('nav.bubble_timeline_description')"
-          :aria-label="$t('nav.bubble_timeline_description')"
-        >{{ $t("nav.bubble_timeline") }}</span>
-      </router-link>
-    </li>
     <li v-if="publicTimelineVisible">
       <router-link
         class="menu-item"
@@ -48,6 +32,22 @@
         >{{ $t("nav.public_tl") }}</span>
       </router-link>
     </li>
+    <li v-if="bubbleTimelineVisible">
+      <router-link
+        class="menu-item"
+        :to="{ name: 'bubble-timeline' }"
+      >
+        <FAIcon
+          fixed-width
+          class="fa-scale-110 fa-old-padding "
+          icon="circle"
+        />
+        <span
+          :title="$t('nav.bubble_timeline_description')"
+          :aria-label="$t('nav.bubble_timeline_description')"
+        >{{ $t("nav.bubble_timeline") }}</span>
+      </router-link>
+    </li>
     <li v-if="federatedTimelineVisible">
       <router-link
         class="menu-item"

From 2a76be56e71c3edb0cf37eb87ec9003b100ac33e Mon Sep 17 00:00:00 2001
From: Denys Nykula <vegan@libre.net.ua>
Date: Mon, 1 May 2023 20:54:18 +0300
Subject: [PATCH 03/10] fix apply theme button without page refresh

---
 .../tabs/theme_tab/theme_tab.scss              |  4 ++++
 src/services/style_setter/style_setter.js      | 18 +++++++++++-------
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
index 0427e42f..0016a328 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
@@ -89,6 +89,10 @@
     margin: 1em 1em 0;
   }
 
+  .presets {
+    text-align: center;
+  }
+
   .tab-header {
     display: flex;
     justify-content: space-between;
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 9e691261..8954a9ce 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -7,14 +7,18 @@ export const applyTheme = (input) => {
   const body = document.body
   body.classList.add('hidden')
 
-  const styleEl = document.getElementById('theme-holder')
-  const styleSheet = styleEl.sheet
+  /** @type {CSSStyleSheet} */
+  const styleSheet = document.getElementById('theme-holder').sheet
+
+  for (let i = styleSheet.cssRules.length; i--; ) {
+    styleSheet.deleteRule(0)
+  }
+
+  styleSheet.insertRule(
+    `:root { ${rules.radii}; ${rules.colors}; ${rules.shadows}; ${rules.fonts}; }`,
+    0
+  )
 
-  styleSheet.toString()
-  styleSheet.insertRule(`:root { ${rules.radii} }`, 'index-max')
-  styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max')
-  styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max')
-  styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max')
   body.classList.remove('hidden')
 }
 

From 8c6ccc321d5c4b0126f274b14ea6e26a99b483cc Mon Sep 17 00:00:00 2001
From: Denys Nykula <vegan@libre.net.ua>
Date: Mon, 15 May 2023 03:11:07 +0300
Subject: [PATCH 04/10] fix unfinished post being sent when scrolling

---
 src/components/post_status_form/post_status_form.vue | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 02468f17..f4680336 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -291,12 +291,14 @@
         >
           {{ $t('post_status.post') }}
         </button>
-        <!-- touchstart is used to keep the OSK at the same position after a message send -->
+        <!-- To keep the OSK at the same position after a message send, -->
+        <!-- @touchstart.stop.prevent was used. But while OSK position is -->
+        <!-- quirky, accidental mobile posts caused by the workaround -->
+        <!-- when people tried to scroll were a more serious bug. -->
         <button
           v-else
           :disabled="uploadingFiles || disableSubmit"
           class="btn button-default"
-          @touchstart.stop.prevent="postStatus($event, newStatus)"
           @click.stop.prevent="postStatus($event, newStatus)"
         >
           {{ $t('post_status.post') }}

From 6b3b55455df59f031f4e9fc1264db9f76f298f95 Mon Sep 17 00:00:00 2001
From: Denys Nykula <vegan@libre.net.ua>
Date: Thu, 18 May 2023 23:05:19 +0300
Subject: [PATCH 05/10] paper theme: more contrast and fix setting tab hover

---
 static/themes/paper.json | 37 ++++++++++++++++---------------------
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/static/themes/paper.json b/static/themes/paper.json
index a3b90a0a..fa697a7a 100644
--- a/static/themes/paper.json
+++ b/static/themes/paper.json
@@ -70,31 +70,21 @@
       "buttonHover": [
         {
           "x": 0,
-          "y": "2",
-          "blur": "5",
-          "spread": 0,
+          "y": 2,
+          "blur": 3,
+          "spread": -2,
           "color": "#494949",
-          "alpha": "0.1"
-        },
-        {
-          "x": 0,
-          "y": "2",
-          "blur": "0",
-          "spread": "20",
-          "color": "#ffffff",
-          "alpha": "1",
-          "inset": true
+          "alpha": "0.5"
         }
       ],
       "buttonPressed": [
         {
           "x": 0,
-          "y": 0,
-          "blur": "4",
-          "spread": "0",
+          "y": 2,
+          "blur": 3,
+          "spread": -3,
           "color": "#494949",
-          "alpha": "0.8",
-          "inset": false
+          "alpha": "0.5"
         }
       ],
       "avatarStatus": [
@@ -138,14 +128,18 @@
       ]
     },
     "opacity": {
-      "underlay": "1",
+      "underlay": 0,
       "border": "0"
     },
     "colors": {
       "bg": "#ffffff",
       "fg": "#f6f6f6",
-      "text": "#494949",
-      "underlay": "#ffffff",
+      "text": "#222222",
+      "underlay": "#f1f2f3",
+      "wallpaper": "#f1f2f3",
+      "selectedMenu": "#f1f2f3",
+      "selectedMenuPopover": "#f1f2f3",
+      "selectedPost": "#f1f2f3",
       "link": "#788ca1",
       "accent": "#97a0aa",
       "cBlue": "#788ca1",
@@ -156,6 +150,7 @@
       "border": "#ffffff",
       "icon": "#b6c9c4",
       "panel": "#ffffff",
+      "topBar": "#ffffff",
       "topBarText": "#4b4b4b"
     },
     "radii": {

From a2ceb89d5e2444169eef5046fcea6ed3117c16e9 Mon Sep 17 00:00:00 2001
From: Weblate <noreply@weblate.org>
Date: Sun, 21 May 2023 20:58:06 +0000
Subject: [PATCH 06/10] Translated using Weblate (Turkish)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently translated at 4.0% (42 of 1042 strings)

Added translation using Weblate (Turkish)

Co-authored-by: Hasan Yıldız <hasanyildiz0@yaani.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/tr/
Translation: Pleroma fe/pleroma-fe
---
 src/i18n/tr.json | 54 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 src/i18n/tr.json

diff --git a/src/i18n/tr.json b/src/i18n/tr.json
new file mode 100644
index 00000000..43c498c8
--- /dev/null
+++ b/src/i18n/tr.json
@@ -0,0 +1,54 @@
+{
+    "about": {
+        "bubble_instances": "Yerel Balon Örnekleri",
+        "bubble_instances_description": "Yöneticiler tarafından bu örneğin yerel alanını temsil etmesi için seçilen örnekler",
+        "mrf": {
+            "federation": "Federasyon",
+            "keyword": {
+                "ftl_removal": "\"Bilinen Tüm Ağ\" Zaman Çizelgesinden Kaldırma",
+                "is_replaced_by": "→",
+                "keyword_policies": "Anahtar kelime politikaları",
+                "reject": "Reddetmek",
+                "replace": "Yer değiştirmek"
+            },
+            "mrf_policies": "Etkinleştirilmiş MRF politikaları",
+            "mrf_policies_desc": "MRF ilkeleri, örneğin federasyon davranışını manipüle eder. Aşağıdaki politikalar etkinleştirildi:",
+            "simple": {
+                "accept": "Kabul etmek",
+                "accept_desc": "Bu örnek yalnızca aşağıdaki örneklerden gelen mesajları kabul eder:",
+                "ftl_removal": "\"Bilinen Ağ\" Zaman Çizelgesinden Kaldırma",
+                "ftl_removal_desc": "Bu örnek, şu örnekleri \"Bilinen Ağ\" zaman çizelgesinden kaldırır:",
+                "instance": "Örnek",
+                "media_nsfw": "Medya hassas olarak ayarlandı",
+                "media_nsfw_desc": "Bu örnek, medyayı aşağıdaki örneklerdeki gönderilerde hassas olarak ayarlanmasına zorlar:",
+                "media_removal": "Medya Kaldırma",
+                "media_removal_desc": "Bu örnek, aşağıdaki örneklerdeki yayınlardan medyayı kaldırır:",
+                "not_applicable": "Yok",
+                "quarantine": "Karantina",
+                "quarantine_desc": "Bu örnek, aşağıdaki örneklere gönderi göndermeyecek:",
+                "reason": "Sebep",
+                "reject": "Reddetmek",
+                "reject_desc": "Bu örnek, aşağıdaki örneklerden gelen mesajları kabul etmeyecektir:",
+                "simple_policies": "Örneğe özgü politikalar"
+            }
+        },
+        "staff": "Kadro"
+    },
+    "announcements": {
+        "all_day_prompt": "Bu tüm gün süren bir etkinlik",
+        "cancel_edit_action": "İptal etmek",
+        "close_error": "Kapalı",
+        "delete_action": "Sil",
+        "edit_action": "Düzenle",
+        "end_time_display": "{time} saatinde biter",
+        "end_time_prompt": "Bitiş zamanı: ",
+        "inactive_message": "Bu duyuru etkin değil",
+        "mark_as_read_action": "Okundu olarak işaretle",
+        "page_header": "Duyurular",
+        "post_action": "Post",
+        "post_error": "Hata: {error}",
+        "post_form_header": "Post sonrası",
+        "post_placeholder": "Duyuru içeriği",
+        "published_time_display": "{time} tarihinde yayınlandı"
+    }
+}

From 5fa305c58ca3a82b9d6e32d9ad21ab60b743909a Mon Sep 17 00:00:00 2001
From: Weblate <noreply@weblate.org>
Date: Sun, 21 May 2023 20:58:06 +0000
Subject: [PATCH 07/10] Translated using Weblate (Japanese (ja_EASY))

Currently translated at 72.0% (751 of 1042 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: kazari <6c577a54-aac9-482a-955e-745c858445e3@simplelogin.com>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_EASY/
Translation: Pleroma fe/pleroma-fe
---
 src/i18n/ja_easy.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/i18n/ja_easy.json b/src/i18n/ja_easy.json
index ed300b4a..80fdde44 100644
--- a/src/i18n/ja_easy.json
+++ b/src/i18n/ja_easy.json
@@ -4,6 +4,7 @@
         "mrf": {
             "federation": "フェデレーション",
             "keyword": {
+                "is_replaced_by": "→",
                 "keyword_policies": "キーワードポリシー",
                 "reject": "おことわり",
                 "replace": "おきかえ"

From 04bcf7d8043cb7dda573e8ac22f8bedbd409f04a Mon Sep 17 00:00:00 2001
From: Weblate <noreply@weblate.org>
Date: Sun, 21 May 2023 20:58:06 +0000
Subject: [PATCH 08/10] Translated using Weblate (Polish)

Currently translated at 66.1% (689 of 1042 strings)

Co-authored-by: Jeder <jeder@jeder.pl>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/pl/
Translation: Pleroma fe/pleroma-fe
---
 src/i18n/pl.json | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/src/i18n/pl.json b/src/i18n/pl.json
index 310ff6de..3727875a 100644
--- a/src/i18n/pl.json
+++ b/src/i18n/pl.json
@@ -1,5 +1,7 @@
 {
     "about": {
+        "bubble_instances": "Instancje lokalnej bańki",
+        "bubble_instances_description": "Instancje wybrane przez administratorów w celu przedstawienia okolicy tej instancji",
         "mrf": {
             "federation": "Federacja",
             "keyword": {
@@ -16,12 +18,15 @@
                 "accept_desc": "Ta instancja akceptuje tylko posty z wymienionych instancji:",
                 "ftl_removal": "Usunięcie z „Całej znanej sieci”",
                 "ftl_removal_desc": "Ta instancja usuwa wymienionych instancje z „Całej znanej sieci”:",
+                "instance": "Instacja",
                 "media_nsfw": "Multimedia ustawione jako wrażliwe",
                 "media_nsfw_desc": "Ta instancja wymusza, by multimedia z wymienionych instancji były ustawione jako wrażliwe:",
                 "media_removal": "Usuwanie multimediów",
                 "media_removal_desc": "Ta instancja usuwa multimedia z postów od wymienionych instancji:",
+                "not_applicable": "N/A",
                 "quarantine": "Kwarantanna",
-                "quarantine_desc": "Ta instancja wysyła tylko publiczne posty do wymienionych instancji:",
+                "quarantine_desc": "Ta instancja nie wysyła postów do wymienionych instancji:",
+                "reason": "Powód",
                 "reject": "Odrzucanie",
                 "reject_desc": "Ta instancja odrzuca posty z wymienionych instancji:",
                 "simple_policies": "Zasady specyficzne dla instancji"
@@ -29,6 +34,27 @@
         },
         "staff": "Administracja"
     },
+    "announcements": {
+        "all_day_prompt": "Jest to całodzienne wydarzenie",
+        "cancel_edit_action": "Anuluj",
+        "close_error": "Zamknij",
+        "delete_action": "Usuń",
+        "edit_action": "Edytuj",
+        "end_time_display": "Kończy się o: {time}",
+        "end_time_prompt": "Koniec: ",
+        "inactive_message": "To ogłoszenie jest nieaktywne",
+        "mark_as_read_action": "Oznacz jako przeczytane",
+        "page_header": "Ogłoszenia",
+        "post_action": "Wyślij",
+        "post_error": "Błąd: {error}",
+        "post_form_header": "Wyślij ogłoszenie",
+        "post_placeholder": "Zawartość ogłoszenia",
+        "published_time_display": "Opublikowano o {time}",
+        "start_time_display": "Zaczyna się o: {time}",
+        "start_time_prompt": "Początek: ",
+        "submit_edit_action": "Wyślij",
+        "title": "Ogłoszenie"
+    },
     "chats": {
         "chats": "Czaty",
         "delete": "Usuń",
@@ -58,6 +84,7 @@
         "keep_open": "Zostaw selektor otwarty",
         "load_all": "Ładuję wszystkie {emojiAmount} emoji",
         "load_all_hint": "Załadowano pierwsze {saneAmount} emoji, Załadowanie wszystkich emoji może spowodować problemy z wydajnością.",
+        "recent": "Ostatnio używane",
         "search_emoji": "Wyszukaj emoji",
         "stickers": "Naklejki",
         "unicode": "Emoji unicode"
@@ -837,4 +864,4 @@
         "more": "Więcej",
         "who_to_follow": "Propozycje obserwacji"
     }
-}
\ No newline at end of file
+}

From 60ff715aff1d7aeed9fccae79e73e0871418bcc8 Mon Sep 17 00:00:00 2001
From: Weblate <noreply@weblate.org>
Date: Sun, 21 May 2023 20:58:06 +0000
Subject: [PATCH 09/10] Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1042 of 1042 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/zh_Hans/
Translation: Pleroma fe/pleroma-fe
---
 src/i18n/zh.json | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/i18n/zh.json b/src/i18n/zh.json
index 8107d76a..6488d654 100644
--- a/src/i18n/zh.json
+++ b/src/i18n/zh.json
@@ -84,6 +84,7 @@
         "keep_open": "选择器保持打开",
         "load_all": "加载全部 {emojiAmount} 个表情符号中",
         "load_all_hint": "已加载前 {saneAmount} 个表情符号,加载全部表情符号可能会带来性能问题。",
+        "recent": "最近使用",
         "search_emoji": "搜索表情符号",
         "stickers": "贴纸",
         "unicode": "Unicode 表情符号"
@@ -732,7 +733,7 @@
         "settings": "设置",
         "settings_profile": "设置配置文件",
         "settings_profile_creation": "创建新的配置文件",
-        "settings_profile_creation_new_name_label": "名字",
+        "settings_profile_creation_new_name_label": "名称",
         "settings_profile_creation_submit": "创建",
         "settings_profile_currently": "目前使用 {name}(版本:{version})",
         "settings_profile_delete": "删除",
@@ -920,7 +921,7 @@
         "type_domains_to_mute": "搜索需要静音的域名",
         "upload_a_photo": "上传照片",
         "useStreamingApi": "实时接收帖文和通知",
-        "useStreamingApiWarning": "(不推荐使用,试验性,已知会跳过一些帖文)",
+        "useStreamingApiWarning": "十分炫酷推荐使用。要是崩了试试刷新?",
         "use_at_icon": "将 {'@'} 符号显示为图标而不是文本",
         "use_blurhash": "对NSFW的缩略图使用模糊处理",
         "use_contain_fit": "生成缩略图时不要裁剪附件",

From 2f479c670fae0ac0d8262efaaf793938552ea0f3 Mon Sep 17 00:00:00 2001
From: FloatingGhost <hannah@coffee-and-dreams.uk>
Date: Tue, 23 May 2023 13:46:59 +0100
Subject: [PATCH 10/10] Add DM settings

---
 src/components/settings_modal/tabs/general_tab.vue   |  1 -
 src/components/settings_modal/tabs/profile_tab.js    | 11 ++++++++++-
 src/components/settings_modal/tabs/profile_tab.vue   | 12 ++++++++++++
 src/i18n/en.json                                     |  4 ++++
 .../entity_normalizer/entity_normalizer.service.js   |  1 +
 5 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 5d135711..7dc1c3f1 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -21,7 +21,6 @@
             >
               {{ $t('settings.settings_profile_force_sync') }}
             </button>
-
           </p>
           <div
             @click="toggleExpandedSettings"
diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js
index c29e8d2a..46cb91e5 100644
--- a/src/components/settings_modal/tabs/profile_tab.js
+++ b/src/components/settings_modal/tabs/profile_tab.js
@@ -12,6 +12,7 @@ import InterfaceLanguageSwitcher from 'src/components/interface_language_switche
 import BooleanSetting from '../helpers/boolean_setting.vue'
 import SharedComputedObject from '../helpers/shared_computed_object.js'
 import localeService from 'src/services/locale/locale.service.js'
+import ChoiceSetting from '../helpers/choice_setting.vue'
 
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
@@ -46,9 +47,16 @@ const ProfileTab = {
       emailLanguage: this.$store.state.users.currentUser.language || '',
       newPostTTLDays: this.$store.state.users.currentUser.status_ttl_days,
       expirePosts: this.$store.state.users.currentUser.status_ttl_days !== null,
+      userAcceptsDirectMessagesFrom: this.$store.state.users.currentUser.accepts_direct_messages_from,
+      userAcceptsDirectMessagesFromOptions: ["everybody", "nobody", "people_i_follow"].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.user_accepts_direct_messages_from_${mode}`)
+      }))
     }
   },
   components: {
+    ChoiceSetting,
     ScopeSelector,
     ImageCropper,
     EmojiInput,
@@ -126,7 +134,8 @@ const ProfileTab = {
         fields_attributes: this.newFields.filter(el => el != null),
         bot: this.bot,
         show_role: this.showRole,
-        status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1
+        status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1,
+        accepts_direct_messages_from: this.userAcceptsDirectMessagesFrom
         /* eslint-enable camelcase */
       }
 
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index 8748b685..9f80582f 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -89,6 +89,15 @@
           {{ $t('settings.bot') }}
         </Checkbox>
       </p>
+      <p>
+        <ChoiceSetting
+          id="userAcceptsDirectMessagesFrom"
+          path="userAcceptsDirectMessagesFrom"
+          :options="userAcceptsDirectMessagesFromOptions"
+        >
+          {{ $t('settings.user_accepts_direct_messages_from') }}
+        </ChoiceSetting>
+      </p>
       <p>
         <Checkbox v-model="expirePosts">
           {{ $t('settings.expire_posts_enabled') }}
@@ -102,6 +111,9 @@
           class="expire-posts-days"
           :placeholder="$t('settings.expire_posts_input_placeholder')"
         />
+      </p>
+      <p>
+
       </p>
       <p>
         <interface-language-switcher
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 92618047..a5e9d9c1 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -929,6 +929,10 @@
         "user_profile_default_tab": "Default Tab on User Profile",
         "user_profiles": "User Profiles",
         "user_settings": "User Settings",
+        "user_accepts_direct_messages_from": "Accept DMs From",
+        "user_accepts_direct_messages_from_everybody": "Everybody",
+        "user_accepts_direct_messages_from_nobody": "Nobody",
+        "user_accepts_direct_messages_from_people_i_follow": "People I follow",
         "valid_until": "Valid until",
         "values": {
             "false": "no",
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index e330ca8c..4fddd875 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -90,6 +90,7 @@ export const parseUser = (data) => {
     output.friends_count = data.following_count
 
     output.bot = data.bot
+    output.accepts_direct_messages_from = data.accepts_direct_messages_from
     output.follow_requests_count = data.follow_requests_count
     if (data.akkoma) {
       output.instance = data.akkoma.instance