From b50f46d12296e96c75bb3991c0fb33202fc859b7 Mon Sep 17 00:00:00 2001 From: Felix Hallenberg Date: Sat, 4 May 2024 11:15:27 +0300 Subject: [PATCH] Fix logic for InitialMessages OlderThan - It is possible that InitialMessages should spawn new requests for fetching messages. - For example Mr. A has sent Mr. B 23 messages. If B fetches initially 10, it should also fetch the 13 unread! --- e2e/chatTest.spec.ts | 48 +--------------------------------- e2e/helpers.ts | 14 +++++----- src/api/messages.ts | 41 +++++++++++++++++++++++------ src/state/reducers/markSeen.ts | 4 +++ src/state/reducers/messages.ts | 38 +++++++++++++-------------- 5 files changed, 64 insertions(+), 81 deletions(-) diff --git a/e2e/chatTest.spec.ts b/e2e/chatTest.spec.ts index 499291e7..098790af 100644 --- a/e2e/chatTest.spec.ts +++ b/e2e/chatTest.spec.ts @@ -5,10 +5,7 @@ import accountFixtures from './fixtures/accounts.json'; import { APISignUpMentee, APISignUpMentor, - APIGetSendInfo, - APISendMessage, APIDeleteAccounts, - APIDeleteAccount, waitAndTypeText, signIn, forceLogout, @@ -24,7 +21,7 @@ describe('Chat', () => { await device.reloadReactNative(); }); - xit('with new mentor', async () => { + it('with new mentor', async () => { const mentee = accountFixtures.mentees[0]; await APISignUpMentee(mentee); const mentor = accountFixtures.mentors[0]; @@ -62,47 +59,4 @@ describe('Chat', () => { await expect(element(by.text(message_from_mentee))).toBeVisible(); await expect(element(by.text(message_from_mentor))).toBeVisible(); }); - - it('if buddy with most recent message deletes account, can receive still messages from other users', async () => { - const mentee = accountFixtures.mentees[0]; - await APISignUpMentee(mentee); - const mentor = accountFixtures.mentors[0]; - await APISignUpMentor(mentor); - - // mentee sends a msg to mentor - const { - sender_id: menteeId, - sender_info: mentee_info, - recipient_id: mentorId, - senderHeaders: menteeHeaders, - } = await APIGetSendInfo(mentee, mentor); - await APISendMessage({ - sender_id: menteeId, - recipient_id: mentorId, - content: 'Hi first', - headers: menteeHeaders, - }); - - await signIn(mentor); - await element(by.id('tabs.chats')).tap(); - await element(by.text(mentee.displayName)).tap(); - await element(by.id('chat.back.button')).tap(); - - // delete mentee account - await APIDeleteAccount(mentee_info.account_id, menteeHeaders) - - // new mentee - const newMentee = accountFixtures.mentees[1]; - await APISignUpMentee(newMentee); - const { - sender_id: newMenteeId, - senderHeaders: newMenteeHeaders, - } = await APIGetSendInfo(newMentee, mentor); - await APISendMessage({ - sender_id: newMenteeId, - recipient_id: mentorId, - content: 'Hi second', - headers: newMenteeHeaders, - }); - }); }); diff --git a/e2e/helpers.ts b/e2e/helpers.ts index 3f548fcd..86501eee 100644 --- a/e2e/helpers.ts +++ b/e2e/helpers.ts @@ -211,12 +211,14 @@ export async function APIDeleteAccounts() { /** * Makes HTTP API calls to delete user */ -export async function APIDeleteAccount(id: string, headers: Record) { - - await fetch(`${API_URL}/accounts/${id}`, { - method: 'DELETE', - headers, - }); +export async function APIDeleteAccount( + id: string, + headers: Record, +) { + await fetch(`${API_URL}/accounts/${id}`, { + method: 'DELETE', + headers, + }); } /** diff --git a/src/api/messages.ts b/src/api/messages.ts index 4c53b973..968a23a9 100644 --- a/src/api/messages.ts +++ b/src/api/messages.ts @@ -205,16 +205,41 @@ const sortSentTime = (a: Message, b: Message) => { return a.sentTime < b.sentTime ? -1 : 1; }; -export const getIsOldestFetchedMessageUnread = ( - fetchedBuddyMessages: Record, -) => { - const sorted = Object.keys(fetchedBuddyMessages) - .map(msgId => fetchedBuddyMessages[msgId]) - .sort(sortSentTime); - - return !sorted[sorted.length - 1].isSeen; +export const getParamsForUnreadMessages = ( + messages: MessageMapping, + params: PollingParams, +): Array => { + switch (params.type) { + case 'OlderThan': { + return getOlderThanParamsIfOldestUnread(messages)(params.buddyId); + } + + case 'InitialMessages': { + return params.buddyIds.flatMap( + getOlderThanParamsIfOldestUnread(messages), + ); + } + + default: { + return []; + } + } }; +export const getOlderThanParamsIfOldestUnread = + (messages: MessageMapping) => + (buddyId: string): Array => { + const sorted = Object.keys(messages[buddyId]) + .map(msgId => messages[buddyId][msgId]) + .sort(sortSentTime); + const oldest = sorted.length > 0 ? sorted[sorted.length - 1] : null; + const isOldestUnseen = oldest && !oldest.isSeen; + + return isOldestUnseen + ? [{ type: 'OlderThan', buddyId, messageId: oldest.messageId }] + : []; + }; + export const extractMostRecentId = (messages: MessageMapping) => { const flattenedMessages = Object.keys(messages).reduce< Record diff --git a/src/state/reducers/markSeen.ts b/src/state/reducers/markSeen.ts index bbe04529..4e17d135 100644 --- a/src/state/reducers/markSeen.ts +++ b/src/state/reducers/markSeen.ts @@ -41,6 +41,10 @@ export const reducer: automaton.Reducer = ( type: 'messages/markSeen', payload: { messages }, })), + // { + // type: 'messages/markSeen', + // payload: { messages }, + // }, ); } diff --git a/src/state/reducers/messages.ts b/src/state/reducers/messages.ts index cb3b7491..97e7ba5b 100644 --- a/src/state/reducers/messages.ts +++ b/src/state/reducers/messages.ts @@ -93,32 +93,30 @@ export const reducer: automaton.Reducer = ( const previousMsgId = messageApi.extractMostRecentId(nextMessages); - const isFetchingOlderMessagesAndIsOldestFetchedUnread = - state.currentParams.type === 'OlderThan' - ? messageApi.getIsOldestFetchedMessageUnread( - newMessages[state.currentParams.buddyId], - ) - : false; - - const [first, rest] = messageApi.getNextParams( + // TODO: add some e2e-test + // case 1: oldermessage-fetching (for example initial) + // - mentee sends 20 messages to mentor + // - mentor login + // - mentor waits 5 seconds (the poll interval) + // - mentor scrolls up to the converstaion ( all messages have been loaded ) + // - mentor opens chat + // case 2: marking unread message + // case 3: marking unread only for messages that are seen 100% + const newOlderThanParams = messageApi.getParamsForUnreadMessages( + newMessages, + state.currentParams, + ); + + const [nextCurrent, nextQueue] = messageApi.getNextParams( action.payload, state.pollingQueue, state.currentParams, previousMsgId, ); - const { nextFetch, nextCurrent, nextQueue } = - isFetchingOlderMessagesAndIsOldestFetchedUnread - ? { - nextCurrent: state.currentParams, - nextFetch: state.currentParams, - nextQueue: [first, ...rest], - } - : { nextCurrent: first, nextFetch: first, nextQueue: rest }; - const nextCmd = withToken( flow( - messageApi.fetchMessages(nextFetch), + messageApi.fetchMessages(nextCurrent), T.delay(config.messageFetchDelay), ), actions.make('messages/get/completed'), @@ -129,8 +127,8 @@ export const reducer: automaton.Reducer = ( ...state, messages: RD.success(nextMessages), previousMsgId, - pollingQueue: nextQueue, currentParams: nextCurrent, + pollingQueue: [...nextQueue, ...newOlderThanParams], }, nextCmd, ); @@ -181,7 +179,7 @@ export const reducer: automaton.Reducer = ( return state; } - const [first, ..._rest] = action.payload.messages; + const [first] = action.payload.messages; if (first.type === 'Sent' || first.isSeen === true) { return state;