From a8c4aec721f8438f1aefc8595bb1ac7889dcad83 Mon Sep 17 00:00:00 2001
From: floatingghost <hannah@coffee-and-dreams.uk>
Date: Wed, 20 Jul 2022 15:36:45 +0000
Subject: [PATCH] purge shout/chat (#49)

Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/49
---
 src/App.js                                    |  13 -
 src/App.vue                                   |   8 +-
 src/_variables.scss                           |   1 -
 src/boot/after_store.js                       |   3 -
 src/boot/routes.js                            |  11 -
 .../account_actions/account_actions.js        |  12 -
 .../account_actions/account_actions.vue       |   7 -
 src/components/chat/chat.js                   | 346 ------------------
 src/components/chat/chat.scss                 | 108 ------
 src/components/chat/chat.vue                  | 100 -----
 src/components/chat/chat_layout_utils.js      |  24 --
 src/components/chat_list/chat_list.js         |  37 --
 src/components/chat_list/chat_list.vue        |  64 ----
 .../chat_list_item/chat_list_item.js          |  69 ----
 .../chat_list_item/chat_list_item.scss        |  91 -----
 .../chat_list_item/chat_list_item.vue         |  53 ---
 src/components/chat_message/chat_message.js   | 108 ------
 src/components/chat_message/chat_message.scss | 179 ---------
 src/components/chat_message/chat_message.vue  | 103 ------
 src/components/chat_new/chat_new.js           |  83 -----
 src/components/chat_new/chat_new.scss         |  31 --
 src/components/chat_new/chat_new.vue          |  51 ---
 src/components/chat_title/chat_title.js       |  27 --
 src/components/chat_title/chat_title.vue      |  65 ----
 .../features_panel/features_panel.js          |   3 -
 .../features_panel/features_panel.vue         |   9 -
 src/components/mobile_nav/mobile_nav.js       |   7 +-
 src/components/mobile_nav/mobile_nav.vue      |   2 +-
 .../mobile_post_status_button.js              |   7 -
 src/components/nav_panel/nav_panel.js         |   5 +-
 src/components/nav_panel/nav_panel.vue        |  18 -
 src/components/notifications/notifications.js |   4 +-
 .../settings_modal/tabs/general_tab.js        |   1 -
 .../settings_modal/tabs/general_tab.vue       |   8 -
 .../tabs/theme_tab/theme_tab.js               |   6 +-
 .../tabs/theme_tab/theme_tab.vue              |  67 ----
 src/components/shout_panel/shout_panel.js     |  53 ---
 src/components/shout_panel/shout_panel.vue    | 156 --------
 src/components/side_drawer/side_drawer.js     |   8 +-
 src/components/side_drawer/side_drawer.vue    |  33 --
 src/main.js                                   |   4 -
 src/modules/api.js                            |  32 --
 src/modules/chats.js                          | 240 ------------
 src/modules/config.js                         |   2 -
 src/modules/instance.js                       |   8 -
 src/modules/serverSideConfig.js               |   4 -
 src/modules/shout.js                          |  46 ---
 src/modules/users.js                          |   8 -
 src/services/api/api.service.js               |  92 +----
 src/services/chat_service/chat_service.js     | 226 ------------
 src/services/chat_utils/chat_utils.js         |  41 ---
 .../entity_normalizer.service.js              |  35 +-
 src/services/style_setter/style_setter.js     |   3 +-
 src/services/theme_data/pleromafe.js          |  57 +--
 .../chat_service/chat_service.spec.js         | 108 ------
 55 files changed, 15 insertions(+), 2872 deletions(-)
 delete mode 100644 src/components/chat/chat.js
 delete mode 100644 src/components/chat/chat.scss
 delete mode 100644 src/components/chat/chat.vue
 delete mode 100644 src/components/chat/chat_layout_utils.js
 delete mode 100644 src/components/chat_list/chat_list.js
 delete mode 100644 src/components/chat_list/chat_list.vue
 delete mode 100644 src/components/chat_list_item/chat_list_item.js
 delete mode 100644 src/components/chat_list_item/chat_list_item.scss
 delete mode 100644 src/components/chat_list_item/chat_list_item.vue
 delete mode 100644 src/components/chat_message/chat_message.js
 delete mode 100644 src/components/chat_message/chat_message.scss
 delete mode 100644 src/components/chat_message/chat_message.vue
 delete mode 100644 src/components/chat_new/chat_new.js
 delete mode 100644 src/components/chat_new/chat_new.scss
 delete mode 100644 src/components/chat_new/chat_new.vue
 delete mode 100644 src/components/chat_title/chat_title.js
 delete mode 100644 src/components/chat_title/chat_title.vue
 delete mode 100644 src/components/shout_panel/shout_panel.js
 delete mode 100644 src/components/shout_panel/shout_panel.vue
 delete mode 100644 src/modules/chats.js
 delete mode 100644 src/modules/shout.js
 delete mode 100644 src/services/chat_service/chat_service.js
 delete mode 100644 src/services/chat_utils/chat_utils.js
 delete mode 100644 test/unit/specs/services/chat_service/chat_service.spec.js

diff --git a/src/App.js b/src/App.js
index f01f8788..4304787f 100644
--- a/src/App.js
+++ b/src/App.js
@@ -3,7 +3,6 @@ import NavPanel from './components/nav_panel/nav_panel.vue'
 import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'
 import FeaturesPanel from './components/features_panel/features_panel.vue'
 import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
-import ShoutPanel from './components/shout_panel/shout_panel.vue'
 import SettingsModal from './components/settings_modal/settings_modal.vue'
 import MediaModal from './components/media_modal/media_modal.vue'
 import SideDrawer from './components/side_drawer/side_drawer.vue'
@@ -26,7 +25,6 @@ export default {
     InstanceSpecificPanel,
     FeaturesPanel,
     WhoToFollowPanel,
-    ShoutPanel,
     MediaModal,
     SideDrawer,
     MobilePostStatusButton,
@@ -75,27 +73,16 @@ export default {
         }
       }
     },
-    shout () { return this.$store.state.shout.joined },
     suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
     showInstanceSpecificPanel () {
       return this.$store.state.instance.showInstanceSpecificPanel &&
         !this.$store.getters.mergedConfig.hideISP &&
         this.$store.state.instance.instanceSpecificPanelContent
     },
-    isChats () {
-      return this.$route.name === 'chat' || this.$route.name === 'chats'
-    },
     newPostButtonShown () {
-      if (this.isChats) return false
       return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile'
     },
     showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
-    shoutboxPosition () {
-      return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false
-    },
-    hideShoutbox () {
-      return this.$store.getters.mergedConfig.hideShoutbox
-    },
     layoutType () { return this.$store.state.interface.layoutType },
     privateMode () { return this.$store.state.instance.private },
     reverseLayout () {
diff --git a/src/App.vue b/src/App.vue
index d1d4217b..c3cf33f8 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -33,7 +33,7 @@
       <div
         id="main-scroller"
         class="column main"
-        :class="{ '-full-height': isChats }"
+        :class="{ '-full-height': false }"
       >
         <div
           v-if="!currentUser"
@@ -55,12 +55,6 @@
       />
     </div>
     <media-modal />
-    <shout-panel
-      v-if="currentUser && shout && !hideShoutbox"
-      :floating="true"
-      class="floating-shout mobile-hidden"
-      :class="{ '-left': shoutboxPosition }"
-    />
     <MobilePostStatusButton />
     <UserReportingModal />
     <PostStatusModal />
diff --git a/src/_variables.scss b/src/_variables.scss
index 099d3606..65ce4027 100644
--- a/src/_variables.scss
+++ b/src/_variables.scss
@@ -27,7 +27,6 @@ $fallback--tooltipRadius: 5px;
 $fallback--avatarRadius: 4px;
 $fallback--avatarAltRadius: 10px;
 $fallback--attachmentRadius: 10px;
-$fallback--chatMessageRadius: 10px;
 
 $fallback--buttonShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
 
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index b8bbbe38..b2c8d59d 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -247,9 +247,6 @@ const getNodeInfo = async ({ store }) => {
       store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
       store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
       store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') })
-      store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') })
-      store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
-      store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
       store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
       store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
       store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
diff --git a/src/boot/routes.js b/src/boot/routes.js
index 00f553c2..00ffda66 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -6,8 +6,6 @@ import BookmarkTimeline from 'components/bookmark_timeline/bookmark_timeline.vue
 import ConversationPage from 'components/conversation-page/conversation-page.vue'
 import Interactions from 'components/interactions/interactions.vue'
 import DMs from 'components/dm_timeline/dm_timeline.vue'
-import ChatList from 'components/chat_list/chat_list.vue'
-import Chat from 'components/chat/chat.vue'
 import UserProfile from 'components/user_profile/user_profile.vue'
 import Search from 'components/search/search.vue'
 import Registration from 'components/registration/registration.vue'
@@ -16,7 +14,6 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue'
 import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
 import Notifications from 'components/notifications/notifications.vue'
 import AuthForm from 'components/auth_form/auth_form.js'
-import ShoutPanel from 'components/shout_panel/shout_panel.vue'
 import WhoToFollow from 'components/who_to_follow/who_to_follow.vue'
 import About from 'components/about/about.vue'
 import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue'
@@ -68,7 +65,6 @@ export default (store) => {
     { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
     { name: 'notifications', path: '/:username/notifications', component: Notifications, props: () => ({ disableTeleport: true }), beforeEnter: validateAuthenticatedRoute },
     { name: 'login', path: '/login', component: AuthForm },
-    { name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) },
     { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
     { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) },
     { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },
@@ -80,12 +76,5 @@ export default (store) => {
     { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile }
   ]
 
-  if (store.state.instance.pleromaChatMessagesAvailable) {
-    routes = routes.concat([
-      { name: 'chat', path: '/users/:username/chats/:recipient_id', component: Chat, meta: { dontScroll: false }, beforeEnter: validateAuthenticatedRoute },
-      { name: 'chats', path: '/users/:username/chats', component: ChatList, meta: { dontScroll: false }, beforeEnter: validateAuthenticatedRoute }
-    ])
-  }
-
   return routes
 }
diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js
index 99762562..8fe0fe5e 100644
--- a/src/components/account_actions/account_actions.js
+++ b/src/components/account_actions/account_actions.js
@@ -1,4 +1,3 @@
-import { mapState } from 'vuex'
 import ProgressButton from '../progress_button/progress_button.vue'
 import Popover from '../popover/popover.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
@@ -36,18 +35,7 @@ const AccountActions = {
     },
     reportUser () {
       this.$store.dispatch('openUserReportingModal', { userId: this.user.id })
-    },
-    openChat () {
-      this.$router.push({
-        name: 'chat',
-        params: { username: this.$store.state.users.currentUser.screen_name, recipient_id: this.user.id }
-      })
     }
-  },
-  computed: {
-    ...mapState({
-      pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
-    })
   }
 }
 
diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue
index c35d01af..afd8bb1b 100644
--- a/src/components/account_actions/account_actions.vue
+++ b/src/components/account_actions/account_actions.vue
@@ -48,13 +48,6 @@
           >
             {{ $t('user_card.report') }}
           </button>
