diff --git a/src/__tests__/lib/exampleData.js b/src/__tests__/lib/exampleData.js index b064408a4b9..b3f247163d1 100644 --- a/src/__tests__/lib/exampleData.js +++ b/src/__tests__/lib/exampleData.js @@ -317,6 +317,20 @@ export const unicodeEmojiReaction: Reaction = deepFreeze({ emoji_name: 'thumbs_up', }); +export const zulipExtraEmojiReaction: Reaction = deepFreeze({ + user_id: randUserId(), + reaction_type: 'zulip_extra_emoji', + emoji_code: 'zulip', + emoji_name: 'zulip', +}); + +export const realmEmojiReaction: Reaction = deepFreeze({ + user_id: randUserId(), + reaction_type: 'realm_emoji', + emoji_code: '80', + emoji_name: 'github_parrot', +}); + export const displayRecipientFromUser = (user: User): PmRecipientUser => { const { email, full_name, user_id: id } = user; return deepFreeze({ @@ -583,7 +597,19 @@ export const plusReduxState: GlobalState & PerAccountState = reduxState({ lastDismissedServerPushSetupNotice: null, }, ], - realm: { ...baseReduxState.realm, user_id: selfUser.user_id, email: selfUser.email }, + realm: { + ...baseReduxState.realm, + user_id: selfUser.user_id, + email: selfUser.email, + emoji: { + [realmEmojiReaction.emoji_code]: { + deactivated: false, + code: realmEmojiReaction.emoji_code, + name: realmEmojiReaction.emoji_name, + source_url: `/user_avatars/2/emoji/images/${realmEmojiReaction.emoji_code}.gif`, + }, + }, + }, // TODO add crossRealmBot users: [selfUser, otherUser, thirdUser], streams: [stream, otherStream], diff --git a/src/action-sheets/index.js b/src/action-sheets/index.js index 8f75ba558ca..8007eb9740b 100644 --- a/src/action-sheets/index.js +++ b/src/action-sheets/index.js @@ -391,7 +391,7 @@ export const constructPmConversationActionButtons = ({ |}): Button[] => { const buttons = []; - // TODO: If 1:1 PM, give a mute/unmute-user button, with a confirmation + // TODO(#4655): If 1:1 PM, give a mute/unmute-user button, with a confirmation // dialog saying that it also affects the muted users' stream messages, // and linking to https://zulip.com/help/mute-a-user diff --git a/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.android b/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.android index d20f5cb19cd..b33a8a6b01e 100644 --- a/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.android +++ b/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.android @@ -34,6 +34,22 @@ exports[`getEditSequence correct for interesting changes from many messages to d exports[`getEditSequence correct for interesting changes from many messages to empty 1`] = `40`; +exports[`getEditSequence correct for interesting changes within a given message add reactions to a message 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message mute a sender 1`] = `1`; + +exports[`getEditSequence correct for interesting changes within a given message polls choice added 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message polls vote added 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message remove reactions from a message 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message star a message 1`] = `1`; + +exports[`getEditSequence correct for interesting changes within a given message unmute a sender 1`] = `1`; + +exports[`getEditSequence correct for interesting changes within a given message unstar a message 1`] = `1`; + exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_NARROW 1`] = ` "
@@ -1478,6 +1494,498 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible all-pm
" `; +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with a poll 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+
+

Choose a choice:

+
    + +
  • + + Choice A +
  • +
  • + + Choice B +
  • +
+
+
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: collapsed 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: force_collapse 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: force_expand 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: has_alert_word 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: historical 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: is_me_message 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: mentioned 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: read 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: starred 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ +
starred
+ +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: summarize_in_home 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: summarize_in_stream 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: wildcard_mentioned 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with reactions 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + +
 2👍 1 1
+
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) muted sender 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+
+ This message was hidden because it is from a user you have muted. Long-press to view. +
+
" +`; + exports[`messages -> piece descriptors -> content HTML is stable/sensible pm:1,2 1`] = ` "
diff --git a/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios b/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios index d20f5cb19cd..b33a8a6b01e 100644 --- a/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios +++ b/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios @@ -34,6 +34,22 @@ exports[`getEditSequence correct for interesting changes from many messages to d exports[`getEditSequence correct for interesting changes from many messages to empty 1`] = `40`; +exports[`getEditSequence correct for interesting changes within a given message add reactions to a message 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message mute a sender 1`] = `1`; + +exports[`getEditSequence correct for interesting changes within a given message polls choice added 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message polls vote added 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message remove reactions from a message 1`] = `3`; + +exports[`getEditSequence correct for interesting changes within a given message star a message 1`] = `1`; + +exports[`getEditSequence correct for interesting changes within a given message unmute a sender 1`] = `1`; + +exports[`getEditSequence correct for interesting changes within a given message unstar a message 1`] = `1`; + exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_NARROW 1`] = ` "
@@ -1478,6 +1494,498 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible all-pm
" `; +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with a poll 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+
+

Choose a choice:

+
    + +
  • + + Choice A +
  • +
  • + + Choice B +
  • +
+
+
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: collapsed 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: force_collapse 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: force_expand 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: has_alert_word 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: historical 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: is_me_message 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: mentioned 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: read 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: starred 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ +
starred
+ +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: summarize_in_home 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: summarize_in_stream 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with flag: wildcard_mentioned 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message with reactions 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + +
 2👍 1 1
+
+ +
" +`; + +exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) muted sender 1`] = ` +"
+
+ Dec 31, 1969 +
+
+
+ # nonrandom stream +
+
example topic
+
Dec 31, 1969
+
+
+ \\"Nonrandom +
+
+
+
+ Nonrandom name sender User +
+
11:59 PM
+
+

This is an example stream message.

+ + + +
+
+ This message was hidden because it is from a user you have muted. Long-press to view. +
+
" +`; + exports[`messages -> piece descriptors -> content HTML is stable/sensible pm:1,2 1`] = ` "
diff --git a/src/webview/__tests__/generateInboundEventEditSequence-test.js b/src/webview/__tests__/generateInboundEventEditSequence-test.js index 80ad277c3cf..74e1ff1ea64 100644 --- a/src/webview/__tests__/generateInboundEventEditSequence-test.js +++ b/src/webview/__tests__/generateInboundEventEditSequence-test.js @@ -2,6 +2,7 @@ * @jest-environment jsdom * @flow strict-local */ +import Immutable from 'immutable'; import invariant from 'invariant'; import * as eg from '../../__tests__/lib/exampleData'; @@ -13,7 +14,8 @@ import { keyFromNarrow, ALL_PRIVATE_NARROW, } from '../../utils/narrow'; -import type { Message, Outbox } from '../../types'; +import type { Message, Outbox, FlagsState } from '../../types'; +import type { ReadWrite } from '../../generics'; import { getEditSequence } from '../generateInboundEventEditSequence'; import { applyEditSequence } from '../js/handleInboundEvents'; import getMessageListElements from '../../message/getMessageListElements'; @@ -222,13 +224,7 @@ const baseBackgroundData = { * and `messageListElementHtml`. */ describe('messages -> piece descriptors -> content HTML is stable/sensible', () => { - const check = ({ - // TODO: Test with a variety of different things in - // `backgroundData`. - backgroundData = baseBackgroundData, - narrow, - messages, - }) => { + const check = ({ backgroundData = baseBackgroundData, narrow, messages }) => { invariant( messages.every((message, i, allMessages) => { const prevMessage: Message | void = allMessages[i - 1]; @@ -382,6 +378,137 @@ describe('messages -> piece descriptors -> content HTML is stable/sensible', () }, ].forEach(testCase => check(testCase)); }); + + describe('other interesting cases (single messages)', () => { + const stableSelfUser = eg.makeUser({ user_id: 1, name: 'nonrandom name self' }); + const stableOtherUser = eg.makeUser({ user_id: 2, name: 'nonrandom name other' }); + const stableThirdUser = eg.makeUser({ user_id: 3, name: 'nonrandom name third' }); + + const singleMessageSender = eg.makeUser({ user_id: 10, name: 'nonrandom name sender' }); + const baseSingleMessage = eg.streamMessage({ + id: -1, + timestamp: -1, + stream: eg.makeStream({ stream_id: 1, name: 'nonrandom stream' }), + sender: singleMessageSender, + }); + + test('message with reactions', () => { + check({ + narrow: HOME_NARROW, + messages: [ + { + ...baseSingleMessage, + reactions: [ + { ...eg.unicodeEmojiReaction, user_id: stableSelfUser.user_id }, + { ...eg.zulipExtraEmojiReaction, user_id: stableSelfUser.user_id }, + { ...eg.realmEmojiReaction, user_id: stableOtherUser.user_id }, + { ...eg.realmEmojiReaction, user_id: stableThirdUser.user_id }, + ], + }, + ], + }); + }); + + test('message with a poll', () => { + const baseSubmessage = { + message_id: baseSingleMessage.id, + msg_type: 'widget', + }; + + check({ + narrow: HOME_NARROW, + backgroundData: { + ...eg.backgroundData, + ownUser: stableSelfUser, + allUsersById: new Map([ + [singleMessageSender.user_id, singleMessageSender], + [stableSelfUser.user_id, stableSelfUser], + [stableOtherUser.user_id, stableOtherUser], + ]), + }, + messages: [ + { + ...baseSingleMessage, + submessages: [ + { + // poll + ...baseSubmessage, + content: + '{"widget_type": "poll", "extra_data": {"question": "Choose a choice:", "options": []}}', + sender_id: baseSingleMessage.sender_id, + id: 1, + }, + { + // "Choice A" added + ...baseSubmessage, + content: '{"type":"new_option","idx":1,"option":"Choice A"}', + sender_id: baseSingleMessage.sender_id, + id: 2, + }, + { + // Vote for "Choice A" by self + ...baseSubmessage, + content: `{"type":"vote","key":"${baseSingleMessage.sender_id},1","vote":1}`, + sender_id: stableSelfUser.user_id, + id: 3, + }, + { + // Vote for "Choice A" by other + ...baseSubmessage, + content: `{"type":"vote","key":"${baseSingleMessage.sender_id},1","vote":1}`, + sender_id: stableOtherUser.user_id, + id: 4, + }, + { + // Vote for "Choice A" by sender + ...baseSubmessage, + content: `{"type":"vote","key":"${baseSingleMessage.sender_id},1","vote":1}`, + sender_id: baseSingleMessage.sender_id, + id: 5, + }, + { + // "Choice B" added + ...baseSubmessage, + content: '{"type":"new_option","idx":2,"option":"Choice B"}', + sender_id: baseSingleMessage.sender_id, + id: 6, + }, + { + // Vote for "Choice B" by other + ...baseSubmessage, + content: `{"type":"vote","key":"${baseSingleMessage.sender_id},2","vote":1}`, + sender_id: stableOtherUser.user_id, + id: 7, + }, + ], + }, + ], + }); + }); + + Object.keys(eg.baseReduxState.flags).forEach(flag => { + test(`message with flag: ${flag}`, () => { + const flags: ReadWrite = { ...eg.backgroundData.flags }; + flags[flag] = { [baseSingleMessage.id]: true }; + check({ + narrow: HOME_NARROW, + messages: [baseSingleMessage], + backgroundData: { ...eg.backgroundData, flags }, + }); + }); + }); + + test('muted sender', () => { + check({ + narrow: HOME_NARROW, + messages: [baseSingleMessage], + backgroundData: { + ...eg.backgroundData, + mutedUsers: Immutable.Map([[baseSingleMessage.sender_id, 1644366787]]), + }, + }); + }); + }); }); describe('getEditSequence correct for interesting changes', () => { @@ -399,8 +526,16 @@ describe('getEditSequence correct for interesting changes', () => { const check = ( // TODO: Test with a variety of different things in background data - { oldBackgroundData = baseBackgroundData, oldNarrow = HOME_NARROW, oldMessages }, - { newBackgroundData = baseBackgroundData, newNarrow = HOME_NARROW, newMessages }, + { + backgroundData: oldBackgroundData = baseBackgroundData, + narrow: oldNarrow = HOME_NARROW, + messages: oldMessages, + }, + { + backgroundData: newBackgroundData = baseBackgroundData, + narrow: newNarrow = HOME_NARROW, + messages: newMessages, + }, ) => { const oldElements = getMessageListElements(oldMessages, oldNarrow); const newElements = getMessageListElements(newMessages, newNarrow); @@ -459,56 +594,50 @@ describe('getEditSequence correct for interesting changes', () => { describe('from empty', () => { test('to empty', () => { - check({ oldMessages: [] }, { newMessages: [] }); + check({ messages: [] }, { messages: [] }); }); test('to one message', () => { - check({ oldMessages: [] }, { newMessages: [allMessages[0]] }); + check({ messages: [] }, { messages: [allMessages[0]] }); }); test('to many messages', () => { - check({ oldMessages: [] }, { newMessages: allMessages }); + check({ messages: [] }, { messages: allMessages }); }); }); describe('from many messages', () => { test('to empty', () => { - check({ oldMessages: allMessages }, { newMessages: [] }); + check({ messages: allMessages }, { messages: [] }); }); test('to disjoint set of many later messages', () => { check( - { oldMessages: allMessages.slice(0, allMessages.length / 2) }, - { newMessages: allMessages.slice(allMessages.length / 2, allMessages.length) }, + { messages: allMessages.slice(0, allMessages.length / 2) }, + { messages: allMessages.slice(allMessages.length / 2, allMessages.length) }, ); }); test('to disjoint set of many earlier messages', () => { check( - { oldMessages: allMessages.slice(allMessages.length / 2, allMessages.length) }, - { newMessages: allMessages.slice(0, allMessages.length / 2) }, + { messages: allMessages.slice(allMessages.length / 2, allMessages.length) }, + { messages: allMessages.slice(0, allMessages.length / 2) }, ); }); test('insert one message at end', () => { - check( - { oldMessages: allMessages.slice(0, allMessages.length - 1) }, - { newMessages: allMessages }, - ); + check({ messages: allMessages.slice(0, allMessages.length - 1) }, { messages: allMessages }); }); test('delete one message at end', () => { - check( - { oldMessages: allMessages }, - { newMessages: allMessages.slice(0, allMessages.length - 1) }, - ); + check({ messages: allMessages }, { messages: allMessages.slice(0, allMessages.length - 1) }); }); test('replace one message at end with new content', () => { check( - { oldMessages: allMessages }, + { messages: allMessages }, { - newMessages: [ + messages: [ ...allMessages.slice(0, allMessages.length - 1), withContentReplaced(allMessages[allMessages.length - 1]), ], @@ -517,39 +646,27 @@ describe('getEditSequence correct for interesting changes', () => { }); test('insert one message at start', () => { - check( - { oldMessages: allMessages.slice(1, allMessages.length) }, - { newMessages: allMessages }, - ); + check({ messages: allMessages.slice(1, allMessages.length) }, { messages: allMessages }); }); test('delete one message at start', () => { - check( - { oldMessages: allMessages }, - { newMessages: allMessages.slice(1, allMessages.length) }, - ); + check({ messages: allMessages }, { messages: allMessages.slice(1, allMessages.length) }); }); test('replace one message at start with new content', () => { const [firstMessage, ...rest] = allMessages; - check( - { oldMessages: allMessages }, - { newMessages: [withContentReplaced(firstMessage), ...rest] }, - ); + check({ messages: allMessages }, { messages: [withContentReplaced(firstMessage), ...rest] }); }); test('insert many messages at end', () => { - check( - { oldMessages: allMessages.slice(0, allMessages.length / 2) }, - { newMessages: allMessages }, - ); + check({ messages: allMessages.slice(0, allMessages.length / 2) }, { messages: allMessages }); }); test('insert many messages at start', () => { check( - { oldMessages: allMessages.slice(allMessages.length / 2, allMessages.length - 1) }, - { newMessages: allMessages }, + { messages: allMessages.slice(allMessages.length / 2, allMessages.length - 1) }, + { messages: allMessages }, ); }); @@ -557,8 +674,8 @@ describe('getEditSequence correct for interesting changes', () => { const firstThirdIndex = Math.floor(allMessages.length / 3); const secondThirdIndex = Math.floor(allMessages.length * (2 / 3)); check( - { oldMessages: allMessages.slice(firstThirdIndex, secondThirdIndex) }, - { newMessages: allMessages }, + { messages: allMessages.slice(firstThirdIndex, secondThirdIndex) }, + { messages: allMessages }, ); }); @@ -566,9 +683,9 @@ describe('getEditSequence correct for interesting changes', () => { const firstThirdIndex = Math.floor(allMessages.length / 3); const secondThirdIndex = Math.floor(allMessages.length * (2 / 3)); check( - { oldMessages: allMessages }, + { messages: allMessages }, { - newMessages: [ + messages: [ ...allMessages.slice(0, firstThirdIndex), ...allMessages.slice(secondThirdIndex, allMessages.length - 1), ], @@ -579,9 +696,9 @@ describe('getEditSequence correct for interesting changes', () => { test('replace one message in middle with new content', () => { const midIndex = Math.floor(allMessages.length / 2); check( - { oldMessages: allMessages }, + { messages: allMessages }, { - newMessages: [ + messages: [ ...allMessages.slice(0, midIndex), withContentReplaced(allMessages[midIndex]), ...allMessages.slice(midIndex + 1, allMessages.length - 1), @@ -590,4 +707,147 @@ describe('getEditSequence correct for interesting changes', () => { ); }); }); + + describe('within a given message', () => { + test('add reactions to a message', () => { + const message = eg.streamMessage(); + check( + { messages: [message] }, + { messages: [{ ...message, reactions: [eg.unicodeEmojiReaction] }] }, + ); + }); + + test('remove reactions from a message', () => { + const message = eg.streamMessage({ reactions: [eg.unicodeEmojiReaction] }); + check({ messages: [message] }, { messages: [{ ...message, reactions: [] }] }); + }); + + describe('polls', () => { + const baseMessage = eg.streamMessage(); + const baseSubmessage = { + message_id: baseMessage.id, + msg_type: 'widget', + sender_id: baseMessage.sender_id, + }; + const msgWithPoll = { + ...baseMessage, + submessages: [ + { + ...baseSubmessage, + content: + '{"widget_type": "poll", "extra_data": {"question": "Choose a choice:", "options": []}}', + id: 1, + }, + ], + }; + const msgWithChoice = { + ...baseMessage, + submessages: [ + ...msgWithPoll.submessages, + { + ...baseSubmessage, + content: '{"type":"new_option","idx":1,"option":"Choice A"}', + id: 2, + }, + ], + }; + + test('choice added', () => { + check({ messages: [msgWithPoll] }, { messages: [msgWithChoice] }); + }); + + const msgWithVote = { + ...baseMessage, + submessages: [ + ...msgWithChoice.submessages, + { + ...baseSubmessage, + content: `{"type":"vote","key":"${baseMessage.sender_id},1","vote":1}`, + id: 3, + }, + ], + }; + + test('vote added', () => { + check({ messages: [msgWithChoice] }, { messages: [msgWithVote] }); + }); + }); + + test('star a message', () => { + const message = eg.streamMessage(); + check( + { + messages: [message], + backgroundData: { + ...eg.backgroundData, + flags: { ...eg.backgroundData.flags, starred: {} }, + }, + }, + { + messages: [message], + backgroundData: { + ...eg.backgroundData, + flags: { ...eg.backgroundData.flags, starred: { [message.id]: true } }, + }, + }, + ); + }); + + test('unstar a message', () => { + const message = eg.streamMessage(); + check( + { + messages: [message], + backgroundData: { + ...eg.backgroundData, + flags: { ...eg.backgroundData.flags, starred: { [message.id]: true } }, + }, + }, + { + messages: [message], + backgroundData: { + ...eg.backgroundData, + flags: { ...eg.backgroundData.flags, starred: {} }, + }, + }, + ); + }); + + // TODO(#5208): We haven't settled how we want to track name/avatar + test.todo("sender's name/avatar changed"); + + test('mute a sender', () => { + const message = eg.streamMessage(); + check( + { + messages: [message], + backgroundData: { ...eg.backgroundData, mutedUsers: Immutable.Map() }, + }, + { + messages: [message], + backgroundData: { + ...eg.backgroundData, + mutedUsers: Immutable.Map([[message.sender_id, 1644366787]]), + }, + }, + ); + }); + + test('unmute a sender', () => { + const message = eg.streamMessage(); + check( + { + messages: [message], + backgroundData: { + ...eg.backgroundData, + mutedUsers: Immutable.Map([[message.sender_id, 1644366787]]), + }, + }, + { + messages: [message], + backgroundData: { ...eg.backgroundData, mutedUsers: Immutable.Map() }, + }, + ); + }); + }); }); diff --git a/src/webview/generateInboundEventEditSequence.js b/src/webview/generateInboundEventEditSequence.js index d5260d6a95e..4643f3f3611 100644 --- a/src/webview/generateInboundEventEditSequence.js +++ b/src/webview/generateInboundEventEditSequence.js @@ -92,6 +92,8 @@ function doElementsDifferInterestingly( && oldBackgroundData.flags[flagName][oldElement.message.id] !== newBackgroundData.flags[flagName][newElement.message.id], ) + || oldBackgroundData.mutedUsers.get(oldElement.message.sender_id) + !== newBackgroundData.mutedUsers.get(newElement.message.sender_id) ); } default: { diff --git a/src/webview/generateInboundEvents.js b/src/webview/generateInboundEvents.js index a3e328d4ce3..652702d8597 100644 --- a/src/webview/generateInboundEvents.js +++ b/src/webview/generateInboundEvents.js @@ -105,6 +105,8 @@ export default function generateInboundEvents( nextProps.messageListElementsForShownMessages, ) || !equalFlagsExcludingRead(prevProps.backgroundData.flags, nextProps.backgroundData.flags) + // TODO(#4655): Should also update here if backgroundData.mutedUsers + // changes, e.g. because the user muted someone. ) { uevents.push(updateContent(prevProps, nextProps)); }