Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/webapp/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"executor": "nx:run-commands",
"dependsOn": ["webapp:stylelint", "webapp:type-check"],
"options": {
"command": "eslint --max-warnings=1030 --ext .js,.ts,.tsx {projectRoot}"
"command": "eslint --max-warnings=959 --ext .js,.ts,.tsx {projectRoot}"
}
},
"stylelint": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {MutableRefObject, useCallback, useEffect, useLayoutEffect, useMemo, useR
import {useVirtualizer} from '@tanstack/react-virtual';
import cx from 'classnames';

import {FireAndForgetInvoker} from '@wireapp/core';

import {MarkerComponent} from 'Components/MessagesList/Message/Marker';
import {Message} from 'Components/MessagesList/Message/VirtualizedMessage';
import {MessagesListParams} from 'Components/MessagesList/MessageList.types';
Expand All @@ -34,6 +36,9 @@ import {groupMessagesBySenderAndTime, isMarker} from 'Components/MessagesList/ut
import {useLoadMessages} from 'Components/MessagesList/VirtualizedMessagesList/useLoadMessages';
import {useScrollMessages} from 'Components/MessagesList/VirtualizedMessagesList/useScrollMessages';
import {useRoveFocus} from 'Hooks/useRoveFocus';
import {MessageRepository} from 'Repositories/conversation/MessageRepository';
import {Conversation} from 'Repositories/entity/Conversation';
import {useApplicationContext} from 'src/script/page/RootProvider';
import {useKoSubscribableChildren} from 'Util/componentUtil';

import {VirtualizedJumpToLastMessageButton} from '../VirtualizedJumpToLastMessageButton';
Expand All @@ -47,6 +52,30 @@ interface Props extends Omit<MessagesListParams, 'isRightSidebarOpen' | 'onLoadi
onLoading: (isLoading: boolean) => void;
}

type LoadMessagesForTimestampSelectionOptions = {
conversation: Conversation;
conversationRepository: Props['conversationRepository'];
fireAndForgetInvoker: FireAndForgetInvoker;
messageId: string;
messageRepository: MessageRepository;
};

async function loadMessagesForTimestampSelection(options: LoadMessagesForTimestampSelectionOptions): Promise<void> {
const {conversation, conversationRepository, fireAndForgetInvoker, messageId, messageRepository} = options;

const messageIsLoaded = conversation.getMessage(messageId);

if (messageIsLoaded) {
return;
}

const messageEntity = await messageRepository.getMessageInConversationById(conversation, messageId);
conversation.removeMessages();
fireAndForgetInvoker.fireAndForget(async (): Promise<void> => {
await conversationRepository.getMessagesWithOffset(conversation, messageEntity);
});
}