-          <button
-            v-if="pleromaChatMessagesAvailable"
-            class="btn button-default btn-block dropdown-item"
-            @click="openChat"
-          >
-            {{ $t('user_card.message') }}
-          </button>
         </div>
       </template>
       <template v-slot:trigger>
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
deleted file mode 100644
index 9f6e64e3..00000000
--- a/src/components/chat/chat.js
+++ /dev/null
@@ -1,346 +0,0 @@
-import _ from 'lodash'
-import { WSConnectionStatus } from '../../services/api/api.service.js'
-import { mapGetters, mapState } from 'vuex'
-import ChatMessage from '../chat_message/chat_message.vue'
-import PostStatusForm from '../post_status_form/post_status_form.vue'
-import ChatTitle from '../chat_title/chat_title.vue'
-import chatService from '../../services/chat_service/chat_service.js'
-import { promiseInterval } from '../../services/promise_interval/promise_interval.js'
-import { getScrollPosition, getNewTopPosition, isBottomedOut, isScrollable } from './chat_layout_utils.js'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
-  faChevronDown,
-  faChevronLeft
-} from '@fortawesome/free-solid-svg-icons'
-import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js'
-
-library.add(
-  faChevronDown,
-  faChevronLeft
-)
-
-const BOTTOMED_OUT_OFFSET = 10
-const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 10
-const SAFE_RESIZE_TIME_OFFSET = 100
-const MARK_AS_READ_DELAY = 1500
-const MAX_RETRIES = 10
-
-const Chat = {
-  components: {
-    ChatMessage,
-    ChatTitle,
-    PostStatusForm
-  },
-  data () {
-    return {
-      jumpToBottomButtonVisible: false,
-      hoveredMessageChainId: undefined,
-      lastScrollPosition: {},
-      scrollableContainerHeight: '100%',
-      errorLoadingChat: false,
-      messageRetriers: {}
-    }
-  },
-  created () {
-    this.startFetching()
-    window.addEventListener('resize', this.handleResize)
-  },
-  mounted () {
-    window.addEventListener('scroll', this.handleScroll)
-    if (typeof document.hidden !== 'undefined') {
-      document.addEventListener('visibilitychange', this.handleVisibilityChange, false)
-    }
-
-    this.$nextTick(() => {
-      this.handleResize()
-    })
-  },
-  unmounted () {
-    window.removeEventListener('scroll', this.handleScroll)
-    if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false)
-    this.$store.dispatch('clearCurrentChat')
-  },
-  computed: {
-    recipient () {
-      return this.currentChat && this.currentChat.account
-    },
-    recipientId () {
-      return this.$route.params.recipient_id
-    },
-    formPlaceholder () {
-      if (this.recipient) {
-        return this.$t('chats.message_user', { nickname: this.recipient.screen_name_ui })
-      } else {
-        return ''
-      }
-    },
-    chatViewItems () {
-      return chatService.getView(this.currentChatMessageService)
-    },
-    newMessageCount () {
-      return this.currentChatMessageService && this.currentChatMessageService.newMessageCount
-    },
-    streamingEnabled () {
-      return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED
-    },
-    ...mapGetters([
-      'currentChat',
-      'currentChatMessageService',
-      'findOpenedChatByRecipientId',
-      'mergedConfig'
-    ]),
-    ...mapState({
-      backendInteractor: state => state.api.backendInteractor,
-      mastoUserSocketStatus: state => state.api.mastoUserSocketStatus,
-      mobileLayout: state => state.interface.layoutType === 'mobile',
-      currentUser: state => state.users.currentUser
-    })
-  },
-  watch: {
-    chatViewItems () {
-      // We don't want to scroll to the bottom on a new message when the user is viewing older messages.
-      // Therefore we need to know whether the scroll position was at the bottom before the DOM update.
-      const bottomedOutBeforeUpdate = this.bottomedOut(BOTTOMED_OUT_OFFSET)
-      this.$nextTick(() => {
-        if (bottomedOutBeforeUpdate) {
-          this.scrollDown()
-        }
-      })
-    },
-    '$route': function () {
-      this.startFetching()
-    },
-    mastoUserSocketStatus (newValue) {
-      if (newValue === WSConnectionStatus.JOINED) {
-        this.fetchChat({ isFirstFetch: true })
-      }
-    }
-  },
-  methods: {
-    // Used to animate the avatar near the first message of the message chain when any message belonging to the chain is hovered
-    onMessageHover ({ isHovered, messageChainId }) {
-      this.hoveredMessageChainId = isHovered ? messageChainId : undefined
-    },
-    onFilesDropped () {
-      this.$nextTick(() => {
-        this.handleResize()
-      })
-    },
-    handleVisibilityChange () {
-      this.$nextTick(() => {
-        if (!document.hidden && this.bottomedOut(BOTTOMED_OUT_OFFSET)) {
-          this.scrollDown({ forceRead: true })
-        }
-      })
-    },
-    // "Sticks" scroll to bottom instead of top, helps with OSK resizing the viewport
-    handleResize (opts = {}) {
-      const { expand = false, delayed = false } = opts
-
-      if (delayed) {
-        setTimeout(() => {
-          this.handleResize({ ...opts, delayed: false })
-        }, SAFE_RESIZE_TIME_OFFSET)
-        return
-      }
-
-      this.$nextTick(() => {
-        const { offsetHeight = undefined } = getScrollPosition()
-        const diff = this.lastScrollPosition.offsetHeight - offsetHeight
-        if (diff !== 0 || (!this.bottomedOut() && expand)) {
-          this.$nextTick(() => {
-            window.scrollTo({ top: window.scrollY + diff })
-          })
-        }
-        this.lastScrollPosition = getScrollPosition()
-      })
-    },
-    scrollDown (options = {}) {
-      const { behavior = 'auto', forceRead = false } = options
-      this.$nextTick(() => {
-        window.scrollTo({ top: document.documentElement.scrollHeight, behavior })
-      })
-      if (forceRead) {
-        this.readChat()
-      }
-    },
-    readChat () {
-      if (!(this.currentChatMessageService && this.currentChatMessageService.maxId)) { return }
-      if (document.hidden) { return }
-      const lastReadId = this.currentChatMessageService.maxId
-      this.$store.dispatch('readChat', {
-        id: this.currentChat.id,
-        lastReadId
-      })
-    },
-    bottomedOut (offset) {
-      return isBottomedOut(offset)
-    },
-    reachedTop () {
-      return window.scrollY <= 0
-    },
-    cullOlderCheck () {
-      window.setTimeout(() => {
-        if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
-          this.$store.dispatch('cullOlderMessages', this.currentChatMessageService.chatId)
-        }
-      }, 5000)
-    },
-    handleScroll: _.throttle(function () {
-      if (!this.currentChat) { return }
-
-      if (this.reachedTop()) {
-        this.fetchChat({ maxId: this.currentChatMessageService.minId })
-      } else if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
-        this.jumpToBottomButtonVisible = false
-        this.cullOlderCheck()
-        if (this.newMessageCount > 0) {
-          // Use a delay before marking as read to prevent situation where new messages
-          // arrive just as you're leaving the view and messages that you didn't actually
-          // get to see get marked as read.
-          window.setTimeout(() => {
-            // Don't mark as read if the element doesn't exist, user has left chat view
-            if (this.$el) this.readChat()
-          }, MARK_AS_READ_DELAY)
-        }
-      } else {
-        this.jumpToBottomButtonVisible = true
-      }
-    }, 200),
-    handleScrollUp (positionBeforeLoading) {
-      const positionAfterLoading = getScrollPosition()
-      window.scrollTo({
-        top: getNewTopPosition(positionBeforeLoading, positionAfterLoading)
-      })
-    },
-    fetchChat ({ isFirstFetch = false, fetchLatest = false, maxId }) {
-      const chatMessageService = this.currentChatMessageService
-      if (!chatMessageService) { return }
-      if (fetchLatest && this.streamingEnabled) { return }
-
-      const chatId = chatMessageService.chatId
-      const fetchOlderMessages = !!maxId
-      const sinceId = fetchLatest && chatMessageService.maxId
-
-      return this.backendInteractor.chatMessages({ id: chatId, maxId, sinceId })
-        .then((messages) => {
-          // Clear the current chat in case we're recovering from a ws connection loss.
-          if (isFirstFetch) {
-            chatService.clear(chatMessageService)
-          }
-
-          const positionBeforeUpdate = getScrollPosition()
-          this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => {
-            this.$nextTick(() => {
-              if (fetchOlderMessages) {
-                this.handleScrollUp(positionBeforeUpdate)
-              }
-
-              // In vertical screens, the first batch of fetched messages may not always take the
-              // full height of the scrollable container.
-              // If this is the case, we want to fetch the messages until the scrollable container
-              // is fully populated so that the user has the ability to scroll up and load the history.
-              if (!isScrollable() && messages.length > 0) {
-                this.fetchChat({ maxId: this.currentChatMessageService.minId })
-              }
-            })
-          })
-        })
-    },
-    async startFetching () {
-      let chat = this.findOpenedChatByRecipientId(this.recipientId)
-      if (!chat) {
-        try {
-          chat = await this.backendInteractor.getOrCreateChat({ accountId: this.recipientId })
-        } catch (e) {
-          console.error('Error creating or getting a chat', e)
-          this.errorLoadingChat = true
-        }
-      }
-      if (chat) {
-        this.$nextTick(() => {
-          this.scrollDown({ forceRead: true })
-        })
-        this.$store.dispatch('addOpenedChat', { chat })
-        this.doStartFetching()
-      }
-    },
-    doStartFetching () {
-      this.$store.dispatch('startFetchingCurrentChat', {
-        fetcher: () => promiseInterval(() => this.fetchChat({ fetchLatest: true }), 5000)
-      })
-      this.fetchChat({ isFirstFetch: true })
-    },
-    handleAttachmentPosting () {
-      this.$nextTick(() => {
-        this.handleResize()
-        // When the posting form size changes because of a media attachment, we need an extra resize
-        // to account for the potential delay in the DOM update.
-        this.scrollDown({ forceRead: true })
-      })
-    },
-    sendMessage ({ status, media, idempotencyKey }) {
-      const params = {
-        id: this.currentChat.id,
-        content: status,
-        idempotencyKey
-      }
-
-      if (media[0]) {
-        params.mediaId = media[0].id
-      }
-
-      const fakeMessage = buildFakeMessage({
-        attachments: media,
-        chatId: this.currentChat.id,
-        content: status,
-        userId: this.currentUser.id,
-        idempotencyKey
-      })
-
-      this.$store.dispatch('addChatMessages', {
-        chatId: this.currentChat.id,
-        messages: [fakeMessage]
-      }).then(() => {
-        this.handleAttachmentPosting()
-      })
-
-      return this.doSendMessage({ params, fakeMessage, retriesLeft: MAX_RETRIES })
-    },
-    doSendMessage ({ params, fakeMessage, retriesLeft = MAX_RETRIES }) {
-      if (retriesLeft <= 0) return
-
-      this.backendInteractor.sendChatMessage(params)
-        .then(data => {
-          this.$store.dispatch('addChatMessages', {
-            chatId: this.currentChat.id,
-            updateMaxId: false,
-            messages: [{ ...data, fakeId: fakeMessage.id }]
-          })
-
-          return data
-        })
-        .catch(error => {
-          console.error('Error sending message', error)
-          this.$store.dispatch('handleMessageError', {
-            chatId: this.currentChat.id,
-            fakeId: fakeMessage.id,
-            isRetry: retriesLeft !== MAX_RETRIES
-          })
-          if ((error.statusCode >= 500 && error.statusCode < 600) || error.message === 'Failed to fetch') {
-            this.messageRetriers[fakeMessage.id] = setTimeout(() => {
-              this.doSendMessage({ params, fakeMessage, retriesLeft: retriesLeft - 1 })
-            }, 1000 * (2 ** (MAX_RETRIES - retriesLeft)))
-          }
-          return {}
-        })
-
-      return Promise.resolve(fakeMessage)
-    },
-    goBack () {
-      this.$router.push({ name: 'chats', params: { username: this.currentUser.screen_name } })
-    }
-  }
-}
-
-export default Chat
diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss
deleted file mode 100644
index f2e154ab..00000000
--- a/src/components/chat/chat.scss
+++ /dev/null
@@ -1,108 +0,0 @@
-.chat-view {
-  display: flex;
-  height: 100%;
-
-  .chat-view-inner {
-    height: auto;
-    width: 100%;
-    overflow: visible;
-    display: flex;
-  }
-
-  .chat-view-body {
-    box-sizing: border-box;
-    background-color: var(--chatBg, $fallback--bg);
-    display: flex;
-    flex-direction: column;
-    width: 100%;
-    overflow: visible;
-    min-height: calc(100vh - var(--navbar-height));
-    margin: 0 0 0 0;
-    border-radius: 10px 10px 0 0;
-    border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0;
-
-    &::after {
-      border-radius: 0;
-    }
-  }
-
-  .message-list {
-    padding: 0 0.8em;
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    justify-content: end;
-  }
-
-  .footer {
-    position: sticky;
-    bottom: 0;
-    background-color: $fallback--bg;
-    background-color: var(--bg, $fallback--bg);
-    z-index: 1;
-  }
-
-  .chat-view-heading {
-    grid-template-columns: auto minmax(50%, 1fr);
-  }
-
-  .go-back-button {
-    text-align: center;
-    line-height: 1;
-    height: 100%;
-    align-self: start;
-    width: var(--__panel-heading-height-inner);
-  }
-
-  .jump-to-bottom-button {
-    width: 2.5em;
-    height: 2.5em;
-    border-radius: 100%;
-    position: absolute;
-    right: 1.3em;
-    top: -3.2em;
-    background-color: $fallback--fg;
-    background-color: var(--btn, $fallback--fg);
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3);
-    z-index: 10;
-    transition: 0.35s all;
-    transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
-    opacity: 0;
-    visibility: hidden;
-    cursor: pointer;
-
-    &.visible {
-      opacity: 1;
-      visibility: visible;
-    }
-
-    i {
-      font-size: 1em;
-      color: $fallback--text;
-      color: var(--text, $fallback--text);
-    }
-
-    .unread-message-count {
-      font-size: 0.8em;
-      left: 50%;
-      margin-top: -1rem;
-      padding: 0.1em;
-      border-radius: 50px;
-      position: absolute;
-    }
-
-    .chat-loading-error {
-      width: 100%;
-      display: flex;
-      align-items: flex-end;
-      height: 100%;
-
-      .error {
-        width: 100%;
-      }
-    }
-  }
-}
diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue
deleted file mode 100644
index 2e7df7bd..00000000
--- a/src/components/chat/chat.vue
+++ /dev/null
@@ -1,100 +0,0 @@
-<template>
-  <div class="chat-view">
-    <div class="chat-view-inner">
-      <div
-        ref="inner"
-        class="panel-default panel chat-view-body"
-      >
-        <div
-          ref="header"
-          class="panel-heading -sticky chat-view-heading"
-        >
-          <button
-            class="button-unstyled go-back-button"
-            @click="goBack"
-          >
-            <FAIcon
-              size="lg"
-              icon="chevron-left"
-            />
-          </button>
-          <div class="title text-center">
-            <ChatTitle
-              :user="recipient"
-              :with-avatar="true"
-            />
-          </div>
-        </div>
-        <div
-          class="message-list"
-          :style="{ height: scrollableContainerHeight }"
-        >
-          <template v-if="!errorLoadingChat">
-            <ChatMessage
-              v-for="chatViewItem in chatViewItems"
-              :key="chatViewItem.id"
-              :author="recipient"
-              :chat-view-item="chatViewItem"
-              :hovered-message-chain="chatViewItem.messageChainId === hoveredMessageChainId"
-              @hover="onMessageHover"
-            />
-          </template>
-          <div
-            v-else
-            class="chat-loading-error"
-          >
-            <div class="alert error">
-              {{ $t('chats.error_loading_chat') }}
-            </div>
-          </div>
-        </div>
-        <div
-          ref="footer"
-          class="panel-body footer"
-        >
-          <div
-            class="jump-to-bottom-button"
-            :class="{ 'visible': jumpToBottomButtonVisible }"
-            @click="scrollDown({ behavior: 'smooth' })"
-          >
-            <span>
-              <FAIcon icon="chevron-down" />
-              <div
-                v-if="newMessageCount"
-                class="badge badge-notification unread-chat-count unread-message-count"
-              >
-                {{ newMessageCount }}
-              </div>
-            </span>
-          </div>
-          <PostStatusForm
-            :disable-subject="true"
-            :disable-scope-selector="true"
-            :disable-notice="true"
-            :disable-lock-warning="true"
-            :disable-polls="true"
-            :disable-sensitivity-checkbox="true"
-            :disable-submit="errorLoadingChat || !currentChat"
-            :disable-preview="true"
-            :optimistic-posting="true"
-            :post-handler="sendMessage"
-            :submit-on-enter="!mobileLayout"
-            :preserve-focus="!mobileLayout"
-            :auto-focus="!mobileLayout"
-            :placeholder="formPlaceholder"
-            :file-limit="1"
-            max-height="160"
-            emoji-picker-placement="top"
-            @resize="handleResize"
-          />
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script src="./chat.js"></script>
-<style lang="scss">
-@import '../../_variables.scss';
-@import './chat.scss';
-</style>
diff --git a/src/components/chat/chat_layout_utils.js b/src/components/chat/chat_layout_utils.js
deleted file mode 100644
index c187892d..00000000
--- a/src/components/chat/chat_layout_utils.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// Captures a scroll position
-export const getScrollPosition = () => {
-  return {
-    scrollTop: window.scrollY,
-    scrollHeight: document.documentElement.scrollHeight,
-    offsetHeight: window.innerHeight
-  }
-}
-
-// A helper function that is used to keep the scroll position fixed as the new elements are added to the top
-// Takes two scroll positions, before and after the update.
-export const getNewTopPosition = (previousPosition, newPosition) => {
-  return previousPosition.scrollTop + (newPosition.scrollHeight - previousPosition.scrollHeight)
-}
-
-export const isBottomedOut = (offset = 0) => {
-  const scrollHeight = window.scrollY + offset
-  const totalHeight = document.documentElement.scrollHeight - window.innerHeight
-  return totalHeight <= scrollHeight
-}
-// Returns whether or not the scrollbar is visible.
-export const isScrollable = () => {
-  return document.documentElement.scrollHeight > window.innerHeight
-}
diff --git a/src/components/chat_list/chat_list.js b/src/components/chat_list/chat_list.js
deleted file mode 100644
index 95708d1d..00000000
--- a/src/components/chat_list/chat_list.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { mapState, mapGetters } from 'vuex'
-import ChatListItem from '../chat_list_item/chat_list_item.vue'
-import ChatNew from '../chat_new/chat_new.vue'
-import List from '../list/list.vue'
-
-const ChatList = {
-  components: {
-    ChatListItem,
-    List,
-    ChatNew
-  },
-  computed: {
-    ...mapState({
-      currentUser: state => state.users.currentUser
-    }),
-    ...mapGetters(['sortedChatList'])
-  },
-  data () {
-    return {
-      isNew: false
-    }
-  },
-  created () {
-    this.$store.dispatch('fetchChats', { latest: true })
-  },
-  methods: {
-    cancelNewChat () {
-      this.isNew = false
-      this.$store.dispatch('fetchChats', { latest: true })
-    },
-    newChat () {
-      this.isNew = true
-    }
-  }
-}
-
-export default ChatList
diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue
deleted file mode 100644
index 58e8d0b3..00000000
--- a/src/components/chat_list/chat_list.vue
+++ /dev/null
@@ -1,64 +0,0 @@
-<template>
-  <div v-if="isNew">
-    <ChatNew @cancel="cancelNewChat" />
-  </div>
-  <div
-    v-else
-    class="chat-list panel panel-default"
-  >
-    <div class="panel-heading -sticky">
-      <span class="title">
-        {{ $t("chats.chats") }}
-      </span>
-      <button
-        class="button-default"
-        @click="newChat"
-      >
-        {{ $t("chats.new") }}
-      </button>
-    </div>
-    <div class="panel-body">
-      <div
-        v-if="sortedChatList.length > 0"
-        class="timeline"
-      >
-        <List :items="sortedChatList">
-          <template v-slot:item="{item}">
-            <ChatListItem
-              :key="item.id"
-              :compact="false"
-              :chat="item"
-            />
-          </template>
-        </List>
-      </div>
-      <div
-        v-else
-        class="emtpy-chat-list-alert"
-      >
-        <span>{{ $t('chats.empty_chat_list_placeholder') }}</span>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script src="./chat_list.js"></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.chat-list {
-  min-height: 25em;
-  margin-bottom: 0;
-}
-
-.emtpy-chat-list-alert {
-  padding: 3em;
-  font-size: 1.2em;
-  display: flex;
-  justify-content: center;
-  color: $fallback--text;
-  color: var(--faint, $fallback--text);
-}
-
-</style>
diff --git a/src/components/chat_list_item/chat_list_item.js b/src/components/chat_list_item/chat_list_item.js
deleted file mode 100644
index e5032176..00000000
--- a/src/components/chat_list_item/chat_list_item.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { mapState } from 'vuex'
-import StatusBody from '../status_content/status_content.vue'
-import fileType from 'src/services/file_type/file_type.service'
-import UserAvatar from '../user_avatar/user_avatar.vue'
-import AvatarList from '../avatar_list/avatar_list.vue'
-import Timeago from '../timeago/timeago.vue'
-import ChatTitle from '../chat_title/chat_title.vue'
-
-const ChatListItem = {
-  name: 'ChatListItem',
-  props: [
-    'chat'
-  ],
-  components: {
-    UserAvatar,
-    AvatarList,
-    Timeago,
-    ChatTitle,
-    StatusBody
-  },
-  computed: {
-    ...mapState({
-      currentUser: state => state.users.currentUser
-    }),
-    attachmentInfo () {
-      if (this.chat.lastMessage.attachments.length === 0) { return }
-
-      const types = this.chat.lastMessage.attachments.map(file => fileType.fileType(file.mimetype))
-      if (types.includes('video')) {
-        return this.$t('file_type.video')
-      } else if (types.includes('audio')) {
-        return this.$t('file_type.audio')
-      } else if (types.includes('image')) {
-        return this.$t('file_type.image')
-      } else {
-        return this.$t('file_type.file')
-      }
-    },
-    messageForStatusContent () {
-      const message = this.chat.lastMessage
-      const messageEmojis = message ? message.emojis : []
-      const isYou = message && message.account_id === this.currentUser.id
-      const content = message ? (this.attachmentInfo || message.content) : ''
-      const messagePreview = isYou ? `<i>${this.$t('chats.you')}</i> ${content}` : content
-      return {
-        summary: '',
-        emojis: messageEmojis,
-        raw_html: messagePreview,
-        text: messagePreview,
-        attachments: []
-      }
-    }
-  },
-  methods: {
-    openChat (_e) {
-      if (this.chat.id) {
-        this.$router.push({
-          name: 'chat',
-          params: {
-            username: this.currentUser.screen_name,
-            recipient_id: this.chat.account.id
-          }
-        })
-      }
-    }
-  }
-}
-
-export default ChatListItem
diff --git a/src/components/chat_list_item/chat_list_item.scss b/src/components/chat_list_item/chat_list_item.scss
deleted file mode 100644
index c6b45c34..00000000
--- a/src/components/chat_list_item/chat_list_item.scss
+++ /dev/null
@@ -1,91 +0,0 @@
-.chat-list-item {
-  display: flex;
-  flex-direction: row;
-  padding: 0.75em;
-  height: 5em;
-  overflow: hidden;
-  box-sizing: border-box;
-  cursor: pointer;
-
-  :focus {
-    outline: none;
-  }
-
-  &:hover {
-    background-color: var(--selectedPost, $fallback--lightBg);
-    box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.1);
-  }
-
-  .chat-list-item-left {
-    margin-right: 1em;
-  }
-
-  .chat-list-item-center {
-    width: 100%;
-    box-sizing: border-box;
-    overflow: hidden;
-    word-wrap: break-word;
-  }
-
-  .heading {
-    width: 100%;
-    display: inline-flex;
-    justify-content: space-between;
-    line-height: 1em;
-  }
-
-  .heading-right {
-    white-space: nowrap;
-  }
-
-  .name-and-account-name {
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    overflow: hidden;
-    flex-shrink: 1;
-    line-height: var(--post-line-height);
-  }
-
-  .chat-preview {
-    display: inline-flex;
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-    margin: 0.35em 0;
-    color: $fallback--text;
-    color: var(--faint, $fallback--text);
-    width: 100%;
-  }
-
-  a {
-    color: var(--faintLink, $fallback--link);
-    text-decoration: none;
-    pointer-events: none;
-  }
-
-  &:hover .animated.avatar {
-    canvas {
-      display: none;
-    }
-    img {
-      visibility: visible;
-    }
-  }
-
-  .Avatar {
-    border-radius: $fallback--avatarAltRadius;
-    border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
-  }
-
-  .chat-preview-body {
-    --emoji-size: 1.4em;
-  }
-
-  .time-wrapper {
-    line-height: var(--post-line-height);
-  }
-
-  .chat-preview-body {
-    padding-right: 1em;
-  }
-}
diff --git a/src/components/chat_list_item/chat_list_item.vue b/src/components/chat_list_item/chat_list_item.vue
deleted file mode 100644
index c7c0e878..00000000
--- a/src/components/chat_list_item/chat_list_item.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<template>
-  <div
-    class="chat-list-item"
-    @click.capture.prevent="openChat"
-  >
-    <div class="chat-list-item-left">
-      <UserAvatar
-        :user="chat.account"
-        height="48px"
-        width="48px"
-      />
-    </div>
-    <div class="chat-list-item-center">
-      <div class="heading">
-        <span
-          v-if="chat.account"
-          class="name-and-account-name"
-        >
-          <ChatTitle
-            :user="chat.account"
-          />
-        </span>
-        <span class="heading-right" />
-        <div class="time-wrapper">
-          <Timeago
-            :time="chat.updated_at"
-            :auto-update="60"
-          />
-        </div>
-      </div>
-      <div class="chat-preview">
-        <StatusBody
-          class="chat-preview-body"
-          :status="messageForStatusContent"
-          :single-line="true"
-        />
-        <div
-          v-if="chat.unread > 0"
-          class="badge badge-notification unread-chat-count"
-        >
-          {{ chat.unread }}
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script src="./chat_list_item.js"></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-@import './chat_list_item.scss';
-</style>
diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js
deleted file mode 100644
index 5bac7736..00000000
--- a/src/components/chat_message/chat_message.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import { mapState, mapGetters } from 'vuex'
-import Popover from '../popover/popover.vue'
-import Attachment from '../attachment/attachment.vue'
-import UserAvatar from '../user_avatar/user_avatar.vue'
-import Gallery from '../gallery/gallery.vue'
-import LinkPreview from '../link-preview/link-preview.vue'
-import StatusContent from '../status_content/status_content.vue'
-import ChatMessageDate from '../chat_message_date/chat_message_date.vue'
-import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
-  faTimes,
-  faEllipsisH
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
-  faTimes,
-  faEllipsisH
-)
-
-const ChatMessage = {
-  name: 'ChatMessage',
-  props: [
-    'author',
-    'edited',
-    'noHeading',
-    'chatViewItem',
-    'hoveredMessageChain'
-  ],
-  emits: ['hover'],
-  components: {
-    Popover,
-    Attachment,
-    StatusContent,
-    UserAvatar,
-    Gallery,
-    LinkPreview,
-    ChatMessageDate
-  },
-  computed: {
-    // Returns HH:MM (hours and minutes) in local time.
-    createdAt () {
-      const time = this.chatViewItem.data.created_at
-      return time.toLocaleTimeString('en', { hour: '2-digit', minute: '2-digit', hour12: false })
-    },
-    isCurrentUser () {
-      return this.message.account_id === this.currentUser.id
-    },
-    message () {
-      return this.chatViewItem.data
-    },
-    userProfileLink () {
-      return generateProfileLink(this.author.id, this.author.screen_name, this.$store.state.instance.restrictedNicknames)
-    },
-    isMessage () {
-      return this.chatViewItem.type === 'message'
-    },
-    messageForStatusContent () {
-      return {
-        summary: '',
-        emojis: this.message.emojis,
-        raw_html: this.message.content || '',
-        text: this.message.content || '',
-        attachments: this.message.attachments
-      }
-    },
-    hasAttachment () {
-      return this.message.attachments.length > 0
-    },
-    ...mapState({
-      betterShadow: state => state.interface.browserSupport.cssFilter,
-      currentUser: state => state.users.currentUser,
-      restrictedNicknames: state => state.instance.restrictedNicknames
-    }),
-    popoverMarginStyle () {
-      if (this.isCurrentUser) {
-        return {}
-      } else {
-        return { left: 50 }
-      }
-    },
-    ...mapGetters(['mergedConfig', 'findUser'])
-  },
-  data () {
-    return {
-      hovered: false,
-      menuOpened: false
-    }
-  },
-  methods: {
-    onHover (bool) {
-      this.$emit('hover', { isHovered: bool, messageChainId: this.chatViewItem.messageChainId })
-    },
-    async deleteMessage () {
-      const confirmed = window.confirm(this.$t('chats.delete_confirm'))
-      if (confirmed) {
-        await this.$store.dispatch('deleteChatMessage', {
-          messageId: this.chatViewItem.data.id,
-          chatId: this.chatViewItem.data.chat_id
-        })
-      }
-      this.hovered = false
-      this.menuOpened = false
-    }
-  }
-}
-
-export default ChatMessage
diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss
deleted file mode 100644
index 1913479f..00000000
--- a/src/components/chat_message/chat_message.scss
+++ /dev/null
@@ -1,179 +0,0 @@
-@import '../../_variables.scss';
-
-.chat-message-wrapper {
-
-  &.hovered-message-chain {
-    .animated.Avatar {
-      canvas {
-        display: none;
-      }
-      img {
-        visibility: visible;
-      }
-    }
-  }
-
-  .chat-message-menu {
-    transition: opacity 0.1s;
-    opacity: 0;
-    position: absolute;
-    top: -0.8em;
-
-    button {
-      padding-top: 0.2em;
-      padding-bottom: 0.2em;
-    }
-  }
-
-  .menu-icon {
-    cursor: pointer;
-
-    &:hover, .extra-button-popover.open & {
-      color: $fallback--text;
-      color: var(--text, $fallback--text);
-    }
-  }
-
-  .popover {
-    width: 12em;
-  }
-
-  .chat-message {
-    display: flex;
-    padding-bottom: 0.5em;
-
-    .status-body:hover {
-      --_still-image-img-visibility: visible;
-      --_still-image-canvas-visibility: hidden;
-      --_still-image-label-visibility: hidden;
-    }
-  }
-
-  .avatar-wrapper {
-    margin-right: 0.72em;
-    width: 32px;
-  }
-
-  .link-preview, .attachments {
-    margin-bottom: 1em;
-  }
-
-  .chat-message-inner {
-    display: flex;
-    flex-direction: column;
-    align-items: flex-start;
-    max-width: 80%;
-    min-width: 10em;
-    width: 100%;
-
-    &.with-media {
-      width: 100%;
-
-      .status {
-        width: 100%;
-      }
-    }
-  }
-
-  .status {
-    border-radius: $fallback--chatMessageRadius;
-    border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius);
-    display: flex;
-    padding: 0.75em;
-  }
-
-  .created-at {
-    position: relative;
-    float: right;
-    font-size: 0.8em;
-    margin: -1em 0 -0.5em 0;
-    font-style: italic;
-    opacity: 0.8;
-  }
-
-  .without-attachment {
-    .message-content {
-      // TODO figure out how to do it properly
-      .RichContent::after {
-        margin-right: 5.4em;
-        content: " ";
-        display: inline-block;
-      }
-    }
-  }
-
-  .pending {
-    .status-content.media-body, .created-at {
-      color: var(--faint);
-    }
-  }
-
-  .error {
-    .status-content.media-body, .created-at {
-      color: $fallback--cRed;
-      color: var(--badgeNotification, $fallback--cRed);
-    }
-  }
-
-  .incoming {
-    a {
-      color: var(--chatMessageIncomingLink, $fallback--link);
-    }
-
-    .status {
-      color: var(--chatMessageIncomingText, $fallback--text);
-      background-color: var(--chatMessageIncomingBg, $fallback--bg);
-      border: 1px solid var(--chatMessageIncomingBorder, --border);
-    }
-
-    .created-at {
-      a {
-        color: var(--chatMessageIncomingText, $fallback--text);
-      }
-    }
-
-    .chat-message-menu {
-      left: 0.4rem;
-    }
-  }
-
-  .outgoing {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    align-content: end;
-    justify-content: flex-end;
-
-    a {
-      color: var(--chatMessageOutgoingLink, $fallback--link);
-    }
-
-    .status {
-      color: var(--chatMessageOutgoingText, $fallback--text);
-      background-color: var(--chatMessageOutgoingBg, $fallback--lightBg);
-      border: 1px solid var(--chatMessageOutgoingBorder, --lightBg);
-    }
-
-    .chat-message-inner {
-      align-items: flex-end;
-    }
-
-    .chat-message-menu {
-      right: 0.4rem;
-    }
-  }
-
-  .visible {
-    opacity: 1;
-  }
-
-}
-
-.chat-message-date-separator {
-  text-align: center;
-  margin: 1.4em 0;
-  font-size: 0.9em;
-  user-select: none;
-  color: $fallback--text;
-  color: var(--faintedText, $fallback--text);
-}
diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue
deleted file mode 100644
index d62b831d..00000000
--- a/src/components/chat_message/chat_message.vue
+++ /dev/null
@@ -1,103 +0,0 @@
-<template>
-  <div
-    v-if="isMessage"
-    class="chat-message-wrapper"
-    :class="{ 'hovered-message-chain': hoveredMessageChain }"
-    @mouseover="onHover(true)"
-    @mouseleave="onHover(false)"
-  >
-    <div
-      class="chat-message"
-      :class="[{ 'outgoing': isCurrentUser, 'incoming': !isCurrentUser }]"
-    >
-      <div
-        v-if="!isCurrentUser"
-        class="avatar-wrapper"
-      >
-        <router-link
-          v-if="chatViewItem.isHead"
-          :to="userProfileLink"
-        >
-          <UserAvatar
-            :compact="true"
-            :better-shadow="betterShadow"
-            :user="author"
-          />
-        </router-link>
-      </div>
-      <div class="chat-message-inner">
-        <div
-          class="status-body"
-          :style="{ 'min-width': message.attachment ? '80%' : '' }"
-        >
-          <div
-            class="media status"
-            :class="{ 'without-attachment': !hasAttachment, 'pending': chatViewItem.data.pending, 'error': chatViewItem.data.error }"
-            style="position: relative"
-            @mouseenter="hovered = true"
-            @mouseleave="hovered = false"
-          >
-            <div
-              class="chat-message-menu"
-              :class="{ 'visible': hovered || menuOpened }"
-            >
-              <Popover
-                trigger="click"
-                placement="top"
-                :bound-to-selector="isCurrentUser ? '' : '.scrollable-message-list'"
-                :bound-to="{ x: 'container' }"
-                :margin="popoverMarginStyle"
-                @show="menuOpened = true"
-                @close="menuOpened = false"
-              >
-                <template v-slot:content>
-                  <div class="dropdown-menu">
-                    <button
-                      class="button-default dropdown-item dropdown-item-icon"
-                      @click="deleteMessage"
-                    >
-                      <FAIcon icon="times" /> {{ $t("chats.delete") }}
-                    </button>
-                  </div>
-                </template>
-                <template v-slot:trigger>
-                  <button
-                    class="button-default menu-icon"
-                    :title="$t('chats.more')"
-                  >
-                    <FAIcon icon="ellipsis-h" />
-                  </button>
-                </template>
-              </Popover>
-            </div>
-            <StatusContent
-              class="message-content"
-              :status="messageForStatusContent"
-              :full-content="true"
-            >
-              <template v-slot:footer>
-                <span
-                  class="created-at"
-                >
-                  {{ createdAt }}
-                </span>
-              </template>
-            </StatusContent>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-  <div
-    v-else
-    class="chat-message-date-separator"
-  >
-    <ChatMessageDate :date="chatViewItem.date" />
-  </div>
-</template>
-
-<script src="./chat_message.js" ></script>
-<style lang="scss">
-@import './chat_message.scss';
-
-</style>
diff --git a/src/components/chat_new/chat_new.js b/src/components/chat_new/chat_new.js
deleted file mode 100644
index 71585995..00000000
--- a/src/components/chat_new/chat_new.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import { mapState, mapGetters } from 'vuex'
-import BasicUserCard from '../basic_user_card/basic_user_card.vue'
-import UserAvatar from '../user_avatar/user_avatar.vue'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
-  faSearch,
-  faChevronLeft
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
-  faSearch,
-  faChevronLeft
-)
-
-const chatNew = {
-  components: {
-    BasicUserCard,
-    UserAvatar
-  },
-  data () {
-    return {
-      suggestions: [],
-      userIds: [],
-      loading: false,
-      query: ''
-    }
-  },
-  async created () {
-    const { chats } = await this.backendInteractor.chats()
-    chats.forEach(chat => this.suggestions.push(chat.account))
-  },
-  computed: {
-    users () {
-      return this.userIds.map(userId => this.findUser(userId))
-    },
-    availableUsers () {
-      if (this.query.length !== 0) {
-        return this.users
-      } else {
-        return this.suggestions
-      }
-    },
-    ...mapState({
-      currentUser: state => state.users.currentUser,
-      backendInteractor: state => state.api.backendInteractor
-    }),
-    ...mapGetters(['findUser'])
-  },
-  methods: {
-    goBack () {
-      this.$emit('cancel')
-    },
-    goToChat (user) {
-      this.$router.push({ name: 'chat', params: { recipient_id: user.id } })
-    },
-    onInput () {
-      this.search(this.query)
-    },
-    addUser (user) {
-      this.selectedUserIds.push(user.id)
-      this.query = ''
-    },
-    removeUser (userId) {
-      this.selectedUserIds = this.selectedUserIds.filter(id => id !== userId)
-    },
-    search (query) {
-      if (!query) {
-        this.loading = false
-        return
-      }
-
-      this.loading = true
-      this.userIds = []
-      this.$store.dispatch('search', { q: query, resolve: true, type: 'accounts' })
-        .then(data => {
-          this.loading = false
-          this.userIds = data.accounts.map(a => a.id)
-        })
-    }
-  }
-}
-
-export default chatNew
diff --git a/src/components/chat_new/chat_new.scss b/src/components/chat_new/chat_new.scss
deleted file mode 100644
index 240e1a38..00000000
--- a/src/components/chat_new/chat_new.scss
+++ /dev/null
@@ -1,31 +0,0 @@
-.chat-new {
-  .input-wrap {
-    display: flex;
-    margin: 0.7em 0.5em 0.7em 0.5em;
-
-    input {
-      width: 100%;
-    }
-  }
-
-  .search-icon {
-    margin-right: 0.3em;
-  }
-
-  .member-list {
-    padding-bottom: 0.7rem;
-  }
-
-  .basic-user-card:hover {
-    cursor: pointer;
-    background-color: var(--selectedPost, $fallback--lightBg);
-  }
-
-  .go-back-button {
-    text-align: center;
-    line-height: 1;
-    height: 100%;
-    align-self: start;
-    width: var(--__panel-heading-height-inner);
-  }
-}
diff --git a/src/components/chat_new/chat_new.vue b/src/components/chat_new/chat_new.vue
deleted file mode 100644
index bf09a379..00000000
--- a/src/components/chat_new/chat_new.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-<template>
-  <div
-    class="panel-default panel chat-new"
-  >
-    <div
-      ref="header"
-      class="panel-heading"
-    >
-      <button
-        class="button-unstyled go-back-button"
-        @click="goBack"
-      >
-        <FAIcon
-          size="lg"
-          icon="chevron-left"
-        />
-      </button>
-    </div>
-    <div class="input-wrap">
-      <div class="input-search">
-        <FAIcon
-          class="search-icon fa-scale-110 fa-old-padding"
-          icon="search"
-        />
-      </div>
-      <input
-        ref="search"
-        v-model="query"
-        placeholder="Search people"
-        @input="onInput"
-      >
-    </div>
-    <div class="member-list">
-      <div
-        v-for="user in availableUsers"
-        :key="user.id"
-        class="member"
-      >
-        <div @click.capture.prevent="goToChat(user)">
-          <BasicUserCard :user="user" />
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script src="./chat_new.js"></script>
-<style lang="scss">
-@import '../../_variables.scss';
-@import './chat_new.scss';
-</style>
diff --git a/src/components/chat_title/chat_title.js b/src/components/chat_title/chat_title.js
deleted file mode 100644
index f6e299ad..00000000
--- a/src/components/chat_title/chat_title.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
-import UserAvatar from '../user_avatar/user_avatar.vue'
-import RichContent from 'src/components/rich_content/rich_content.jsx'
-
-export default {
-  name: 'ChatTitle',
-  components: {
-    UserAvatar,
-    RichContent
-  },
-  props: [
-    'user', 'withAvatar'
-  ],
-  computed: {
-    title () {
-      return this.user ? this.user.screen_name_ui : ''
-    },
-    htmlTitle () {
-      return this.user ? this.user.name_html : ''
-    }
-  },
-  methods: {
-    getUserProfileLink (user) {
-      return generateProfileLink(user.id, user.screen_name)
-    }
-  }
-}
diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue
deleted file mode 100644
index 1fafbf9a..00000000
--- a/src/components/chat_title/chat_title.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<template>
-  <div
-    class="chat-title"
-    :title="title"
-  >
-    <router-link
-      v-if="withAvatar && user"
-      class="avatar-container"
-      :to="getUserProfileLink(user)"
-    >
-      <UserAvatar
-        class="titlebar-avatar"
-        :user="user"
-      />
-    </router-link>
-    <RichContent
-      v-if="user"
-      class="username"
-      :title="'@'+(user && user.screen_name_ui)"
-      :html="htmlTitle"
-      :emoji="user.emoji || []"
-    />
-  </div>
-</template>
-
-<script src="./chat_title.js"></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.chat-title {
-  display: flex;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-
-  --emoji-size: 14px;
-
-  .username {
-    max-width: 100%;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    display: inline;
-    word-wrap: break-word;
-    overflow: hidden;
-  }
-
-  .avatar-container {
-    align-self: center;
-    line-height: 1;
-  }
-
-  .titlebar-avatar {
-    margin-right: 0.5em;
-    height: 1.5em;
-    width: 1.5em;
-    border-radius: $fallback--avatarAltRadius;
-    border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
-
-    &.animated::before {
-      display: none;
-    }
-  }
-}
-</style>
diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js
index d177efeb..705d0502 100644
--- a/src/components/features_panel/features_panel.js
+++ b/src/components/features_panel/features_panel.js
@@ -2,9 +2,6 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for
 
 const FeaturesPanel = {
   computed: {
-    shout: function () { return this.$store.state.instance.shoutAvailable },
-    pleromaChatMessages: function () { return this.$store.state.instance.pleromaChatMessagesAvailable },
-    gopher: function () { return this.$store.state.instance.gopherAvailable },
     whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
     mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },
     minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode },
