Skip to content

Commit

Permalink
Fix logic for InitialMessages OlderThan
Browse files Browse the repository at this point in the history
- 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!
  • Loading branch information
rottabonus committed May 10, 2024
1 parent c0939e9 commit b50f46d
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 81 deletions.
48 changes: 1 addition & 47 deletions e2e/chatTest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import accountFixtures from './fixtures/accounts.json';
import {
APISignUpMentee,
APISignUpMentor,
APIGetSendInfo,
APISendMessage,
APIDeleteAccounts,
APIDeleteAccount,
waitAndTypeText,
signIn,
forceLogout,
Expand All @@ -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];
Expand Down Expand Up @@ -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,
});
});
});
14 changes: 8 additions & 6 deletions e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,14 @@ export async function APIDeleteAccounts() {
/**
* Makes HTTP API calls to delete user
*/
export async function APIDeleteAccount(id: string, headers: Record<string, string>) {

await fetch(`${API_URL}/accounts/${id}`, {
method: 'DELETE',
headers,
});
export async function APIDeleteAccount(
id: string,
headers: Record<string, string>,
) {
await fetch(`${API_URL}/accounts/${id}`, {
method: 'DELETE',
headers,
});
}

/**
Expand Down
41 changes: 33 additions & 8 deletions src/api/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,41 @@ const sortSentTime = (a: Message, b: Message) => {
return a.sentTime < b.sentTime ? -1 : 1;
};

export const getIsOldestFetchedMessageUnread = (
fetchedBuddyMessages: Record<string, Message>,
) => {
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<PollingParams> => {
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<PollingParams> => {
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<string, Message>
Expand Down
4 changes: 4 additions & 0 deletions src/state/reducers/markSeen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export const reducer: automaton.Reducer<State, actions.Action> = (
type: 'messages/markSeen',
payload: { messages },
})),
// {
// type: 'messages/markSeen',
// payload: { messages },
// },
);
}

Expand Down
38 changes: 18 additions & 20 deletions src/state/reducers/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,32 +93,30 @@ export const reducer: automaton.Reducer<State, actions.Action> = (

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'),
Expand All @@ -129,8 +127,8 @@ export const reducer: automaton.Reducer<State, actions.Action> = (
...state,
messages: RD.success(nextMessages),
previousMsgId,
pollingQueue: nextQueue,
currentParams: nextCurrent,
pollingQueue: [...nextQueue, ...newOlderThanParams],
},
nextCmd,
);
Expand Down Expand Up @@ -181,7 +179,7 @@ export const reducer: automaton.Reducer<State, actions.Action> = (
return state;
}

const [first, ..._rest] = action.payload.messages;
const [first] = action.payload.messages;

if (first.type === 'Sent' || first.isSeen === true) {
return state;
Expand Down

0 comments on commit b50f46d

Please sign in to comment.