export const VirtualizedMessagesList = ({
conversation,
parentElement,
Expand All @@ -72,6 +101,7 @@ export const VirtualizedMessagesList = ({
onLoading,
isConversationLoaded,
}: Props) => {
const {fireAndForgetInvoker} = useApplicationContext();
const {
messages: allMessages,
lastDeliveredMessage,
Expand Down Expand Up @@ -182,7 +212,7 @@ export const VirtualizedMessagesList = ({

const scrolledToHighlightedMessage = useRef(false);

const onTimestampClick = async (messageId: string) => {
const onTimestampClick = async (messageId: string): Promise<void> => {
scrolledToHighlightedMessage.current = false;
setHighlightedMessage(messageId);

Expand All @@ -192,13 +222,13 @@ export const VirtualizedMessagesList = ({
clearTimeout(clearHighlightedMessage);
}, 5000);

const messageIsLoaded = conversation.getMessage(messageId);

if (!messageIsLoaded) {
const messageEntity = await messageRepository.getMessageInConversationById(conversation, messageId);
conversation.removeMessages();
void conversationRepository.getMessagesWithOffset(conversation, messageEntity);
}
await loadMessagesForTimestampSelection({
conversation,
conversationRepository,
fireAndForgetInvoker,
messageId,
messageRepository,
});
};

useLayoutEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,25 @@ import {Virtualizer} from '@tanstack/react-virtual';

import {ConversationRepository} from 'Repositories/conversation/ConversationRepository';
import {Conversation} from 'Repositories/entity/Conversation';
import {useApplicationContext} from 'src/script/page/RootProvider';
import {isLastReceivedMessage} from 'Util/conversationMessages';

interface Props {
type UseLoadMessagesProps = {
conversation: Conversation;
conversationRepository: ConversationRepository;
itemsLength: number;
shouldPullMessages: boolean;
isConversationLoaded: boolean;
parentElement: HTMLElement;
}
};

export const useLoadMessages = (
virtualizer: Virtualizer<HTMLDivElement, Element>,
{conversation, conversationRepository, itemsLength, shouldPullMessages, isConversationLoaded, parentElement}: Props,
) => {
properties: UseLoadMessagesProps,
): void => {
const {conversation, conversationRepository, itemsLength, shouldPullMessages, isConversationLoaded, parentElement} =
properties;
const {fireAndForgetInvoker} = useApplicationContext();
const fillContainerByMessagesRef = useRef(false);
const [isLoadingMessages, setIsLoadingMessages] = useState(false);

Expand Down Expand Up @@ -103,12 +107,12 @@ export const useLoadMessages = (
const [firstItem] = [...virtualItems];

if (firstItem.index === 0) {
void loadPrecedingMessages();
fireAndForgetInvoker.fireAndForget(loadPrecedingMessages);
}
}, 100);

return () => clearTimeout(timeout);
}, [isConversationLoaded, isLoadingMessages, loadPrecedingMessages, virtualItems]);
}, [fireAndForgetInvoker, isConversationLoaded, isLoadingMessages, loadPrecedingMessages, virtualItems]);

// Load new messages when scrolling to the down
useEffect(() => {
Expand All @@ -127,12 +131,20 @@ export const useLoadMessages = (
const [lastItem] = [...virtualItems].reverse();

if (lastItem.index >= itemsLength - 1) {
void loadFollowingMessages();
fireAndForgetInvoker.fireAndForget(loadFollowingMessages);
}
}, 100);

return () => clearTimeout(timeout);
}, [isConversationLoaded, isLoadingMessages, itemsLength, loadFollowingMessages, virtualizer, virtualItems]);
}, [
fireAndForgetInvoker,
isConversationLoaded,
isLoadingMessages,
itemsLength,
loadFollowingMessages,
virtualizer,
virtualItems,
]);

// This function ensures that after user scroll to top or bottom content,
// the preceding / following messages will be loaded.
Expand All @@ -148,12 +160,12 @@ export const useLoadMessages = (
const containerHeight = virtualizer.scrollElement?.clientHeight ?? 0;

if (totalSize < containerHeight) {
void loadPrecedingMessages();
fireAndForgetInvoker.fireAndForget(loadPrecedingMessages);
}

fillContainerByMessagesRef.current = true;
});

return () => cancelAnimationFrame(frame);
}, [itemsLength, loadPrecedingMessages, virtualizer]);
}, [fireAndForgetInvoker, itemsLength, loadPrecedingMessages, virtualizer]);
};
2 changes: 2 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ const config: Linter.Config[] = [
'apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/MultipartAssets.tsx',
'apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/useGetMultipartAsset/useGetMultipartAsset.ts',
'apps/webapp/src/script/components/MessagesList/Message/MessageWrapper.tsx',
'apps/webapp/src/script/components/MessagesList/VirtualizedMessagesList/VirtualizedMessagesList.tsx',
'apps/webapp/src/script/components/MessagesList/VirtualizedMessagesList/useLoadMessages.ts',
'apps/webapp/src/script/components/MessagesList/utils/useLoadConversation.ts',
'apps/webapp/src/script/components/Modals/FileHistoryModal/FileVersionItem.tsx',
'apps/webapp/src/script/components/Modals/FileHistoryModal/hooks/useFileVersions.ts',
Expand Down
Loading