diff --git a/src/components/features_panel/features_panel.vue b/src/components/features_panel/features_panel.vue
index a58a99af..0dc5ac08 100644
--- a/src/components/features_panel/features_panel.vue
+++ b/src/components/features_panel/features_panel.vue
@@ -8,15 +8,6 @@
       </div>
       <div class="panel-body features-panel">
         <ul>
-          <li v-if="shout">
-            {{ $t('features_panel.shout') }}
-          </li>
-          <li v-if="pleromaChatMessages">
-            {{ $t('features_panel.pleroma_chat_messages') }}
-          </li>
-          <li v-if="gopher">
-            {{ $t('features_panel.gopher') }}
-          </li>
           <li v-if="whoToFollow">
             {{ $t('features_panel.who_to_follow') }}
           </li>
diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js
index 6efffd13..713fd37b 100644
--- a/src/components/mobile_nav/mobile_nav.js
+++ b/src/components/mobile_nav/mobile_nav.js
@@ -2,7 +2,6 @@ import SideDrawer from '../side_drawer/side_drawer.vue'
 import Notifications from '../notifications/notifications.vue'
 import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
 import GestureService from '../../services/gesture_service/gesture_service'
-import { mapGetters } from 'vuex'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
   faTimes,
@@ -43,11 +42,7 @@ const MobileNav = {
       return this.unseenNotifications.length
     },
     hideSitename () { return this.$store.state.instance.hideSitename },
