Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHAT-1565] Sync own_reactions in events #606

Merged
merged 5 commits into from
Feb 16, 2021
Merged
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
8 changes: 4 additions & 4 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1624,19 +1624,19 @@ export class Channel<
}
break;
case 'reaction.new':
if (event.reaction) {
channelState.addReaction(event.reaction, event.message);
if (event.message && event.reaction) {
event.message = channelState.addReaction(event.reaction, event.message);
}
break;
case 'reaction.deleted':
if (event.reaction) {
channelState.removeReaction(event.reaction, event.message);
event.message = channelState.removeReaction(event.reaction, event.message);
}
break;
case 'reaction.updated':
if (event.reaction) {
// assuming reaction.updated is only called if enforce_unique is true
channelState.addReaction(event.reaction, event.message, true);
event.message = channelState.addReaction(event.reaction, event.message, true);
ferhatelmas marked this conversation as resolved.
Show resolved Hide resolved
}
break;
case 'channel.hidden':
Expand Down
187 changes: 71 additions & 116 deletions src/channel_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,46 +319,17 @@ export class ChannelState<
>,
enforce_unique?: boolean,
) {
const { messages } = this;
if (!message) return;
const { parent_id, show_in_channel } = message;

if (parent_id && this.threads[parent_id]) {
const thread = this.threads[parent_id];

for (let i = 0; i < thread.length; i++) {
const msg = thread[i];
const messageWithReaction = this._addReactionToMessage(
msg,
reaction,
enforce_unique,
);
if (!messageWithReaction) {
continue;
}

thread[i] = messageWithReaction;
this.threads[parent_id] = thread;
break;
}
}

if ((!show_in_channel && !parent_id) || show_in_channel) {
for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
const messageWithReaction = this._addReactionToMessage(
msg,
reaction,
enforce_unique,
);
if (!messageWithReaction) {
continue;
}

this.messages[i] = messageWithReaction;
break;
}
}
let messageWithReaction = message;
this._updateMessage(message, (msg) => {
const newMsg = this._addReactionToMessage(msg, reaction, enforce_unique);
messageWithReaction = {
...messageWithReaction,
own_reactions: newMsg.own_reactions,
};
return newMsg;
});
return messageWithReaction;
}

