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) - }) - }) -})