-    sitename () { return this.$store.state.instance.name },
-    isChat () {
-      return this.$route.name === 'chat'
-    },
-    ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
+    sitename () { return this.$store.state.instance.name }
   },
   methods: {
     toggleMobileSidebar () {
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index 50e71ffb..3583ab3c 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -17,7 +17,7 @@
             icon="bars"
           />
           <div
-            v-if="unreadChatCount || unreadAnnouncementCount"
+            v-if="unreadAnnouncementCount"
             class="alert-dot"
           />
         </button>
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 ecf79b64..d08e4d65 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
@@ -8,11 +8,6 @@ library.add(
   faPen
 )
 
-const HIDDEN_FOR_PAGES = new Set([
-  'chats',
-  'chat'
-])
-
 const MobilePostStatusButton = {
   data () {
     return {
@@ -40,8 +35,6 @@ const MobilePostStatusButton = {
       return !!this.$store.state.users.currentUser
     },
     isHidden () {
-      if (HIDDEN_FOR_PAGES.has(this.$route.name)) { return true }
-
       return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
     },
     isPersistent () {
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 1f188174..18c7cded 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -56,10 +56,9 @@ const NavPanel = {
       currentUser: state => state.users.currentUser,
       followRequestCount: state => state.api.followRequests.length,
       privateMode: state => state.instance.private,
-      federating: state => state.instance.federating,
-      pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
+      federating: state => state.instance.federating
     }),
-    ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
+    ...mapGetters(['unreadAnnouncementCount'])
   }
 }
 
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 15f8b227..1a893348 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -49,24 +49,6 @@
             />{{ $t("nav.interactions") }}
           </router-link>
         </li>
-        <li v-if="currentUser && pleromaChatMessagesAvailable">
-          <router-link
-            class="menu-item"
-            :to="{ name: 'chats', params: { username: currentUser.screen_name } }"
-          >
-            <div
-              v-if="unreadChatCount"
-              class="badge badge-notification"
-            >
-              {{ unreadChatCount }}
-            </div>
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              icon="comments"
-            />{{ $t("nav.chats") }}
-          </router-link>
-        </li>
         <li v-if="currentUser && currentUser.locked">
           <router-link
             class="menu-item"
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 5f2a1251..21f90b48 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -60,7 +60,7 @@ const Notifications = {
       return this.unseenNotifications.length
     },
     unseenCountTitle () {
-      return this.unseenCount + (this.unreadChatCount) + this.unreadAnnouncementCount
+      return this.unseenCount + this.unreadAnnouncementCount
     },
     loading () {
       return this.$store.state.statuses.notifications.loading
@@ -80,7 +80,7 @@ const Notifications = {
     notificationsToDisplay () {
       return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
     },
-    ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
+    ...mapGetters(['unreadAnnouncementCount'])
   },
   watch: {
     unseenCountTitle (count) {
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 1e11b9e0..981da55b 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -76,7 +76,6 @@ const GeneralTab = {
       return this.$store.state.instance.background &&
         !this.$store.state.users.currentUser.background_image
     },
-    instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
     language: {
       get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
       set: function (val) {
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index f2d332ce..1db25752 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -120,14 +120,6 @@
             {{ $t('settings.autohide_floating_post_button') }}
           </BooleanSetting>
         </li>
-        <li v-if="instanceShoutboxPresent">
-          <BooleanSetting
-            path="hideShoutbox"
-            expert="1"
-          >
-            {{ $t('settings.hide_shoutbox') }}
-          </BooleanSetting>
-        </li>
       </ul>
     </div>
     <div class="setting-item">
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js
index 969984c1..5b50edf5 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js
@@ -115,8 +115,7 @@ export default {
       avatarRadiusLocal: '',
       avatarAltRadiusLocal: '',
       attachmentRadiusLocal: '',
-      tooltipRadiusLocal: '',
-      chatMessageRadiusLocal: ''
+      tooltipRadiusLocal: ''
     }
   },
   created () {
@@ -231,8 +230,7 @@ export default {
         avatar: this.avatarRadiusLocal,
         avatarAlt: this.avatarAltRadiusLocal,
         tooltip: this.tooltipRadiusLocal,
-        attachment: this.attachmentRadiusLocal,
-        chatMessage: this.chatMessageRadiusLocal
+        attachment: this.attachmentRadiusLocal
       }
     },
     preview () {
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
index ff2fece9..8dc64901 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
@@ -748,65 +748,6 @@
             />
             <ContrastRatio :contrast="previewContrast.selectedMenuLink" />
           </div>
-          <div class="color-item">
-            <h4>{{ $t('chats.chats') }}</h4>
-            <ColorInput
-              v-model="chatBgColorLocal"
-              name="chatBgColor"
-              :fallback="previewTheme.colors.bg"
-              :label="$t('settings.background')"
-            />
-            <h5>{{ $t('settings.style.advanced_colors.chat.incoming') }}</h5>
-            <ColorInput
-              v-model="chatMessageIncomingBgColorLocal"
-              name="chatMessageIncomingBgColor"
-              :fallback="previewTheme.colors.bg"
-              :label="$t('settings.background')"
-            />
-            <ColorInput
-              v-model="chatMessageIncomingTextColorLocal"
-              name="chatMessageIncomingTextColor"
-              :fallback="previewTheme.colors.text"
-              :label="$t('settings.text')"
-            />
-            <ColorInput
-              v-model="chatMessageIncomingLinkColorLocal"
-              name="chatMessageIncomingLinkColor"
-              :fallback="previewTheme.colors.link"
-              :label="$t('settings.links')"
-            />
-            <ColorInput
-              v-model="chatMessageIncomingBorderColorLocal"
-              name="chatMessageIncomingBorderLinkColor"
-              :fallback="previewTheme.colors.fg"
-              :label="$t('settings.style.advanced_colors.chat.border')"
-            />
-            <h5>{{ $t('settings.style.advanced_colors.chat.outgoing') }}</h5>
-            <ColorInput
-              v-model="chatMessageOutgoingBgColorLocal"
-              name="chatMessageOutgoingBgColor"
-              :fallback="previewTheme.colors.bg"
-              :label="$t('settings.background')"
-            />
-            <ColorInput
-              v-model="chatMessageOutgoingTextColorLocal"
-              name="chatMessageOutgoingTextColor"
-              :fallback="previewTheme.colors.text"
-              :label="$t('settings.text')"
-            />
-            <ColorInput
-              v-model="chatMessageOutgoingLinkColorLocal"
-              name="chatMessageOutgoingLinkColor"
-              :fallback="previewTheme.colors.link"
-              :label="$t('settings.links')"
-            />
-            <ColorInput
-              v-model="chatMessageOutgoingBorderColorLocal"
-              name="chatMessageOutgoingBorderLinkColor"
-              :fallback="previewTheme.colors.bg"
-              :label="$t('settings.style.advanced_colors.chat.border')"
-            />
-          </div>
         </div>
 
         <div
@@ -886,14 +827,6 @@
             max="50"
             hard-min="0"
           />
-          <RangeInput
-            v-model="chatMessageRadiusLocal"
-            name="chatMessageRadius"
-            :label="$t('settings.chatMessageRadius')"
-            :fallback="previewTheme.radii.chatMessage || 2"
-            max="50"
-            hard-min="0"
-          />
         </div>
 
         <div
diff --git a/src/components/shout_panel/shout_panel.js b/src/components/shout_panel/shout_panel.js
deleted file mode 100644
index a6168971..00000000
--- a/src/components/shout_panel/shout_panel.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
-  faBullhorn,
-  faTimes
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
-  faBullhorn,
-  faTimes
-)
-
-const shoutPanel = {
-  props: [ 'floating' ],
-  data () {
-    return {
-      currentMessage: '',
-      channel: null,
-      collapsed: true
-    }
-  },
-  computed: {
-    messages () {
-      return this.$store.state.shout.messages
-    }
-  },
-  methods: {
-    submit (message) {
-      this.$store.state.shout.channel.push('new_msg', { text: message }, 10000)
-      this.currentMessage = ''
-    },
-    togglePanel () {
-      this.collapsed = !this.collapsed
-    },
-    userProfileLink (user) {
-      return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames)
-    }
-  },
-  watch: {
-    messages (newVal) {
-      const scrollEl = this.$el.querySelector('.chat-window')
-      if (!scrollEl) return
-      if (scrollEl.scrollTop + scrollEl.offsetHeight + 20 > scrollEl.scrollHeight) {
-        this.$nextTick(() => {
-          if (!scrollEl) return
-          scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.offsetHeight
-        })
-      }
-    }
-  }
-}
-
-export default shoutPanel
diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue
deleted file mode 100644
index 1eca88a7..00000000
--- a/src/components/shout_panel/shout_panel.vue
+++ /dev/null
@@ -1,156 +0,0 @@
-<template>
-  <div
-    v-if="!collapsed || !floating"
-    class="shout-panel"
-  >
-    <div class="panel panel-default">
-      <div
-        class="panel-heading timeline-heading"
-        :class="{ 'shout-heading': floating }"
-        @click.stop.prevent="togglePanel"
-      >
-        <div class="title">
-          {{ $t('shoutbox.title') }}
-          <FAIcon
-            v-if="floating"
-            icon="times"
-            class="close-icon"
-          />
-        </div>
-      </div>
-      <div class="shout-window">
-        <div
-          v-for="message in messages"
-          :key="message.id"
-          class="shout-message"
-        >
-          <span class="shout-avatar">
-            <img :src="message.author.avatar">
-          </span>
-          <div class="shout-content">
-            <router-link
-              class="shout-name"
-              :to="userProfileLink(message.author)"
-            >
-              {{ message.author.username }}
-            </router-link>
-            <br>
-            <span class="shout-text">
-              {{ message.text }}
-            </span>
-          </div>
-        </div>
-      </div>
-      <div class="shout-input">
-        <textarea
-          v-model="currentMessage"
-          class="shout-input-textarea"
-          rows="1"
-          @keyup.enter="submit(currentMessage)"
-        />
-      </div>
-    </div>
-  </div>
-  <div
-    v-else
-    class="shout-panel"
-  >
-    <div class="panel panel-default">
-      <div
-        class="panel-heading -stub timeline-heading shout-heading"
-        @click.stop.prevent="togglePanel"
-      >
-        <div class="title">
-          <FAIcon
-            class="icon"
-            icon="bullhorn"
-          />
-          {{ $t('shoutbox.title') }}
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script src="./shout_panel.js"></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.floating-shout {
-  position: fixed;
-  bottom: 0.5em;
-  z-index: 1000;
-  max-width: 25em;
-
-  &.-left {
-    left: 0.5em;
-  }
-
-  &:not(.-left) {
-    right: 0.5em;
-  }
-}
-
-.shout-panel {
-  .shout-heading {
-    cursor: pointer;
-
-    .icon {
-      color: $fallback--text;
-      color: var(--panelText, $fallback--text);
-      margin-right: 0.5em;
-    }
-
-    .title {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-    }
-  }
-
-  .shout-window {
-    overflow-y: auto;
-    overflow-x: hidden;
-    max-height: 20em;
-  }
-
-  .shout-window-container {
-    height: 100%;
-  }
-
-  .shout-message {
-    display: flex;
-    padding: 0.2em 0.5em;
-  }
-
-  .shout-avatar {
-    img {
-      height: 24px;
-      width: 24px;
-      border-radius: $fallback--avatarRadius;
-      border-radius: var(--avatarRadius, $fallback--avatarRadius);
-      margin-right: 0.5em;
-      margin-top: 0.25em;
-    }
-  }
-
-  .shout-input {
-    display: flex;
-
-    textarea {
-      flex: 1;
-      margin: 0.6em;
-      min-height: 3.5em;
-      resize: none;
-    }
-  }
-
-  .shout-panel {
-    .title {
-      display: flex;
-      justify-content: space-between;
-    }
-  }
-}
-</style>
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index c5c28698..9bfee6bd 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -1,4 +1,4 @@
-import { mapState, mapGetters } from 'vuex'
+import { mapGetters } from 'vuex'
 import UserCard from '../user_card/user_card.vue'
 import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
 import GestureService from '../../services/gesture_service/gesture_service'
@@ -51,7 +51,6 @@ const SideDrawer = {
     currentUser () {
       return this.$store.state.users.currentUser
     },
-    shout () { return this.$store.state.shout.joined },
     unseenNotifications () {
       return unseenNotificationsFromStore(this.$store)
     },
@@ -85,10 +84,7 @@ const SideDrawer = {
       }
       return this.currentUser ? 'friends' : 'public-timeline'
     },
-    ...mapState({
-      pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
-    }),
-    ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
+    ...mapGetters(['unreadAnnouncementCount'])
   },
   methods: {
     toggleDrawer () {
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 0b6fb8d9..4bbc43bb 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -67,27 +67,6 @@
             /> {{ $t("nav.lists") }}
           </router-link>
         </li>
-        <li
-          v-if="currentUser && pleromaChatMessagesAvailable"
-          @click="toggleDrawer"
-        >
-          <router-link
-            :to="{ name: 'chats', params: { username: currentUser.screen_name } }"
-            style="position: relative"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110 fa-old-padding"
-              icon="comments"
-            /> {{ $t("nav.chats") }}
-            <span
-              v-if="unreadChatCount"
-              class="badge badge-notification"
-            >
-              {{ unreadChatCount }}
-            </span>
-          </router-link>
-        </li>
       </ul>
       <ul v-if="currentUser">
         <li @click="toggleDrawer">
@@ -117,18 +96,6 @@
             </span>
           </router-link>
         </li>
-        <li
-          v-if="shout"
-          @click="toggleDrawer"
-        >
-          <router-link :to="{ name: 'shout-panel' }">
-            <FAIcon
-              fixed-width
-              class="fa-scale-110 fa-old-padding"
-              icon="bullhorn"
-            /> {{ $t("shoutbox.title") }}
-          </router-link>
-        </li>
       </ul>
       <ul>
         <li
diff --git a/src/main.js b/src/main.js
index 33666308..356a45da 100644
--- a/src/main.js
+++ b/src/main.js
@@ -11,7 +11,6 @@ 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'
 import mediaViewerModule from './modules/media_viewer.js'
@@ -19,7 +18,6 @@ import oauthTokensModule from './modules/oauth_tokens.js'
 import reportsModule from './modules/reports.js'
 import pollsModule from './modules/polls.js'
 import postStatusModule from './modules/postStatus.js'
-import chatsModule from './modules/chats.js'
 import announcementsModule from './modules/announcements.js'
 
 import { createI18n } from 'vue-i18n'
@@ -76,7 +74,6 @@ const persistedStateOptions = {
       api: apiModule,
       config: configModule,
       serverSideConfig: serverSideConfigModule,
-      shout: shoutModule,
       oauth: oauthModule,
       authFlow: authFlowModule,
       mediaViewer: mediaViewerModule,
@@ -84,7 +81,6 @@ const persistedStateOptions = {
       reports: reportsModule,
       polls: pollsModule,
       postStatus: postStatusModule,
-      chats: chatsModule,
       announcements: announcementsModule
     },
     plugins,
diff --git a/src/modules/api.js b/src/modules/api.js
index 0521db5d..3e3d2d4b 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -1,7 +1,5 @@
 import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
 import { WSConnectionStatus } from '../services/api/api.service.js'
-import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
-import { Socket } from 'phoenix'
 
 const retryTimeout = (multiplier) => 1000 * multiplier
 
@@ -102,19 +100,6 @@ const api = {
                 })
               } else if (message.event === 'delete') {
                 dispatch('deleteStatusById', message.id)
-              } else if (message.event === 'pleroma:chat_update') {
-                // The setTimeout wrapper is a temporary band-aid to avoid duplicates for the user's own messages when doing optimistic sending.
-                // The cause of the duplicates is the WS event arriving earlier than the HTTP response.
-                // This setTimeout wrapper can be removed once the commit `8e41baff` is in the stable Pleroma release.
-                // (`8e41baff` adds the idempotency key to the chat message entity, which PleromaFE uses when it's available, and it makes this artificial delay unnecessary).
-                setTimeout(() => {
-                  dispatch('addChatMessages', {
-                    chatId: message.chatUpdate.id,
-                    messages: [message.chatUpdate.lastMessage]
-                  })
-                  dispatch('updateChat', { chat: message.chatUpdate })
-                  maybeShowChatNotification(store, message.chatUpdate)
-                }, 100)
               }
             }
           )
@@ -134,15 +119,12 @@ const api = {
             ]).has(state.mastoUserSocketStatus)) {
               dispatch('stopFetchingTimeline', { timeline: 'friends' })
               dispatch('stopFetchingNotifications')
-              dispatch('stopFetchingChats')
             }
             commit('resetRetryMultiplier')
             commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED)
           })
           state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
             console.error('Error in MastoAPI websocket:', error)
