From 020b672c15279a5e2c7cc96846b1fc2f546ffe42 Mon Sep 17 00:00:00 2001 From: gdbroman <99gustaf@gmail.com> Date: Wed, 28 Jun 2023 15:09:54 +0200 Subject: [PATCH 1/6] Re-sort inbox list on message sent/received --- .../apps/Courier/views/Inbox/Inbox.tsx | 12 ++++++-- app/src/renderer/stores/chat.store.ts | 28 +++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/app/src/renderer/apps/Courier/views/Inbox/Inbox.tsx b/app/src/renderer/apps/Courier/views/Inbox/Inbox.tsx index 63ff04227b..9d17062303 100644 --- a/app/src/renderer/apps/Courier/views/Inbox/Inbox.tsx +++ b/app/src/renderer/apps/Courier/views/Inbox/Inbox.tsx @@ -15,8 +15,14 @@ export const InboxPresenter = ({ isStandaloneChat = false }: Props) => { const { loggedInAccount, shellStore } = useAppState(); const { chatStore, spacesStore } = useShipStore(); - const { sortedChatList, loader, setChat, setSubroute, isChatPinned } = - chatStore; + const { + sortedChatList, + sortedStandaloneChatList, + loader, + setChat, + setSubroute, + isChatPinned, + } = chatStore; const currentSpace = spacesStore.selected; useEffect(() => { @@ -25,7 +31,7 @@ export const InboxPresenter = ({ isStandaloneChat = false }: Props) => { return ( { +export const sortByUpdatedAt = (a: ChatModelType, b: ChatModelType) => { return ( (b.updatedAt || b.metadata.timestamp) - (a.updatedAt || a.metadata.timestamp) @@ -55,6 +55,26 @@ export const ChatStore = types isChatSelected(path: string) { return self.selectedChat?.path === path; }, + // like sortedChatList but we don't apply to space chats - causes jumpiness + get sortedStandaloneChatList() { + return self.inbox.slice().sort((a: ChatModelType, b: ChatModelType) => { + // Check if the chats are pinned + const isAPinned = self.pinnedChats.includes(a.path); + const isBPinned = self.pinnedChats.includes(b.path); + + // Compare the pinned status + if (isAPinned !== isBPinned) { + return isBPinned ? 1 : -1; + } + + // Compare the updatedAt or metadata.timestamp properties + const aTimestamp = + a.lastMessage?.createdAt || a.updatedAt || a.metadata.timestamp; + const bTimestamp = + b.lastMessage?.createdAt || b.updatedAt || b.metadata.timestamp; + return bTimestamp - aTimestamp; + }); + }, get sortedChatList() { const spacesStore: SpacesStoreType = getParentOfType( self, @@ -85,8 +105,10 @@ export const ChatStore = types } // Compare the updatedAt or metadata.timestamp properties - const aTimestamp = a.updatedAt || a.metadata.timestamp; - const bTimestamp = b.updatedAt || b.metadata.timestamp; + const aTimestamp = + a.lastMessage?.createdAt || a.updatedAt || a.metadata.timestamp; + const bTimestamp = + b.lastMessage?.createdAt || b.updatedAt || b.metadata.timestamp; return bTimestamp - aTimestamp; }); }, From 303cd68c22336d85ce096f9f73da080466f7f658 Mon Sep 17 00:00:00 2001 From: gdbroman <99gustaf@gmail.com> Date: Wed, 28 Jun 2023 17:41:22 +0200 Subject: [PATCH 2/6] Fix "loading new messages" state --- app/src/renderer/apps/Courier/app.tsx | 2 +- .../apps/Courier/views/Inbox/Inbox.tsx | 5 ++-- .../StandaloneChat/StandaloneChatBody.tsx | 2 +- app/src/renderer/stores/chat.store.ts | 26 ++++++++++++------- app/src/renderer/stores/ship.store.ts | 6 +++-- .../SystemBar/components/ShipBar/index.tsx | 2 +- 6 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/src/renderer/apps/Courier/app.tsx b/app/src/renderer/apps/Courier/app.tsx index ebedfecd77..aa82926066 100644 --- a/app/src/renderer/apps/Courier/app.tsx +++ b/app/src/renderer/apps/Courier/app.tsx @@ -33,7 +33,7 @@ export const CourierAppPresenter = () => { clearInnerNavigation(); }, [chatStore.subroute]); - if (chatStore.loader.isFirstLoad) { + if (chatStore.inboxInitLoader.isFirstLoad) { return ( { const { sortedChatList, sortedStandaloneChatList, - loader, + inboxLoader, + inboxMetadataLoader, setChat, setSubroute, isChatPinned, @@ -35,7 +36,7 @@ export const InboxPresenter = ({ isStandaloneChat = false }: Props) => { accountIdentity={loggedInAccount?.serverId} spacePath={currentSpace?.path} isStandaloneChat={isStandaloneChat} - isLoading={loader.isLoading} + isLoading={inboxLoader.isLoading || inboxMetadataLoader.isLoading} isChatPinned={isChatPinned} onClickInbox={(path) => { setChat(path); diff --git a/app/src/renderer/apps/StandaloneChat/StandaloneChatBody.tsx b/app/src/renderer/apps/StandaloneChat/StandaloneChatBody.tsx index f4facece15..0556930a21 100644 --- a/app/src/renderer/apps/StandaloneChat/StandaloneChatBody.tsx +++ b/app/src/renderer/apps/StandaloneChat/StandaloneChatBody.tsx @@ -53,7 +53,7 @@ export const StandaloneChatBodyPresenter = () => { } }, [chatStore.subroute, chatStore.selectedChat, chatStore.inbox]); - if (chatStore.loader.isFirstLoad) { + if (chatStore.inboxInitLoader.isFirstLoad) { return ( ({ isChatPinned(path: string) { @@ -153,8 +155,9 @@ export const ChatStore = types })) .actions((self) => ({ init: flow(function* () { + self.inboxInitLoader.set('loading'); + try { - self.loader.set('loading'); const pinnedChats = yield ChatIPC.fetchPinnedChats(); const muted = yield ChatIPC.fetchMuted(); self.inbox = yield ChatIPC.getChatList(); @@ -167,17 +170,19 @@ export const ChatStore = types console.error(error); } - self.loader.set('loaded'); + self.inboxInitLoader.set('loaded'); return self.inbox; }), fetchInboxMetadata: flow(function* () { + self.inboxMetadataLoader.set('loading'); const { muted, pinned } = yield ChatIPC.fetchPathMetadata(); self.pinnedChats = pinned; self.mutedChats = muted; + self.inboxMetadataLoader.set('loaded'); }), loadChatList: flow(function* () { - self.loader.set('loading'); + self.inboxLoader.set('loading'); try { self.inbox = yield ChatIPC.getChatList(); @@ -185,7 +190,7 @@ export const ChatStore = types console.error(error); } - self.loader.set('loaded'); + self.inboxLoader.set('loaded'); }), findChatDM: flow(function* (peer: string, our: string) { // find the DM, if exists, where it's only ourselves and the peer @@ -209,15 +214,17 @@ export const ChatStore = types } self.subroute = subroute; }), - setChat(path: string) { + setChat: flow(function* (path: string) { + self.chatLoader.set('loading'); self.selectedChat = tryReference(() => self.inbox.find((chat) => chat.path === path) ); - ChatIPC.refreshMessagesOnPath(path, window.ship); + yield ChatIPC.refreshMessagesOnPath(path, window.ship); if (self.subroute === 'inbox') { self.subroute = 'chat'; } - }, + self.chatLoader.set('loaded'); + }), togglePinned: flow(function* (path: string, pinned: boolean) { try { if (pinned) { @@ -314,10 +321,9 @@ export const ChatStore = types self.isOpen = false; }, _onInit(payload: any) { - if (self.loader.isFirstLoad) { + if (self.inboxInitLoader.isFirstLoad) { self.inbox = payload; localStorage.setItem(`${window.ship}-firstLoad`, 'true'); - self.loader.set('loaded'); } }, })); diff --git a/app/src/renderer/stores/ship.store.ts b/app/src/renderer/stores/ship.store.ts index 9dc2a6b4b0..e94413a344 100644 --- a/app/src/renderer/stores/ship.store.ts +++ b/app/src/renderer/stores/ship.store.ts @@ -34,7 +34,7 @@ export const ShipStore = types .actions((self) => ({ init(session: RealmSessionCredentials) { if (!localStorage.getItem(`${session.serverId}-firstLoad`)) { - self.chatStore.loader.set('first-load'); + self.chatStore.inboxInitLoader.set('first-load'); } self.credentials = CredentialsModel.create(session); self.friends.init(); @@ -109,8 +109,10 @@ export const shipStore = ShipStore.create({ subroute: 'inbox', isOpen: false, pinnedChats: [], - loader: { state: 'loading' }, inboxLoader: { state: 'initial' }, + inboxInitLoader: { state: 'loading' }, + inboxMetadataLoader: { state: 'loading' }, + chatLoader: { state: 'initial' }, }, spacesStore: { spaces: {}, diff --git a/app/src/renderer/system/desktop/components/SystemBar/components/ShipBar/index.tsx b/app/src/renderer/system/desktop/components/SystemBar/components/ShipBar/index.tsx index b0ff7ebdea..74e2f80e8c 100644 --- a/app/src/renderer/system/desktop/components/SystemBar/components/ShipBar/index.tsx +++ b/app/src/renderer/system/desktop/components/SystemBar/components/ShipBar/index.tsx @@ -227,7 +227,7 @@ export const ShipBarPresenter = () => { }} > - {chatStore.loader.isFirstLoad ? ( + {chatStore.inboxInitLoader.isFirstLoad ? ( Date: Wed, 28 Jun 2023 17:48:19 +0200 Subject: [PATCH 3/6] Add loading feedback to chatlog --- .../apps/Courier/components/ChatLogHeader.tsx | 9 ++++++--- .../apps/Courier/views/ChatLog/ChatLogBody.tsx | 4 ++-- .../Courier/views/ChatLog/ChatLogBodyList.tsx | 16 ++++++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx b/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx index 27b7627790..ecddd0ce9c 100644 --- a/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx +++ b/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { observer } from 'mobx-react'; import styled from 'styled-components'; -import { Button, Flex, Icon } from '@holium/design-system/general'; +import { Button, Flex, Icon, Spinner } from '@holium/design-system/general'; import { Menu, MenuItemProps } from '@holium/design-system/navigation'; import { useAppState } from 'renderer/stores/app.store'; @@ -47,7 +47,7 @@ const ChatLogHeaderPresenter = ({ }: Props) => { const { loggedInAccount, shellStore } = useAppState(); const { chatStore } = useShipStore(); - const { selectedChat, setSubroute, toggleMuted } = chatStore; + const { selectedChat, chatLoader, setSubroute, toggleMuted } = chatStore; const isSpaceChat = selectedChat?.type === 'space'; const chatLogId = useMemo(() => `chat-log-${path}`, [path]); @@ -146,7 +146,10 @@ const ChatLogHeaderPresenter = ({ {rightAction} - {hasMenu && ( + {chatLoader.isLoading && ( + + )} + {hasMenu && !chatLoader.isLoading && ( { const { chatStore } = useShipStore(); - const { selectedChat, setSubroute, inboxLoader } = chatStore; + const { selectedChat, setSubroute, chatLoader } = chatStore; const messages = selectedChat?.messages ?? []; @@ -93,7 +93,7 @@ const ChatLogBodyPresenter = ({ pinnedChatMessage={pinnedChatMessage} isStandaloneChat={isStandaloneChat} isEmpty={messages.length === 0} - isLoaded={inboxLoader.isLoaded} + isLoaded={chatLoader.isLoaded} /> diff --git a/app/src/renderer/apps/Courier/views/ChatLog/ChatLogBodyList.tsx b/app/src/renderer/apps/Courier/views/ChatLog/ChatLogBodyList.tsx index c31d837f1b..7c23f921e5 100644 --- a/app/src/renderer/apps/Courier/views/ChatLog/ChatLogBodyList.tsx +++ b/app/src/renderer/apps/Courier/views/ChatLog/ChatLogBodyList.tsx @@ -34,12 +34,16 @@ export const ChatLogBodyList = ({ isStandaloneChat, pinnedChatMessage, }: Props) => { - if (isEmpty && isLoaded) { - return ( - - You haven't sent or received any messages in this chat yet. - - ); + if (isEmpty) { + if (isLoaded) { + return ( + + You haven't sent or received any messages in this chat yet. + + ); + } + + return null; } return ( From 99daac8575631e3b02c9044a137e5fbebd59783b Mon Sep 17 00:00:00 2001 From: gdbroman <99gustaf@gmail.com> Date: Wed, 28 Jun 2023 20:03:09 +0200 Subject: [PATCH 4/6] Fix pinned/muted chats so they're instantly shown --- .../apps/Courier/views/Inbox/InboxBody.tsx | 2 +- .../views/Inbox/InboxBodyLoadingHeader.tsx | 2 +- app/src/renderer/stores/chat.store.ts | 21 ++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/src/renderer/apps/Courier/views/Inbox/InboxBody.tsx b/app/src/renderer/apps/Courier/views/Inbox/InboxBody.tsx index 90cc0e5484..200d7f03e8 100644 --- a/app/src/renderer/apps/Courier/views/Inbox/InboxBody.tsx +++ b/app/src/renderer/apps/Courier/views/Inbox/InboxBody.tsx @@ -85,7 +85,7 @@ export const InboxBody = ({ - {isLoading && } + {isLoading && isStandaloneChat && } ( transition={{ duration: 0.2, ease: 'easeInOut' }} > - Loading new messages... + Syncing messages... ); diff --git a/app/src/renderer/stores/chat.store.ts b/app/src/renderer/stores/chat.store.ts index 3d356f02d8..7028a6058a 100644 --- a/app/src/renderer/stores/chat.store.ts +++ b/app/src/renderer/stores/chat.store.ts @@ -158,14 +158,11 @@ export const ChatStore = types self.inboxInitLoader.set('loading'); try { - const pinnedChats = yield ChatIPC.fetchPinnedChats(); + const pinned = yield ChatIPC.fetchPinnedChats(); const muted = yield ChatIPC.fetchMuted(); self.inbox = yield ChatIPC.getChatList(); - self.inbox.forEach((chat) => { - chat.setMuted(muted.includes(chat.path)); - chat.setPinned(pinnedChats.includes(chat.path)); - }); - self.pinnedChats = pinnedChats; + self.mutedChats = muted; + self.pinnedChats = pinned; } catch (error) { console.error(error); } @@ -185,7 +182,17 @@ export const ChatStore = types self.inboxLoader.set('loading'); try { - self.inbox = yield ChatIPC.getChatList(); + const initialInbox = yield ChatIPC.getChatList(); + const pins = initialInbox + .filter((chat: any) => chat.pinned) + .map((chat: any) => chat.path); + const mutes = initialInbox + .filter((chat: any) => chat.muted) + .map((chat: any) => chat.path); + + self.inbox = initialInbox; + self.pinnedChats = pins; + self.mutedChats = mutes; } catch (error) { console.error(error); } From b9c11974ad4cd6c5f4dc01d9aded085816a06e2d Mon Sep 17 00:00:00 2001 From: gdbroman <99gustaf@gmail.com> Date: Thu, 29 Jun 2023 12:39:21 +0200 Subject: [PATCH 5/6] Display spinner next to three dots --- app/src/renderer/apps/Courier/components/ChatLogHeader.tsx | 7 ++----- .../apps/StandaloneChat/StandaloneChatPassport.tsx | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx b/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx index ecddd0ce9c..40f429e320 100644 --- a/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx +++ b/app/src/renderer/apps/Courier/components/ChatLogHeader.tsx @@ -30,7 +30,6 @@ type Props = { path: string; isMuted: boolean; hasMenu: boolean; - rightAction?: React.ReactNode; forceBackButton?: boolean; isStandaloneChat?: boolean; onBack: () => void; @@ -38,7 +37,6 @@ type Props = { const ChatLogHeaderPresenter = ({ path, - rightAction, isMuted, hasMenu = true, forceBackButton = false, @@ -144,12 +142,11 @@ const ChatLogHeaderPresenter = ({ )} - - {rightAction} + {chatLoader.isLoading && ( )} - {hasMenu && !chatLoader.isLoading && ( + {hasMenu && ( { > Date: Thu, 29 Jun 2023 12:45:34 +0200 Subject: [PATCH 6/6] New pinned inbox UI: icon instead of background --- .../Courier/components/ChatRow/ChatRow.tsx | 24 +++++++++++-------- .../Courier/views/Inbox/InboxRow.styles.tsx | 10 +------- .../apps/Courier/views/Inbox/InboxRow.tsx | 8 +------ 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/app/src/renderer/apps/Courier/components/ChatRow/ChatRow.tsx b/app/src/renderer/apps/Courier/components/ChatRow/ChatRow.tsx index bff00fbcc8..42a81f17e8 100644 --- a/app/src/renderer/apps/Courier/components/ChatRow/ChatRow.tsx +++ b/app/src/renderer/apps/Courier/components/ChatRow/ChatRow.tsx @@ -20,7 +20,6 @@ type ChatRowProps = { title: string; peers: string[]; isAdmin: boolean; - muted: boolean; metadata: any; timestamp: number; type: ChatPathType; @@ -230,14 +229,7 @@ export const ChatRowPresenter = ({ > {lastMessageTimestamp} - {isMuted && ( - - )} + {unreadCount > 0 && } {lastMessageUpdated} - + + {isMuted && ( + + )} + {isPinned && ( + + )} + diff --git a/app/src/renderer/apps/Courier/views/Inbox/InboxRow.styles.tsx b/app/src/renderer/apps/Courier/views/Inbox/InboxRow.styles.tsx index f697486459..bd508b6a2a 100644 --- a/app/src/renderer/apps/Courier/views/Inbox/InboxRow.styles.tsx +++ b/app/src/renderer/apps/Courier/views/Inbox/InboxRow.styles.tsx @@ -16,18 +16,10 @@ export const StyledInboxRowContainer = styled(Box)<{ `} `; -export const StyledInboxRow = styled(Box)<{ - isPinned: boolean; -}>` +export const StyledInboxRow = styled(Box)` width: 100%; min-width: 0; align-items: center; z-index: 2; border-radius: 6px; - - ${({ isPinned }) => - isPinned && - ` - background: var(--rlm-overlay-hover-color); - `} `; diff --git a/app/src/renderer/apps/Courier/views/Inbox/InboxRow.tsx b/app/src/renderer/apps/Courier/views/Inbox/InboxRow.tsx index bec70a3fb5..9462fe141e 100644 --- a/app/src/renderer/apps/Courier/views/Inbox/InboxRow.tsx +++ b/app/src/renderer/apps/Courier/views/Inbox/InboxRow.tsx @@ -39,12 +39,7 @@ export const InboxRow = ({ isSelectedSpaceChat={isSelectedSpaceChat} className={className} > - + { evt.stopPropagation();