_addReactionToMessage(
Expand All @@ -376,33 +347,22 @@ export class ChannelState<
reaction: ReactionResponse<ReactionType, UserType>,
enforce_unique?: boolean,
) {
const idMatch = !!message.id && message.id === reaction.message_id;

if (!idMatch) {
return false;
const newMessage = message;
if (enforce_unique) {
newMessage.own_reactions = [];
} else {
this._removeOwnReactionFromMessage(message, reaction);
}

const newMessage = this._removeReactionFromMessage(message, reaction, enforce_unique);
if (this._channel.getClient().userID === reaction.user?.id) {
const ownReactions = newMessage.own_reactions;
if (ownReactions) {
newMessage.own_reactions = [...ownReactions, reaction];
}
newMessage.own_reactions = newMessage.own_reactions || [];
if (this._channel.getClient().userID === reaction.user_id) {
newMessage.own_reactions.push(reaction);
}
const latestReactions = newMessage.latest_reactions;
if (latestReactions) {
newMessage.latest_reactions = [...latestReactions, reaction];
}

const oldReactionCounts = newMessage.reaction_counts || {};
const oldReactionType = oldReactionCounts[reaction.type];
oldReactionCounts[reaction.type] = oldReactionType ? oldReactionType + 1 : 1;
newMessage.reaction_counts = oldReactionCounts;

return newMessage;
}

_removeReactionFromMessage(
_removeOwnReactionFromMessage(
message: ReturnType<
ChannelState<
AttachmentType,
Expand All @@ -415,32 +375,12 @@ export class ChannelState<
>['formatMessage']
>,
reaction: ReactionResponse<ReactionType, UserType>,
enforce_unique?: boolean,
) {
const filterReaction = (old: ReactionResponse<ReactionType, UserType>[]) =>
old.filter((item) =>
enforce_unique
? item.user?.id !== reaction.user?.id
: item.type !== reaction.type || item.user?.id !== reaction.user?.id,
);
if (message.own_reactions) {
message.own_reactions = filterReaction(message.own_reactions);
}
if (message.latest_reactions) {
message.latest_reactions = filterReaction(message.latest_reactions);
}
if (enforce_unique) {
const oldReaction = message.own_reactions?.find(
({ type }) => type === reaction.type,
message.own_reactions = message.own_reactions.filter(
(item) => item.user_id !== reaction.user_id || item.type !== reaction.type,
);
if (oldReaction && message.reaction_counts) {
const oldReactionCount = message.reaction_counts[oldReaction.type];
message.reaction_counts[oldReaction.type] = oldReactionCount
? oldReactionCount - 1
: 0;
}
}
return message;
}

// eslint-disable-next-line sonarjs/cognitive-complexity
Expand All @@ -455,50 +395,65 @@ export class ChannelState<
UserType
>,
) {
const { messages } = this;
if (!message) return;
let messageWithReaction = message;
this._updateMessage(message, (msg) => {
this._removeOwnReactionFromMessage(msg, reaction);
messageWithReaction = {
...messageWithReaction,
own_reactions: msg.own_reactions,
};
return msg;
});
return messageWithReaction;
}

/**
* Updates all instances of given message in channel state
* @param message
* @param updateFunc
*/
_updateMessage(
message: { id?: string; parent_id?: string; show_in_channel?: boolean },
updateFunc: (
msg: ReturnType<
ChannelState<
AttachmentType,
ChannelType,
CommandType,
EventType,
MessageType,
ReactionType,
UserType
>['formatMessage']
>,
) => ReturnType<
ChannelState<
AttachmentType,
ChannelType,
CommandType,
EventType,
MessageType,
ReactionType,
UserType
>['formatMessage']
>,
) {
const { parent_id, show_in_channel } = message;

if (parent_id && this.threads[parent_id]) {
const thread = this.threads[parent_id];
for (let i = 0; i < thread.length; i++) {
const msg = thread[i];
const idMatch = !!msg.id && msg.id === reaction.message_id;

if (!idMatch) {
continue;
}
const messageWithReaction = this._removeReactionFromMessage(msg, reaction);
const oldReactionCounts = messageWithReaction.reaction_counts;
if (oldReactionCounts) {
const oldReactionType = oldReactionCounts[reaction.type];
oldReactionCounts[reaction.type] = oldReactionType ? oldReactionType - 1 : 0;
messageWithReaction.reaction_counts = oldReactionCounts;
}

thread[i] = messageWithReaction;
const msgIndex = thread.findIndex((msg) => msg.id === message.id);
if (msgIndex !== -1) {
thread[msgIndex] = updateFunc(thread[msgIndex]);
this.threads[parent_id] = thread;
break;
}
}

if ((!show_in_channel && !parent_id) || show_in_channel) {
for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
const idMatch = !!msg.id && msg.id === reaction.message_id;

if (!idMatch) {
continue;
}
const messageWithReaction = this._removeReactionFromMessage(msg, reaction);
const oldReactionCounts = messageWithReaction.reaction_counts;
if (oldReactionCounts) {
const oldReactionType = oldReactionCounts[reaction.type];
oldReactionCounts[reaction.type] = oldReactionType ? oldReactionType - 1 : 0;
messageWithReaction.reaction_counts = oldReactionCounts;
}

this.messages[i] = messageWithReaction;
break;
const msgIndex = this.messages.findIndex((msg) => msg.id === message.id);
if (msgIndex !== -1) {
this.messages[msgIndex] = updateFunc(this.messages[msgIndex]);
}
}
}
Expand Down
63 changes: 62 additions & 1 deletion test/unit/channel_state.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import chai from 'chai';
import { ChannelState } from '../../src/channel_state';
import { ChannelState, StreamChat, Channel } from '../../src';
import { generateMsg } from './test-utils/generateMessage';

const expect = chai.expect;
Expand Down Expand Up @@ -252,3 +252,64 @@ describe('ChannelState addMessagesSorted', function () {
expect(state.pinnedMessages[2].id).to.be.equal('2');
});
});

describe('ChannelState reactions', () => {
const message = generateMsg();
let state;
beforeEach(() => {
const client = new StreamChat();
client.userID = 'observer';
state = new ChannelState(new Channel(client, 'live', 'stream', {}));
state.addMessageSorted(message);
});
it('Add one reaction', () => {
const newMessage = state.addReaction(
{
user_id: 'observer',
type: 'like',
score: 1,
},
message,
);
expect(newMessage.own_reactions.length).to.be.eq(1);
});
it('Add same reaction twice', () => {
let newMessage = state.addReaction(
{
user_id: 'observer',
type: 'like',
score: 1,
},
message,
);
newMessage = state.addReaction(
{
user_id: 'observer',
type: 'like',
score: 1,
},
newMessage,
);
expect(newMessage.own_reactions.length).to.be.eq(1);
});
it('Add two reactions', () => {
let newMessage = state.addReaction(
{
user_id: 'observer',
type: 'like',
score: 1,
},
message,
);
newMessage = state.addReaction(
{
user_id: 'user2',
type: 'like',
score: 4,
},
newMessage,
);
expect(newMessage.own_reactions.length).to.be.eq(1);
expect(newMessage.own_reactions[0].user_id).to.be.eq('observer');
});
});