From 0d78bb2f9d050324d2ffe1048b85c3d764637c60 Mon Sep 17 00:00:00 2001 From: Simon Laux Date: Wed, 27 May 2020 06:10:08 +0200 Subject: [PATCH] start to virtualize the chatview, somehow it doesn't work yet --- src/renderer/components/composer/Composer.tsx | 22 +- src/renderer/components/dialogs/DeadDrop.tsx | 5 +- src/renderer/components/map/MapComponent.tsx | 7 +- .../components/message/MessageList.tsx | 324 +++++++----------- .../message/MessageListAndComposer.tsx | 9 +- .../components/message/MessageWrapper.tsx | 10 +- .../components/message/messageFunctions.ts | 32 ++ .../components/message/messageListHook.ts | 83 +++++ src/renderer/keybindings.ts | 11 + src/renderer/stores/chat.ts | 233 ------------- 10 files changed, 276 insertions(+), 460 deletions(-) create mode 100644 src/renderer/components/message/messageListHook.ts diff --git a/src/renderer/components/composer/Composer.tsx b/src/renderer/components/composer/Composer.tsx index 25287435b1..55a713b69c 100644 --- a/src/renderer/components/composer/Composer.tsx +++ b/src/renderer/components/composer/Composer.tsx @@ -5,8 +5,8 @@ import { SettingsContext } from '../../contexts' import ComposerMessageInput from './ComposerMessageInput' import { getLogger } from '../../../shared/logger' import { EmojiAndStickerPicker } from './EmojiAndStickerPicker' -import { useChatStore } from '../../stores/chat' import { EmojiData, BaseEmoji } from 'emoji-mart' +import { UI_SendMessage } from '../message/messageFunctions' const { remote } = window.electron_functions const log = getLogger('renderer/composer') @@ -36,23 +36,19 @@ const Composer = forwardRef< } >((props, ref) => { const { isDisabled, disabledReason, chatId, draft } = props - const chatStoreDispatch = useChatStore()[1] const [showEmojiPicker, setShowEmojiPicker] = useState(false) const messageInputRef = useRef() const emojiAndStickerRef = useRef() const pickerButtonRef = useRef() - const sendMessage = () => { + const sendTextMessage = () => { const message = messageInputRef.current.getText() if (message.match(/^\s*$/)) { log.debug(`Empty message: don't send it...`) return } - chatStoreDispatch({ - type: 'SEND_MESSAGE', - payload: [chatId, message, null], - }) + UI_SendMessage(chatId, { text: message }) messageInputRef.current.clearText() messageInputRef.current.focus() @@ -63,10 +59,7 @@ const Composer = forwardRef< { properties: ['openFile'] }, (filenames: string[]) => { if (filenames && filenames[0]) { - chatStoreDispatch({ - type: 'SEND_MESSAGE', - payload: [chatId, '', filenames[0]], - }) + UI_SendMessage(chatId, { text: '', filename: filenames[0] }) } } ) @@ -135,7 +128,7 @@ const Composer = forwardRef< )} -
+
diff --git a/src/renderer/components/dialogs/DeadDrop.tsx b/src/renderer/components/dialogs/DeadDrop.tsx index dc55d84c82..36e2157d26 100644 --- a/src/renderer/components/dialogs/DeadDrop.tsx +++ b/src/renderer/components/dialogs/DeadDrop.tsx @@ -1,7 +1,7 @@ import React from 'react' import { DeltaBackend } from '../../delta-remote' import { Classes } from '@blueprintjs/core' -import { useChatStore } from '../../stores/chat' +import { selectChat } from '../../stores/chat' import { DialogProps } from './DialogController' import { DCContact, MessageType } from '../../../shared/shared-types' import { SmallDialog } from './DeltaDialog' @@ -15,7 +15,6 @@ export default function DeadDrop(props: { onClose: DialogProps['onClose'] }) { const { contact, msg, onClose } = props - const chatStoreDispatch = useChatStore()[1] const never = () => { DeltaBackend.call('contacts.blockContact', contact.id) @@ -35,7 +34,7 @@ export default function DeadDrop(props: { messageId, contactId, }) - chatStoreDispatch({ type: 'SELECT_CHAT', payload: chatId }) + selectChat(chatId) onClose() } diff --git a/src/renderer/components/map/MapComponent.tsx b/src/renderer/components/map/MapComponent.tsx index 91294b116a..611b326e5e 100644 --- a/src/renderer/components/map/MapComponent.tsx +++ b/src/renderer/components/map/MapComponent.tsx @@ -13,7 +13,6 @@ import { Slider, Button, Collapse } from '@blueprintjs/core' import PopupMessage from './PopupMessage' import * as SessionStorage from '../helpers/SessionStorage' import { SettingsContext } from '../../contexts' -import chatStore from '../../stores/chat' import { state as LocationStoreState } from '../../stores/locations' @@ -25,6 +24,7 @@ import { JsonContact, JsonLocations, } from '../../../shared/shared-types' +import { UI_SendMessage } from '../message/messageFunctions' type MapData = { contact: JsonContact @@ -426,10 +426,7 @@ export default class MapComponent extends React.Component< return } const latLng = Object.assign({}, this.poiLocation) - chatStore.dispatch({ - type: 'SEND_MESSAGE', - payload: [selectedChat.id, message, null, latLng], - }) + UI_SendMessage(selectedChat.id, { text: message, location: latLng }) if (this.contextMenuPopup) { this.contextMenuPopup.remove() diff --git a/src/renderer/components/message/MessageList.tsx b/src/renderer/components/message/MessageList.tsx index aa9c59113e..501f4de4f2 100644 --- a/src/renderer/components/message/MessageList.tsx +++ b/src/renderer/components/message/MessageList.tsx @@ -1,229 +1,157 @@ import React, { useRef, useEffect } from 'react' import { MessageWrapper } from './MessageWrapper' -import { - useChatStore, - ChatStoreState, - ChatStoreDispatch, -} from '../../stores/chat' -import { useDebouncedCallback } from 'use-debounce' +import { ChatStoreState } from '../../stores/chat' import { C } from 'deltachat-node/dist/constants' import moment from 'moment' import { getLogger } from '../../../shared/logger' import { MessageType } from '../../../shared/shared-types' +import { + InfiniteLoader, + AutoSizer, + List, + CellMeasurerCache, + CellMeasurer, +} from 'react-virtualized' +import { useKeyBindingAction, KeybindAction } from '../../keybindings' +import { useMessageIdList, useMessageList } from './messageListHook' const log = getLogger('render/msgList') -const messageIdsToShow = ( - oldestFetchedMessageIndex: number, - messageIds: number[] -) => { - const messageIdsToShow = [] - for (let i = oldestFetchedMessageIndex; i < messageIds.length; i++) { - messageIdsToShow.push(messageIds[i]) - } - return messageIdsToShow -} - export default function MessageList({ chat, - refComposer, locationStreamingEnabled, }: { chat: ChatStoreState - refComposer: todo locationStreamingEnabled: boolean }) { - const [ - { - oldestFetchedMessageIndex, - messages, - messageIds, - scrollToBottom, - scrollToLastPage, - scrollHeight, - }, - chatStoreDispatch, - ] = useChatStore() - const messageListRef = useRef(null) - const lastKnownScrollHeight = useRef(null) - const isFetching = useRef(false) - - useEffect(() => { - if (scrollToBottom === false) return - - log.debug( - 'scrollToBottom', - messageListRef.current.scrollTop, - messageListRef.current.scrollHeight - ) - messageListRef.current.scrollTop = messageListRef.current.scrollHeight - chatStoreDispatch({ - type: 'FINISHED_SCROLL', - payload: 'SCROLLED_TO_BOTTOM', - }) - }, [scrollToBottom]) - - useEffect(() => { - if (scrollToLastPage === false) return - // restore old scroll position after new messages are rendered - messageListRef.current.scrollTop = - messageListRef.current.scrollHeight - lastKnownScrollHeight.current - chatStoreDispatch({ - type: 'FINISHED_SCROLL', - payload: 'SCROLLED_TO_LAST_PAGE', + console.log('render') + const heightCache = useRef( + new CellMeasurerCache({ + fixedWidth: true, + defaultHeight: 60, + minHeight: 25, }) - isFetching.current = false - }, [scrollToLastPage, scrollHeight]) - - useEffect(() => { - isFetching.current = false - - const composerTextarea = refComposer.current.childNodes[1] - composerTextarea && composerTextarea.focus() - }, [chat.id]) - - const [fetchMore] = useDebouncedCallback( - () => { - chatStoreDispatch({ - type: 'FETCH_MORE_MESSAGES', - payload: { scrollHeight: messageListRef.current.scrollHeight }, - }) - }, - 30, - { leading: true } ) - const onScroll = (Event: React.UIEvent) => { - if (messageListRef.current.scrollTop !== 0) return - if (isFetching.current === false) { - lastKnownScrollHeight.current = messageListRef.current.scrollHeight - isFetching.current = true - log.debug('Scrolled to top, fetching more messsages!') - fetchMore() - } - Event.preventDefault() - Event.stopPropagation() - return false - } - - return ( - + useEffect(() => heightCache.current.clearAll(), [chat.id]) + useKeyBindingAction(KeybindAction.Event_Window_Resize, () => + heightCache.current.clearAll() ) -} - -export const MessageListInner = React.memo( - (props: { - onScroll: (event: React.UIEvent) => void - oldestFetchedMessageIndex: number - messageIds: number[] - messages: ChatStoreState['messages'] - messageListRef: todo - locationStreamingEnabled: boolean - chat: ChatStoreState - chatStoreDispatch: ChatStoreDispatch - }) => { - const { - onScroll, - oldestFetchedMessageIndex, - messageIds, - messages, - messageListRef, - locationStreamingEnabled, - chat, - chatStoreDispatch, - } = props - const _messageIdsToShow = messageIdsToShow( - oldestFetchedMessageIndex, - messageIds - ) - const tx = window.translate + const messageIds = useMessageIdList(chat.id) + const { isMessageLoaded, loadMessages, messageCache } = useMessageList( + messageIds + ) - let specialMessageIdCounter = 0 + const tx = window.translate - let emptyChatMessage = tx('chat_no_messages_hint', [chat.name, chat.name]) + let emptyChatMessage = tx('chat_no_messages_hint', [chat.name, chat.name]) - if (chat.isGroup) { - emptyChatMessage = chat.isUnpromoted - ? tx('chat_new_group_hint') - : tx('chat_no_messages') - } else if (chat.isSelfTalk) { - emptyChatMessage = tx('saved_messages_explain') - } else if (chat.isDeviceChat) { - emptyChatMessage = tx('device_talk_explain') - } + if (chat.isGroup) { + emptyChatMessage = chat.isUnpromoted + ? tx('chat_new_group_hint') + : tx('chat_no_messages') + } else if (chat.isSelfTalk) { + emptyChatMessage = tx('saved_messages_explain') + } else if (chat.isDeviceChat) { + emptyChatMessage = tx('device_talk_explain') + } - return ( -
-
    - {messageIds.length < 1 ? ( -
  • -
    -

    {emptyChatMessage}

    -
    -
  • - ) : ( - '' - )} - {_messageIdsToShow.map((messageId, i) => { - if (messageId === C.DC_MSG_ID_DAYMARKER) { - const key = 'magic' + messageId + '_' + specialMessageIdCounter++ - const nextMessage = messages[_messageIdsToShow[i + 1]] - if (!nextMessage) return null - return ( - - ) - } - const message = messages[messageId] - if (!message || message.msg == null) return - return ( - - ) - })} -
-
- ) - }, - (prevProps, nextProps) => { - const areEqual = - prevProps.messageIds === nextProps.messageIds && - prevProps.messages === nextProps.messages && - prevProps.oldestFetchedMessageIndex === - nextProps.oldestFetchedMessageIndex && - prevProps.locationStreamingEnabled === nextProps.locationStreamingEnabled + const Loader = useRef(null) + const Scroller = useRef(null) - return areEqual - } -) + useKeyBindingAction(KeybindAction.ChatView_ScrollToBottom, () => { + Scroller.current?.scrollToRow(messageIds.length) + }) -export function DayMarker(props: { timestamp: number }) { - const { timestamp } = props - const tx = window.translate return ( -
-

- {moment.unix(timestamp).calendar(null, { - sameDay: `[${tx('today')}]`, - lastDay: `[${tx('yesterday')}]`, - lastWeek: 'LL', - sameElse: 'LL', - })} -

+
+ + {({ width, height }) => ( +
    + {messageIds.length < 1 && ( +
  • +
    +

    {emptyChatMessage}

    +
    +
  • + )} + + + {({ onRowsRendered, registerChild }) => ( + { + Scroller.current = ref + registerChild(ref) + }} + onRowsRendered={onRowsRendered} + height={height} + width={width} + rowCount={messageIds.length} + rowHeight={heightCache.current.rowHeight} + rowRenderer={({ key, index, parent }) => { + console.log('rowRenderer', index, messageIds[index]) + const msgId = messageIds[index] + + if (msgId === C.DC_MSG_ID_DAYMARKER) { + const nextMessage = messageCache[messageIds[index + 1]] + if (!nextMessage) return null + return ( + +
    +

    + {moment + .unix(nextMessage.msg.timestamp) + .calendar(null, { + sameDay: `[${tx('today')}]`, + lastDay: `[${tx('yesterday')}]`, + lastWeek: 'LL', + sameElse: 'LL', + })} +

    +
    +
    + ) + } + const message = messageCache[msgId] + if (!message || message.msg == null) return + return ( + + + + ) + }} + deferredMeasurementCache={heightCache.current} + /> + )} +
    +
+ )} +
) } diff --git a/src/renderer/components/message/MessageListAndComposer.tsx b/src/renderer/components/message/MessageListAndComposer.tsx index 4798f385a9..9fef9e7da2 100644 --- a/src/renderer/components/message/MessageListAndComposer.tsx +++ b/src/renderer/components/message/MessageListAndComposer.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useContext } from 'react' +import React, { useRef, useState, useContext, useEffect } from 'react' import { DeltaBackend } from '../../delta-remote' import Composer from '../composer/Composer' import { getLogger } from '../../../shared/logger' @@ -7,6 +7,7 @@ import { SettingsContext, ScreenContext } from '../../contexts' import { C } from 'deltachat-node/dist/constants' import { useDebouncedCallback } from 'use-debounce' import { ChatStoreState } from '../../stores/chat' +import { ActionEmitter, KeybindAction } from '../../keybindings' const { DC_CHAT_ID_DEADDROP, DC_CHAT_ID_STARRED } = C @@ -121,6 +122,11 @@ export default function MessageListAndComposer({ } } + // Focus composer when switching chats + useEffect(() => ActionEmitter.emitAction(KeybindAction.Composer_Focus), [ + chat.id, + ]) + return (
diff --git a/src/renderer/components/message/MessageWrapper.tsx b/src/renderer/components/message/MessageWrapper.tsx index 23de465bfd..ae47ecf5ca 100644 --- a/src/renderer/components/message/MessageWrapper.tsx +++ b/src/renderer/components/message/MessageWrapper.tsx @@ -4,8 +4,9 @@ import Message from './Message' import { ScreenContext } from '../../contexts' import { getLogger } from '../../../shared/logger' import { openViewProfileDialog } from '../helpers/ChatMethods' -import { ChatStoreState, ChatStoreDispatch } from '../../stores/chat' +import { ChatStoreState } from '../../stores/chat' import { MessageType, DCContact } from '../../../shared/shared-types' +import { UI_DeleteMessage } from './messageFunctions' const log = getLogger('renderer/messageWrapper') @@ -15,7 +16,6 @@ type RenderMessageProps = { message: MessageType locationStreamingEnabled: boolean chat: ChatStoreState - chatStoreDispatch: ChatStoreDispatch disableMenu?: boolean } @@ -29,7 +29,7 @@ export const MessageWrapper = (props: RenderMessageProps) => { export const RenderMessage = React.memo( (props: RenderMessageProps) => { - const { message, locationStreamingEnabled, chat, chatStoreDispatch } = props + const { message, locationStreamingEnabled, chat } = props const { fromId, id } = message.msg const msg = message.msg const tx = window.translate @@ -44,9 +44,7 @@ export const RenderMessage = React.memo( openDialog('ConfirmationDialog', { message: tx('ask_delete_message_desktop'), confirmLabel: tx('delete'), - cb: (yes: boolean) => - yes && - chatStoreDispatch({ type: 'UI_DELETE_MESSAGE', payload: msg.id }), + cb: (yes: boolean) => yes && UI_DeleteMessage(msg.id), }) const onContactClick = async (contact: DCContact) => { openViewProfileDialog(screenContext, contact.id) diff --git a/src/renderer/components/message/messageFunctions.ts b/src/renderer/components/message/messageFunctions.ts index 08a6369089..bde781aa19 100644 --- a/src/renderer/components/message/messageFunctions.ts +++ b/src/renderer/components/message/messageFunctions.ts @@ -3,6 +3,8 @@ const { openItem } = window.electron_functions import { getLogger } from '../../../shared/logger' const log = getLogger('render/msgFunctions') import { Message } from 'deltachat-node' +import { DeltaBackend } from '../../delta-remote' +import { ActionEmitter, KeybindAction } from '../../keybindings' /** * json representation of the message object we get from the backend */ @@ -19,3 +21,33 @@ export function openAttachmentInShell(msg: MsgObject) { ) } } + +type sendMessageArguments = { + text: string + filename?: string + location?: { + lat: number + lng: number + } +} + +export async function UI_SendMessage( + chatId: number, + { text, filename, location }: sendMessageArguments +) { + await DeltaBackend.call( + 'messageList.sendMessage', + chatId, + text, + filename, + location + ) + + ActionEmitter.emitAction(KeybindAction.ChatView_RefreshMessageList) + ActionEmitter.emitAction(KeybindAction.ChatView_ScrollToBottom) +} + +export async function UI_DeleteMessage(messageId: number) { + await DeltaBackend.call('messageList.deleteMessage', messageId) + ActionEmitter.emitAction(KeybindAction.ChatView_RefreshMessageList) +} diff --git a/src/renderer/components/message/messageListHook.ts b/src/renderer/components/message/messageListHook.ts new file mode 100644 index 0000000000..44fae53198 --- /dev/null +++ b/src/renderer/components/message/messageListHook.ts @@ -0,0 +1,83 @@ +import { useEffect, useState } from 'react' +import { DeltaBackend } from '../../delta-remote' +import { useKeyBindingAction, KeybindAction } from '../../keybindings' +import { MessageType } from '../../../shared/shared-types' +import { Index, IndexRange } from 'react-virtualized' + +export const enum LoadStatus { + FETCHING = 1, + LOADED = 2, +} + +export function useMessageIdList(chatId: number) { + const [state, setState] = useState([]) + + const refresh = () => { + DeltaBackend.call('messageList.getMessageIds', chatId).then(newState => { + setState(newState) + }) + } + + useEffect(refresh, [chatId]) + + // on some events refresh + useKeyBindingAction(KeybindAction.ChatView_RefreshMessageList, refresh) + + // reload on events + // 'DC_EVENT_INCOMING_MSG' + + return state +} + +export function useMessageList(messageIds: number[]) { + const [messageCache, setMessageCache] = useState<{ + [id: number]: MessageType | { msg: null } + }>({}) + const [messageLoadState, setMessageLoading] = useState<{ + [id: number]: undefined | LoadStatus.FETCHING | LoadStatus.LOADED + }>({}) + + const isMessageLoaded: (params: Index) => boolean = ({ index }) => + !!messageLoadState[messageIds[index]] + const loadMessages: (params: IndexRange) => Promise = async ({ + startIndex, + stopIndex, + }) => { + const ids = + startIndex == stopIndex + ? [messageIds[startIndex]] + : messageIds.slice(startIndex, stopIndex + 1) + + setMessageLoading(state => { + ids.forEach(id => (state[id] = LoadStatus.FETCHING)) + return state + }) + const messages = await DeltaBackend.call('messageList.getMessages', ids) + setMessageCache(cache => ({ ...cache, ...messages })) + setMessageLoading(state => { + ids.forEach(id => (state[id] = LoadStatus.LOADED)) + return state + }) + } + + // // discard cached message when recieving a changed event + + // 'DC_EVENT_MSGS_CHANGED' + + // // modify cached message accordingly on status change + // 'DC_EVENT_MSG_READ' + // 'DC_EVENT_MSG_DELIVERED' + + console.log({ + isMessageLoaded, + messageLoadState, + loadMessages, + messageCache, + }) + + return { + isMessageLoaded, + loadMessages, + messageCache, + } +} diff --git a/src/renderer/keybindings.ts b/src/renderer/keybindings.ts index de3b1b39dd..b46d21bcca 100644 --- a/src/renderer/keybindings.ts +++ b/src/renderer/keybindings.ts @@ -13,11 +13,16 @@ export enum KeybindAction { ChatList_ClearSearchInput = 'chatlist:clear-search', Composer_Focus = 'composer:focus', Settings_Open = 'settings:open', + ChatView_ScrollToBottom = 'chatview:scroll-bottom', + ChatView_RefreshMessageList = 'chatview:refresh-message-list', // Composite Actions (actions that trigger other actions) ChatList_FocusAndClearSearchInput = 'chatlist:focus-and-clear-search', ChatList_ExitSearch = 'chatlist:exit-search', ChatList_SearchSelectFirstChat = 'chatlist:search-select-first-chat', + + // Events that are not triggered by keys (abusing the keybindings system for passing events around) + Event_Window_Resize = 'window:resize', } export namespace ActionEmitter { @@ -133,3 +138,9 @@ ActionEmitter.registerHandler( ActionEmitter.emitAction(KeybindAction.Composer_Focus) } ) + +// Register eventListeners that trigger actions + +window.addEventListener('resize', () => { + ActionEmitter.emitAction(KeybindAction.Event_Window_Resize) +}) diff --git a/src/renderer/stores/chat.ts b/src/renderer/stores/chat.ts index 5cb558370b..244989b2d3 100644 --- a/src/renderer/stores/chat.ts +++ b/src/renderer/stores/chat.ts @@ -22,19 +22,11 @@ class state implements FullChat { contacts: JsonContact[] = [] color = '' - // summary = undefined freshMessageCounter = 0 isGroup = false isDeaddrop = false draft: string | null = null - messageIds: number[] = [] - messages: { [key: number]: MessageType | { msg: null } } = {} - oldestFetchedMessageIndex = -1 - scrollToBottom = false // if true the UI will scroll to bottom - scrollToLastPage = false // after fetching more messages reset scroll bar to old position - scrollHeight = 0 - countFetchedMessages = 0 muted = false } @@ -62,81 +54,6 @@ chatStore.attachReducer(({ type, payload, id }, state) => { return { ...defaultState } } else if (type === 'MODIFIED_CHAT') { return { ...state, ...payload } - } else if (type === 'FETCHED_MORE_MESSAGES') { - return { - ...state, - messages: { ...state.messages, ...payload.fetchedMessages }, - oldestFetchedMessageIndex: payload.oldestFetchedMessageIndex, - scrollToLastPage: true, - scrollHeight: payload.scrollHeight, - countFetchedMessages: payload.countFetchedMessages, - } - } else if (type === 'FETCHED_INCOMING_MESSAGES') { - return { - ...state, - messageIds: payload.messageIds, - messages: { - ...state.messages, - ...payload.messagesIncoming, - }, - scrollToBottom: true, - } - // type SCROLL_COMPLETE ? - } else if (type === 'FINISHED_SCROLL') { - if (payload === 'SCROLLED_TO_LAST_PAGE') { - return { ...state, scrollToLastPage: false, scrollHeight: 0 } - } else if (payload === 'SCROLLED_TO_BOTTOM') { - return { ...state, scrollToBottom: false } - } - } else if (type === 'UI_DELETE_MESSAGE') { - const msgId = payload - - const messageIndex = state.messageIds.indexOf(msgId) - let { oldestFetchedMessageIndex } = state - if (messageIndex === oldestFetchedMessageIndex) { - oldestFetchedMessageIndex += 1 - } - const messageIds = state.messageIds.filter(mId => mId !== msgId) - return { - ...state, - messageIds, - messages: { ...state.messages, [msgId]: null }, - oldestFetchedMessageIndex, - } - } else if (type === 'MESSAGE_CHANGED') { - return { - ...state, - messages: { ...state.messages, ...payload.messagesChanged }, - } - } else if (type === 'MESSAGE_SENT') { - const [messageId, message] = payload - const messageIds = [...state.messageIds, messageId] - const messages = { ...state.messages, [messageId]: message } - return { ...state, messageIds, messages, scrollToBottom: true } - } else if (type === 'MESSAGE_DELIVERED') { - const messages = { - ...state.messages, - [payload]: { - ...state.messages[payload], - msg: { - ...state.messages[payload].msg, - status: 'delivered', - }, - }, - } - return { ...state, messages } - } else if (type === 'MESSAGE_READ') { - const messages = { - ...state.messages, - [payload]: { - ...state.messages[payload], - msg: { - ...state.messages[payload].msg, - status: 'read', - }, - }, - } - return { ...state, messages } } return state }) @@ -149,86 +66,15 @@ chatStore.attachEffect(async ({ type, payload }, state) => { const chat = ( await DeltaBackend.call('chatList.selectChat', chatId) ) - const messageIds = ( - await DeltaBackend.call('messageList.getMessageIds', chatId) - ) - const oldestFetchedMessageIndex = Math.max(messageIds.length - PAGE_SIZE, 0) - const newestFetchedMessageIndex = messageIds.length - - const messageIdsToFetch = messageIds.slice( - oldestFetchedMessageIndex, - newestFetchedMessageIndex - ) - const messages = await DeltaBackend.call( - 'messageList.getMessages', - messageIdsToFetch - ) chatStore.dispatch({ type: 'SELECTED_CHAT', payload: { ...chat, id: chatId, - messageIds, - messages, - oldestFetchedMessageIndex, - scrollToBottom: true, }, }) mainProcessUpdateBadge() saveLastChatId(chatId) - } else if (type === 'UI_DELETE_MESSAGE') { - const msgId = payload - DeltaBackend.call('messageList.deleteMessage', msgId) - } else if (type === 'FETCH_MORE_MESSAGES') { - const oldestFetchedMessageIndex = Math.max( - state.oldestFetchedMessageIndex - PAGE_SIZE, - 0 - ) - const lastMessageIndexOnLastPage = state.oldestFetchedMessageIndex - if (lastMessageIndexOnLastPage === 0) return - const fetchedMessageIds = state.messageIds.slice( - oldestFetchedMessageIndex, - lastMessageIndexOnLastPage - ) - if (fetchedMessageIds.length === 0) return - - const fetchedMessages = await DeltaBackend.call( - 'messageList.getMessages', - fetchedMessageIds - ) - - chatStore.dispatch({ - type: 'FETCHED_MORE_MESSAGES', - payload: { - fetchedMessages, - oldestFetchedMessageIndex, - countFetchedMessages: fetchedMessageIds.length, - scrollHeight: payload.scrollHeight, - }, - }) - } else if (type === 'SEND_MESSAGE') { - if (payload[0] !== chatStore.state.id) return - const messageObj = await DeltaBackend.call( - 'messageList.sendMessage', - ...(payload as [ - number, - string, - string, - any - ]) /* [chatId, text, filename, location]*/ - ) - chatStore.dispatch({ - type: 'MESSAGE_SENT', - payload: messageObj, - id: payload[0], - }) - } else if (type === 'MUTE') { - if (payload[0] !== chatStore.state.id) return - if ( - !(await DeltaBackend.call('chat.setMuteDuration', payload[0], payload[1])) - ) { - return - } } }) @@ -259,85 +105,6 @@ ipcBackend.on('DC_EVENT_MSG_DELIVERED', (evt, [id, msgId]) => { }) }) -ipcBackend.on('DC_EVENT_INCOMING_MSG', async (_, [chatId, messageId]) => { - if (chatId !== chatStore.state.id) { - log.debug( - `DC_EVENT_INCOMING_MSG chatId of event (${chatId}) doesn't match id of selected chat (${chatStore.state.id}). Skipping.` - ) - return - } - const messageIds = ( - await DeltaBackend.call('messageList.getMessageIds', chatId) - ) - const messageIdsIncoming = messageIds.filter( - x => !chatStore.state.messageIds.includes(x) - ) - const messagesIncoming = await DeltaBackend.call( - 'messageList.getMessages', - messageIdsIncoming - ) - chatStore.dispatch({ - type: 'FETCHED_INCOMING_MESSAGES', - payload: { - messageIds, - messageIdsIncoming, - messagesIncoming, - }, - }) -}) - -ipcBackend.on('DC_EVENT_MSG_READ', (evt, [id, msgId]) => { - chatStore.dispatch({ - type: 'MESSAGE_READ', - id, - payload: msgId, - }) -}) - -ipcBackend.on('DC_EVENT_MSGS_CHANGED', async (_, [id, messageId]) => { - log.debug('DC_EVENT_MSGS_CHANGED', id, messageId) - if (id !== chatStore.state.id) return - if (chatStore.state.messageIds.indexOf(messageId) !== -1) { - log.debug( - 'DC_EVENT_MSGS_CHANGED', - 'changed message seems to be message we already know' - ) - const messagesChanged = await DeltaBackend.call('messageList.getMessages', [ - messageId, - ]) - chatStore.dispatch({ - type: 'MESSAGE_CHANGED', - payload: { - messageId, - messagesChanged, - }, - }) - } else { - log.debug( - 'DC_EVENT_MSGS_CHANGED', - 'changed message seems to be a new message' - ) - const messageIds = ( - await DeltaBackend.call('messageList.getMessageIds', id) - ) - const messageIdsIncoming = messageIds.filter( - x => !chatStore.state.messageIds.includes(x) - ) - const messagesIncoming = await DeltaBackend.call( - 'messageList.getMessages', - messageIdsIncoming - ) - chatStore.dispatch({ - type: 'FETCHED_INCOMING_MESSAGES', - payload: { - messageIds, - messageIdsIncoming, - messagesIncoming, - }, - }) - } -}) - ipcBackend.on('ClickOnNotification', (ev, { chatId }) => { selectChat(chatId) })