-            // TODO is this needed?
-            dispatch('clearOpenedChats')
           })
           state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
             const ignoreCodes = new Set([
@@ -162,7 +144,6 @@ const api = {
               if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) {
                 dispatch('startFetchingTimeline', { timeline: 'friends' })
                 dispatch('startFetchingNotifications')
-                dispatch('startFetchingChats')
                 dispatch('startFetchingAnnouncements')
                 dispatch('pushGlobalNotice', {
                   level: 'error',
@@ -173,7 +154,6 @@ const api = {
               }
               commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
             }
-            dispatch('clearOpenedChats')
           })
           resolve()
         } catch (e) {
@@ -184,7 +164,6 @@ const api = {
     stopMastoUserSocket ({ state, dispatch }) {
       dispatch('startFetchingTimeline', { timeline: 'friends' })
       dispatch('startFetchingNotifications')
-      dispatch('startFetchingChats')
       state.mastoUserSocket.close()
     },
 
@@ -278,17 +257,6 @@ const api = {
     setWsToken (store, token) {
       store.commit('setWsToken', token)
     },
-    initializeSocket ({ dispatch, commit, state, rootState }) {
-      // Set up websocket connection
-      const token = state.wsToken
-      if (rootState.instance.shoutAvailable && typeof token !== 'undefined' && state.socket === null) {
-        const socket = new Socket('/socket', { params: { token } })
-        socket.connect()
-
-        commit('setSocket', socket)
-        dispatch('initializeShout', socket)
-      }
-    },
     disconnectFromSocket ({ commit, state }) {
       state.socket && state.socket.disconnect()
       commit('setSocket', null)
diff --git a/src/modules/chats.js b/src/modules/chats.js
deleted file mode 100644
index b5aa24b9..00000000
--- a/src/modules/chats.js
+++ /dev/null
@@ -1,240 +0,0 @@
-import { reactive } from 'vue'
-import { find, omitBy, orderBy, sumBy } from 'lodash'
-import chatService from '../services/chat_service/chat_service.js'
-import { parseChat, parseChatMessage } from '../services/entity_normalizer/entity_normalizer.service.js'
-import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
-import { promiseInterval } from '../services/promise_interval/promise_interval.js'
-
-const emptyChatList = () => ({
-  data: [],
-  idStore: {}
-})
-
-const defaultState = {
-  chatList: emptyChatList(),
-  chatListFetcher: null,
-  openedChats: reactive({}),
-  openedChatMessageServices: reactive({}),
-  fetcher: undefined,
-  currentChatId: null,
-  lastReadMessageId: null
-}
-
-const getChatById = (state, id) => {
-  return find(state.chatList.data, { id })
-}
-
-const sortedChatList = (state) => {
-  return orderBy(state.chatList.data, ['updated_at'], ['desc'])
-}
-
-const unreadChatCount = (state) => {
-  return sumBy(state.chatList.data, 'unread')
-}
-
-const chats = {
-  state: { ...defaultState },
-  getters: {
-    currentChat: state => state.openedChats[state.currentChatId],
-    currentChatMessageService: state => state.openedChatMessageServices[state.currentChatId],
-    findOpenedChatByRecipientId: state => recipientId => find(state.openedChats, c => c.account.id === recipientId),
-    sortedChatList,
-    unreadChatCount
-  },
-  actions: {
-    // Chat list
-    startFetchingChats ({ dispatch, commit }) {
-      const fetcher = () => dispatch('fetchChats', { latest: true })
-      fetcher()
-      commit('setChatListFetcher', {
-        fetcher: () => promiseInterval(fetcher, 60000)
-      })
-    },
-    stopFetchingChats ({ commit }) {
-      commit('setChatListFetcher', { fetcher: undefined })
-    },
-    fetchChats ({ dispatch, rootState, commit }, params = {}) {
-      return rootState.api.backendInteractor.chats()
-        .then(({ chats }) => {
-          dispatch('addNewChats', { chats })
-          return chats
-        })
-    },
-    addNewChats (store, { chats }) {
-      const { commit, dispatch, rootGetters } = store
-      const newChatMessageSideEffects = (chat) => {
-        maybeShowChatNotification(store, chat)
-      }
-      commit('addNewChats', { dispatch, chats, rootGetters, newChatMessageSideEffects })
-    },
-    updateChat ({ commit }, { chat }) {
-      commit('updateChat', { chat })
-    },
-
-    // Opened Chats
-    startFetchingCurrentChat ({ commit, dispatch }, { fetcher }) {
-      dispatch('setCurrentChatFetcher', { fetcher })
-    },
-    setCurrentChatFetcher ({ rootState, commit }, { fetcher }) {
-      commit('setCurrentChatFetcher', { fetcher })
-    },
-    addOpenedChat ({ rootState, commit, dispatch }, { chat }) {
-      commit('addOpenedChat', { dispatch, chat: parseChat(chat) })
-      dispatch('addNewUsers', [chat.account])
-    },
-    addChatMessages ({ commit }, value) {
-      commit('addChatMessages', { commit, ...value })
-    },
-    resetChatNewMessageCount ({ commit }, value) {
-      commit('resetChatNewMessageCount', value)
-    },
-    clearCurrentChat ({ rootState, commit, dispatch }, value) {
-      commit('setCurrentChatId', { chatId: undefined })
-      commit('setCurrentChatFetcher', { fetcher: undefined })
-    },
-    readChat ({ rootState, commit, dispatch }, { id, lastReadId }) {
-      const isNewMessage = rootState.chats.lastReadMessageId !== lastReadId
-
-      dispatch('resetChatNewMessageCount')
-      commit('readChat', { id, lastReadId })
-
-      if (isNewMessage) {
-        rootState.api.backendInteractor.readChat({ id, lastReadId })
-      }
-    },
-    deleteChatMessage ({ rootState, commit }, value) {
-      rootState.api.backendInteractor.deleteChatMessage(value)
-      commit('deleteChatMessage', { commit, ...value })
-    },
-    resetChats ({ commit, dispatch }) {
-      dispatch('clearCurrentChat')
-      commit('resetChats', { commit })
-    },
-    clearOpenedChats ({ rootState, commit, dispatch, rootGetters }) {
-      commit('clearOpenedChats', { commit })
-    },
-    handleMessageError ({ commit }, value) {
-      commit('handleMessageError', { commit, ...value })
-    },
-    cullOlderMessages ({ commit }, chatId) {
-      commit('cullOlderMessages', chatId)
-    }
-  },
-  mutations: {
-    setChatListFetcher (state, { commit, fetcher }) {
-      const prevFetcher = state.chatListFetcher
-      if (prevFetcher) {
-        prevFetcher.stop()
-      }
-      state.chatListFetcher = fetcher && fetcher()
-    },
-    setCurrentChatFetcher (state, { fetcher }) {
-      const prevFetcher = state.fetcher
-      if (prevFetcher) {
-        prevFetcher.stop()
-      }
-      state.fetcher = fetcher && fetcher()
-    },
-    addOpenedChat (state, { _dispatch, chat }) {
-      state.currentChatId = chat.id
-      state.openedChats[chat.id] = chat
-
-      if (!state.openedChatMessageServices[chat.id]) {
-        state.openedChatMessageServices[chat.id] = chatService.empty(chat.id)
-      }
-    },
-    setCurrentChatId (state, { chatId }) {
-      state.currentChatId = chatId
-    },
-    addNewChats (state, { chats, newChatMessageSideEffects }) {
-      chats.forEach((updatedChat) => {
-        const chat = getChatById(state, updatedChat.id)
-
-        if (chat) {
-          const isNewMessage = (chat.lastMessage && chat.lastMessage.id) !== (updatedChat.lastMessage && updatedChat.lastMessage.id)
-          chat.lastMessage = updatedChat.lastMessage
-          chat.unread = updatedChat.unread
-          chat.updated_at = updatedChat.updated_at
-          if (isNewMessage && chat.unread) {
-            newChatMessageSideEffects(updatedChat)
-          }
-        } else {
-          state.chatList.data.push(updatedChat)
-          state.chatList.idStore[updatedChat.id] = updatedChat
-        }
-      })
-    },
-    updateChat (state, { _dispatch, chat: updatedChat, _rootGetters }) {
-      const chat = getChatById(state, updatedChat.id)
-      if (chat) {
-        chat.lastMessage = updatedChat.lastMessage
-        chat.unread = updatedChat.unread
-        chat.updated_at = updatedChat.updated_at
-      }
-      if (!chat) { state.chatList.data.unshift(updatedChat) }
-      state.chatList.idStore[updatedChat.id] = updatedChat
-    },
-    deleteChat (state, { _dispatch, id, _rootGetters }) {
-      state.chats.data = state.chats.data.filter(conversation =>
-        conversation.last_status.id !== id
-      )
-      state.chats.idStore = omitBy(state.chats.idStore, conversation => conversation.last_status.id === id)
-    },
-    resetChats (state, { commit }) {
-      state.chatList = emptyChatList()
-      state.currentChatId = null
-      commit('setChatListFetcher', { fetcher: undefined })
-      for (const chatId in state.openedChats) {
-        chatService.clear(state.openedChatMessageServices[chatId])
-        delete state.openedChats[chatId]
-        delete state.openedChatMessageServices[chatId]
-      }
-    },
-    setChatsLoading (state, { value }) {
-      state.chats.loading = value
-    },
-    addChatMessages (state, { chatId, messages, updateMaxId }) {
-      const chatMessageService = state.openedChatMessageServices[chatId]
-      if (chatMessageService) {
-        chatService.add(chatMessageService, { messages: messages.map(parseChatMessage), updateMaxId })
-      }
-    },
-    deleteChatMessage (state, { chatId, messageId }) {
-      const chatMessageService = state.openedChatMessageServices[chatId]
-      if (chatMessageService) {
-        chatService.deleteMessage(chatMessageService, messageId)
-      }
-    },
-    resetChatNewMessageCount (state, _value) {
-      const chatMessageService = state.openedChatMessageServices[state.currentChatId]
-      chatService.resetNewMessageCount(chatMessageService)
-    },
-    // Used when a connection loss occurs
-    clearOpenedChats (state) {
-      const currentChatId = state.currentChatId
-      for (const chatId in state.openedChats) {
-        if (currentChatId !== chatId) {
-          chatService.clear(state.openedChatMessageServices[chatId])
-          delete state.openedChats[chatId]
-          delete state.openedChatMessageServices[chatId]
-        }
-      }
-    },
-    readChat (state, { id, lastReadId }) {
-      state.lastReadMessageId = lastReadId
-      const chat = getChatById(state, id)
-      if (chat) {
-        chat.unread = 0
-      }
-    },
-    handleMessageError (state, { chatId, fakeId, isRetry }) {
-      const chatMessageService = state.openedChatMessageServices[chatId]
-      chatService.handleMessageError(chatMessageService, fakeId, isRetry)
-    },
-    cullOlderMessages (state, chatId) {
-      chatService.cullOlderMessages(state.openedChatMessageServices[chatId])
-    }
-  }
-}
-
-export default chats
diff --git a/src/modules/config.js b/src/modules/config.js
index 6eeafe78..93d7487f 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -28,7 +28,6 @@ export const defaultState = {
   customThemeSource: undefined,
   hideISP: false,
   hideInstanceWallpaper: false,
-  hideShoutbox: false,
   // bad name: actually hides posts of muted USERS
   hideMutedPosts: undefined, // instance default
   hideMutedThreads: undefined, // instance default
@@ -59,7 +58,6 @@ export const defaultState = {
     moves: true,
     emojiReactions: true,
     followRequest: true,
-    chatMention: true,
     polls: true
   },
   webPushNotifications: false,
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 63704ab1..8fe7f7a0 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -74,9 +74,6 @@ const defaultState = {
   knownDomains: [],
 
   // Feature-set, apparently, not everything here is reported...
-  shoutAvailable: false,
-  pleromaChatMessagesAvailable: false,
-  gopherAvailable: false,
   mediaProxyAvailable: false,
   suggestionsEnabled: false,
   suggestionsWeb: '',
@@ -127,11 +124,6 @@ const instance = {
         case 'name':
           dispatch('setPageTitle')
           break
-        case 'shoutAvailable':
-          if (value) {
-            dispatch('initializeSocket')
-          }
-          break
         case 'theme':
           dispatch('setTheme', value)
           break
diff --git a/src/modules/serverSideConfig.js b/src/modules/serverSideConfig.js
index 4b73af26..e7a6c95c 100644
--- a/src/modules/serverSideConfig.js
+++ b/src/modules/serverSideConfig.js
@@ -47,10 +47,6 @@ export const settingsMap = {
   },
   // Privacy
   'locked': 'locked',
-  'acceptChatMessages': {
-    get: 'pleroma.accepts_chat_messages',
-    set: 'accepts_chat_messages'
-  },
   'allowFollowingMove': {
     get: 'pleroma.allow_following_move',
     set: 'allow_following_move'
diff --git a/src/modules/shout.js b/src/modules/shout.js
deleted file mode 100644
index 88aefbfe..00000000
--- a/src/modules/shout.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const shout = {
-  state: {
-    messages: [],
-    channel: { state: '' },
-    joined: false
-  },
-  mutations: {
-    setChannel (state, channel) {
-      state.channel = channel
-    },
-    addMessage (state, message) {
-      state.messages.push(message)
-      state.messages = state.messages.slice(-19, 20)
-    },
-    setMessages (state, messages) {
-      state.messages = messages.slice(-19, 20)
-    },
-    setJoined (state, joined) {
-      state.joined = joined
-    }
-  },
-  actions: {
-    initializeShout (store, socket) {
-      const channel = socket.channel('chat:public')
-      channel.joinPush.receive('ok', () => {
-        store.commit('setJoined', true)
-      })
-      channel.onClose(() => {
-        store.commit('setJoined', false)
-      })
-      channel.onError(() => {
-        store.commit('setJoined', false)
-      })
-      channel.on('new_msg', (msg) => {
-        store.commit('addMessage', msg)
-      })
-      channel.on('messages', ({ messages }) => {
-        store.commit('setMessages', messages)
-      })
-      channel.join()
-      store.commit('setChannel', channel)
-    }
-  }
-}
-
-export default shout
diff --git a/src/modules/users.js b/src/modules/users.js
index 12eb621f..02a9e361 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -523,7 +523,6 @@ const users = {
           store.dispatch('stopFetchingFollowRequests')
           store.commit('clearNotifications')
           store.commit('resetStatuses')
-          store.dispatch('resetChats')
           store.dispatch('setLastTimeline', 'public-timeline')
           store.dispatch('setLayoutWidth', windowWidth())
           store.dispatch('setLayoutHeight', windowHeight())
@@ -555,9 +554,6 @@ const users = {
 
               if (user.token) {
                 store.dispatch('setWsToken', user.token)
-
-                // Initialize the shout socket.
-                store.dispatch('initializeSocket')
               }
 
               const startPolling = () => {
@@ -566,9 +562,6 @@ const users = {
 
                 // Start fetching notifications
                 store.dispatch('startFetchingNotifications')
-
-                // Start fetching chats
-                store.dispatch('startFetchingChats')
               }
 
               if (store.getters.mergedConfig.useStreamingApi) {
@@ -577,7 +570,6 @@ const users = {
                 store.dispatch('enableMastoSockets', true).catch((error) => {
                   console.error('Failed initializing MastoAPI Streaming socket', error)
                 }).then(() => {
-                  store.dispatch('fetchChats', { latest: true })
                   setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
                 })
               } else {
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index a98ea7f7..86fe2927 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -1,5 +1,5 @@
 import { each, map, concat, last, get } from 'lodash'
-import { parseStatus, parseUser, parseNotification, parseAttachment, parseChat, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
+import { parseStatus, parseUser, parseNotification, parseAttachment, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
 import { RegistrationError, StatusCodeError } from '../errors/errors'
 
 /* eslint-env browser */
@@ -92,11 +92,6 @@ const MASTODON_ANNOUNCEMENTS_DISMISS_URL = id => `/api/v1/announcements/${id}/di
 const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions`
 const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
 const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
-const PLEROMA_CHATS_URL = `/api/v1/pleroma/chats`
-const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}`
-const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages`
-const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read`
-const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}`
 const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
 const PLEROMA_ANNOUNCEMENTS_URL = '/api/v1/pleroma/admin/announcements'
 const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements'
@@ -1378,7 +1373,6 @@ const MASTODON_STREAMING_EVENTS = new Set([
 ])
 
 const PLEROMA_STREAMING_EVENTS = new Set([
-  'pleroma:chat_update'
 ])
 
 // A thin wrapper around WebSocket API that allows adding a pre-processor to it
@@ -1448,8 +1442,6 @@ export const handleMastoWS = (wsEvent) => {
       return { event, status: parseStatus(data) }
     } else if (event === 'notification') {
       return { event, notification: parseNotification(data) }
-    } else if (event === 'pleroma:chat_update') {
-      return { event, chatUpdate: parseChat(data) }
     }
   } else {
     console.warn('Unknown event', wsEvent)
@@ -1466,82 +1458,6 @@ export const WSConnectionStatus = Object.freeze({
   'STARTING_INITIAL': 6
 })
 
-const chats = ({ credentials }) => {
-  return fetch(PLEROMA_CHATS_URL, { headers: authHeaders(credentials) })
-    .then((data) => data.json())
-    .then((data) => {
-      return { chats: data.map(parseChat).filter(c => c) }
-    })
-}
-
-const getOrCreateChat = ({ accountId, credentials }) => {
-  return promisedRequest({
-    url: PLEROMA_CHAT_URL(accountId),
-    method: 'POST',
-    credentials
-  })
-}
-
-const chatMessages = ({ id, credentials, maxId, sinceId, limit = 20 }) => {
-  let url = PLEROMA_CHAT_MESSAGES_URL(id)
-  const args = [
-    maxId && `max_id=${maxId}`,
-    sinceId && `since_id=${sinceId}`,
-    limit && `limit=${limit}`
-  ].filter(_ => _).join('&')
-
-  url = url + (args ? '?' + args : '')
-
-  return promisedRequest({
-    url,
-    method: 'GET',
-    credentials
-  })
-}
-
-const sendChatMessage = ({ id, content, mediaId = null, idempotencyKey, credentials }) => {
-  const payload = {
-    'content': content
-  }
-
-  if (mediaId) {
-    payload['media_id'] = mediaId
-  }
-
-  const headers = {}
-
-  if (idempotencyKey) {
-    headers['idempotency-key'] = idempotencyKey
-  }
-
-  return promisedRequest({
-    url: PLEROMA_CHAT_MESSAGES_URL(id),
-    method: 'POST',
-    payload: payload,
-    credentials,
-    headers
-  })
-}
-
-const readChat = ({ id, lastReadId, credentials }) => {
-  return promisedRequest({
-    url: PLEROMA_CHAT_READ_URL(id),
-    method: 'POST',
-    payload: {
-      'last_read_id': lastReadId
-    },
-    credentials
-  })
-}
-
-const deleteChatMessage = ({ chatId, messageId, credentials }) => {
-  return promisedRequest({
-    url: PLEROMA_DELETE_CHAT_MESSAGE_URL(chatId, messageId),
-    method: 'DELETE',
-    credentials
-  })
-}
-
 const apiService = {
   verifyCredentials,
   fetchTimeline,
@@ -1639,12 +1555,6 @@ const apiService = {
   fetchDomainMutes,
   muteDomain,
   unmuteDomain,
-  chats,
-  getOrCreateChat,
-  chatMessages,
-  sendChatMessage,
-  readChat,
-  deleteChatMessage,
   fetchAnnouncements,
   dismissAnnouncement,
   postAnnouncement,
diff --git a/src/services/chat_service/chat_service.js b/src/services/chat_service/chat_service.js
deleted file mode 100644
index 92ff689d..00000000
--- a/src/services/chat_service/chat_service.js
+++ /dev/null
@@ -1,226 +0,0 @@
-import _ from 'lodash'
-
-const empty = (chatId) => {
-  return {
-    idIndex: {},
-    idempotencyKeyIndex: {},
-    messages: [],
-    newMessageCount: 0,
-    lastSeenMessageId: '0',
-    chatId: chatId,
-    minId: undefined,
-    maxId: undefined
-  }
-}
-
-const clear = (storage) => {
-  const failedMessageIds = []
-
-  for (const message of storage.messages) {
-    if (message.error) {
-      failedMessageIds.push(message.id)
-    } else {
-      delete storage.idIndex[message.id]
-      delete storage.idempotencyKeyIndex[message.idempotency_key]
-    }
-  }
-
-  storage.messages = storage.messages.filter(m => failedMessageIds.includes(m.id))
-  storage.newMessageCount = 0
-  storage.lastSeenMessageId = '0'
-  storage.minId = undefined
-  storage.maxId = undefined
-}
-
-const deleteMessage = (storage, messageId) => {
-  if (!storage) { return }
-  storage.messages = storage.messages.filter(m => m.id !== messageId)
-  delete storage.idIndex[messageId]
-
-  if (storage.maxId === messageId) {
-    const lastMessage = _.maxBy(storage.messages, 'id')
-    storage.maxId = lastMessage.id
-  }
-
-  if (storage.minId === messageId) {
-    const firstMessage = _.minBy(storage.messages, 'id')
-    storage.minId = firstMessage.id
-  }
-}
-
-const cullOlderMessages = (storage) => {
-  const maxIndex = storage.messages.length
-  const minIndex = maxIndex - 50
-  if (maxIndex <= 50) return
-
-  storage.messages = _.sortBy(storage.messages, ['id'])
-  storage.minId = storage.messages[minIndex].id
-  for (const message of storage.messages) {
-    if (message.id < storage.minId) {
-      delete storage.idIndex[message.id]
-      delete storage.idempotencyKeyIndex[message.idempotency_key]
-    }
-  }
-  storage.messages = storage.messages.slice(minIndex, maxIndex)
-}
-
-const handleMessageError = (storage, fakeId, isRetry) => {
-  if (!storage) { return }
-  const fakeMessage = storage.idIndex[fakeId]
-  if (fakeMessage) {
-    fakeMessage.error = true
-    fakeMessage.pending = false
-    if (!isRetry) {
-      // Ensure the failed message doesn't stay at the bottom of the list.
-      const lastPersistedMessage = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'desc'])[0]
-      if (lastPersistedMessage) {
-        const oldId = fakeMessage.id
-        fakeMessage.id = `${lastPersistedMessage.id}-${new Date().getTime()}`
-        storage.idIndex[fakeMessage.id] = fakeMessage
-        delete storage.idIndex[oldId]
-      }
-    }
-  }
-}
-
-const add = (storage, { messages: newMessages, updateMaxId = true }) => {
-  if (!storage) { return }
-  for (let i = 0; i < newMessages.length; i++) {
-    const message = newMessages[i]
-
-    // sanity check
-    if (message.chat_id !== storage.chatId) { return }
-
-    if (message.fakeId) {
-      const fakeMessage = storage.idIndex[message.fakeId]
-      if (fakeMessage) {
-        // In case the same id exists (chat update before POST response)
-        // make sure to remove the older duplicate message.
-        if (storage.idIndex[message.id]) {
-          delete storage.idIndex[message.id]
-          storage.messages = storage.messages.filter(msg => msg.id !== message.id)
-        }
-        Object.assign(fakeMessage, message, { error: false })
-        delete fakeMessage['fakeId']
-        storage.idIndex[fakeMessage.id] = fakeMessage
-        delete storage.idIndex[message.fakeId]
-
-        return
-      }
-    }
-
-    if (!storage.minId || (!message.pending && message.id < storage.minId)) {
-      storage.minId = message.id
-    }
-
-    if (!storage.maxId || message.id > storage.maxId) {
-      if (updateMaxId) {
-        storage.maxId = message.id
-      }
-    }
-
-    if (!storage.idIndex[message.id] && !isConfirmation(storage, message)) {
-      if (storage.lastSeenMessageId < message.id) {
-        storage.newMessageCount++
-      }
-      storage.idIndex[message.id] = message
-      storage.messages.push(storage.idIndex[message.id])
-      storage.idempotencyKeyIndex[message.idempotency_key] = true
-    }
-  }
-}
-
-const isConfirmation = (storage, message) => {
-  if (!message.idempotency_key) return
-  return storage.idempotencyKeyIndex[message.idempotency_key]
-}
-
-const resetNewMessageCount = (storage) => {
-  if (!storage) { return }
-  storage.newMessageCount = 0
-  storage.lastSeenMessageId = storage.maxId
-}
-
-// Inserts date separators and marks the head and tail if it's the chain of messages made by the same user
-const getView = (storage) => {
-  if (!storage) { return [] }
-
-  const result = []
-  const messages = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'asc'])
-  const firstMessage = messages[0]
-  let previousMessage = messages[messages.length - 1]
-  let currentMessageChainId
-
-  if (firstMessage) {
-    const date = new Date(firstMessage.created_at)
-    date.setHours(0, 0, 0, 0)
-    result.push({
-      type: 'date',
-      date,
-      id: date.getTime().toString()
-    })
-  }
-
-  let afterDate = false
-
-  for (let i = 0; i < messages.length; i++) {
-    const message = messages[i]
-    const nextMessage = messages[i + 1]
-
-    const date = new Date(message.created_at)
-    date.setHours(0, 0, 0, 0)
-
-    // insert date separator and start a new message chain
-    if (previousMessage && previousMessage.date < date) {
-      result.push({
-        type: 'date',
-        date,
-        id: date.getTime().toString()
-      })
-
-      previousMessage['isTail'] = true
-      currentMessageChainId = undefined
-      afterDate = true
-    }
-
-    const object = {
-      type: 'message',
-      data: message,
-      date,
-      id: message.id,
-      messageChainId: currentMessageChainId
-    }
-
-    // end a message chian
-    if ((nextMessage && nextMessage.account_id) !== message.account_id) {
-      object['isTail'] = true
-      currentMessageChainId = undefined
-    }
-
-    // start a new message chain
-    if ((previousMessage && previousMessage.data && previousMessage.data.account_id) !== message.account_id || afterDate) {
-      currentMessageChainId = _.uniqueId()
-      object['isHead'] = true
-      object['messageChainId'] = currentMessageChainId
-    }
-
-    result.push(object)
-    previousMessage = object
-    afterDate = false
-  }
-
-  return result
-}
-
-const ChatService = {
-  add,
-  empty,
-  getView,
-  deleteMessage,
-  cullOlderMessages,
-  resetNewMessageCount,
-  clear,
-  handleMessageError
-}
-
-export default ChatService
diff --git a/src/services/chat_utils/chat_utils.js b/src/services/chat_utils/chat_utils.js
deleted file mode 100644
index de6e0625..00000000
--- a/src/services/chat_utils/chat_utils.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
-
-export const maybeShowChatNotification = (store, chat) => {
-  if (!chat.lastMessage) return
-  if (store.rootState.chats.currentChatId === chat.id && !document.hidden) return
-  if (store.rootState.users.currentUser.id === chat.lastMessage.account_id) return
-
-  const opts = {
-    tag: chat.lastMessage.id,
-    title: chat.account.name,
-    icon: chat.account.profile_image_url,
-    body: chat.lastMessage.content
-  }
-
-  if (chat.lastMessage.attachment && chat.lastMessage.attachment.type === 'image') {
-    opts.image = chat.lastMessage.attachment.preview_url
-  }
-
-  showDesktopNotification(store.rootState, opts)
-}
-
-export const buildFakeMessage = ({ content, chatId, attachments, userId, idempotencyKey }) => {
-  const fakeMessage = {
-    content,
-    chat_id: chatId,
-    created_at: new Date(),
-    id: `${new Date().getTime()}`,
-    attachments: attachments,
-    account_id: userId,
-    idempotency_key: idempotencyKey,
-    emojis: [],
-    pending: true,
-    isNormalized: true
-  }
-
-  if (attachments[0]) {
-    fakeMessage.attachment = attachments[0]
-  }
-
-  return fakeMessage
-}
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index e2ab5577..074bd0ad 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -94,7 +94,6 @@ export const parseUser = (data) => {
 
       output.background_image = data.pleroma.background_image
       output.favicon = data.pleroma.favicon
-      output.token = data.pleroma.chat_token
 
       if (relationship) {
         output.relationship = relationship
@@ -200,7 +199,6 @@ export const parseUser = (data) => {
       : data.pleroma.deactivated // old backend
 
     output.notification_settings = data.pleroma.notification_settings
-    output.unread_chat_count = data.pleroma.unread_chat_count
   }
 
   output.tags = output.tags || []
@@ -403,7 +401,7 @@ export const parseNotification = (data) => {
       ? parseStatus(data.notice.favorited_status)
       : parsedNotice
     output.action = parsedNotice
-    output.from_profile = output.type === 'pleroma:chat_mention' ? parseUser(data.account) : parseUser(data.from_profile)
+    output.from_profile = parseUser(data.from_profile)
   }
 
   output.created_at = new Date(data.created_at)
@@ -429,34 +427,3 @@ export const parseLinkHeaderPagination = (linkHeader, opts = {}) => {
     minId: flakeId ? minId : parseInt(minId, 10)
   }
 }
-
-export const parseChat = (chat) => {
-  const output = {}
-  output.id = chat.id
-  output.account = parseUser(chat.account)
-  output.unread = chat.unread
-  output.lastMessage = parseChatMessage(chat.last_message)
-  output.updated_at = new Date(chat.updated_at)
-  return output
-}
-
-export const parseChatMessage = (message) => {
-  if (!message) { return }
-  if (message.isNormalized) { return message }
-  const output = message
-  output.id = message.id
-  output.created_at = new Date(message.created_at)
-  output.chat_id = message.chat_id
-  output.emojis = message.emojis
-  output.content = message.content
-  if (message.attachment) {
-    output.attachments = [parseAttachment(message.attachment)]
-  } else {
-    output.attachments = []
-  }
-  output.pending = !!message.pending
-  output.error = false
-  output.idempotency_key = message.idempotency_key
-  output.isNormalized = true
-  return output
-}
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 543aa874..d5bf8749 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -106,8 +106,7 @@ export const generateRadii = (input) => {
     avatar: 5,
     avatarAlt: 50,
     tooltip: 2,
-    attachment: 5,
-    chatMessage: inputRadii.panel
+    attachment: 5
   })
 
   return {
diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js
index c2983be7..29474512 100644
--- a/src/services/theme_data/pleromafe.js
+++ b/src/services/theme_data/pleromafe.js
@@ -23,9 +23,7 @@ export const LAYERS = {
   inputTopBar: 'topBar',
   alert: 'bg',
   alertPanel: 'panel',
-  poll: 'bg',
-  chatBg: 'underlay',
-  chatMessage: 'chatBg'
+  poll: 'bg'
 }
 
 /* By default opacity slots have 1 as default opacity
@@ -707,58 +705,5 @@ export const SLOT_INHERITANCE = {
     layer: 'badge',
     variant: 'badgeNotification',
     textColor: 'bw'
-  },
-
-  chatBg: {
-    depends: ['bg']
-  },
-
-  chatMessageIncomingBg: {
-    depends: ['chatBg']
-  },
-
-  chatMessageIncomingText: {
-    depends: ['text'],
-    layer: 'chatMessage',
-    variant: 'chatMessageIncomingBg',
-    textColor: true
-  },
-
-  chatMessageIncomingLink: {
-    depends: ['link'],
-    layer: 'chatMessage',
-    variant: 'chatMessageIncomingBg',
-    textColor: 'preserve'
-  },
-
-  chatMessageIncomingBorder: {
-    depends: ['border'],
-    opacity: 'border',
-    color: (mod, border) => brightness(2 * mod, border).rgb
-  },
-
-  chatMessageOutgoingBg: {
-    depends: ['chatMessageIncomingBg'],
-    color: (mod, chatMessage) => brightness(5 * mod, chatMessage).rgb
-  },
-
-  chatMessageOutgoingText: {
-    depends: ['text'],
-    layer: 'chatMessage',
-    variant: 'chatMessageOutgoingBg',
-    textColor: true
-  },
-
-  chatMessageOutgoingLink: {
-    depends: ['link'],
-    layer: 'chatMessage',
-    variant: 'chatMessageOutgoingBg',
-    textColor: 'preserve'
-  },
-
-  chatMessageOutgoingBorder: {
-    depends: ['chatMessageOutgoingBg'],
-    opacity: 'border',
-    color: (mod, border) => brightness(2 * mod, border).rgb
   }
 }
diff --git a/test/unit/specs/services/chat_service/chat_service.spec.js b/test/unit/specs/services/chat_service/chat_service.spec.js
deleted file mode 100644
index fbbca436..00000000
--- a/test/unit/specs/services/chat_service/chat_service.spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import chatService from '../../../../../src/services/chat_service/chat_service.js'
-
-const message1 = {
-  id: '9wLkdcmQXD21Oy8lEX',
-  idempotency_key: '1',
-  created_at: (new Date('2020-06-22T18:45:53.000Z'))
-}
-
-const message2 = {
-  id: '9wLkdp6ihaOVdNj8Wu',
-  idempotency_key: '2',
-  account_id: '9vmRb29zLQReckr5ay',
-  created_at: (new Date('2020-06-22T18:45:56.000Z'))
-}
-
-const message3 = {
-  id: '9wLke9zL4Dy4OZR2RM',
-  idempotency_key: '3',
-  account_id: '9vmRb29zLQReckr5ay',
-  created_at: (new Date('2020-07-22T18:45:59.000Z'))
-}
-
-describe('chatService', () => {
-  describe('.add', () => {
-    it("Doesn't add duplicates", () => {
-      const chat = chatService.empty()
-      chatService.add(chat, { messages: [ message1 ] })
-      chatService.add(chat, { messages: [ message1 ] })
-      expect(chat.messages.length).to.eql(1)
-
-      chatService.add(chat, { messages: [ message2 ] })
-      expect(chat.messages.length).to.eql(2)
-    })
-
-    it('Updates minId and lastMessage and newMessageCount', () => {
-      const chat = chatService.empty()
-
-      chatService.add(chat, { messages: [ message1 ] })
-      expect(chat.maxId).to.eql(message1.id)
-      expect(chat.minId).to.eql(message1.id)
-      expect(chat.newMessageCount).to.eql(1)
-
-      chatService.add(chat, { messages: [ message2 ] })
-      expect(chat.maxId).to.eql(message2.id)
-      expect(chat.minId).to.eql(message1.id)
-      expect(chat.newMessageCount).to.eql(2)
-
-      chatService.resetNewMessageCount(chat)
-      expect(chat.newMessageCount).to.eql(0)
-      expect(chat.lastSeenMessageId).to.eql(message2.id)
-
-      // Add message with higher id
-      chatService.add(chat, { messages: [ message3 ] })
-      expect(chat.newMessageCount).to.eql(1)
-    })
-  })
-
-  describe('.delete', () => {
-    it('Updates minId and lastMessage', () => {
-      const chat = chatService.empty()
-
-      chatService.add(chat, { messages: [ message1 ] })
-      chatService.add(chat, { messages: [ message2 ] })
-      chatService.add(chat, { messages: [ message3 ] })
-
-      expect(chat.maxId).to.eql(message3.id)
-      expect(chat.minId).to.eql(message1.id)
-
-      chatService.deleteMessage(chat, message3.id)
-      expect(chat.maxId).to.eql(message2.id)
-      expect(chat.minId).to.eql(message1.id)
-
-      chatService.deleteMessage(chat, message1.id)
-      expect(chat.maxId).to.eql(message2.id)
-      expect(chat.minId).to.eql(message2.id)
-    })
-  })
-
-  describe('.getView', () => {
-    it('Inserts date separators', () => {
-      const chat = chatService.empty()
-
-      chatService.add(chat, { messages: [ message1 ] })
-      chatService.add(chat, { messages: [ message2 ] })
-      chatService.add(chat, { messages: [ message3 ] })
-
-      const view = chatService.getView(chat)
-      expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message'])
-    })
-  })
-
-  describe('.cullOlderMessages', () => {
-    it('keeps 50 newest messages and idIndex matches', () => {
-      const chat = chatService.empty()
-
-      for (let i = 100; i > 0; i--) {
-        // Use decimal values with toFixed to hack together constant length predictable strings
-        chatService.add(chat, { messages: [{ ...message1, id: 'a' + (i / 1000).toFixed(3), idempotency_key: i }] })
-      }
-      chatService.cullOlderMessages(chat)
-      expect(chat.messages.length).to.eql(50)
-      expect(chat.messages[0].id).to.eql('a0.051')
-      expect(chat.minId).to.eql('a0.051')
-      expect(chat.messages[49].id).to.eql('a0.100')
-      expect(Object.keys(chat.idIndex).length).to.eql(50)
-    })
-  })
-})