Skip to content

Commit b40e78f

Browse files
refactor: code splitting
1 parent e8e0f72 commit b40e78f

File tree

1 file changed

+66
-61
lines changed

1 file changed

+66
-61
lines changed

app/containers/message/index.tsx

Lines changed: 66 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { Alert, Keyboard } from 'react-native';
3+
import { dequal } from 'dequal';
34

45
import Message from './Message';
56
import MessageContext from './Context';
@@ -120,11 +121,11 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
120121
} = this.props;
121122

122123
// optimistic UI updates
123-
if (nextState.proxyReactions !== proxyReactions) {
124+
if (!dequal(nextState.proxyReactions, proxyReactions)) {
124125
return true;
125126
}
126127

127-
if (nextProps.item.reactions !== item.reactions) {
128+
if (!dequal(nextProps.item.reactions, item.reactions)) {
128129
return true;
129130
}
130131
if (nextProps.showUnreadSeparator !== showUnreadSeparator) {
@@ -221,74 +222,78 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
221222
}
222223
};
223224

224-
onReactionPress = async (emoji: string) => {
225-
const { onReactionPress, item, user } = this.props;
226-
const { username } = user;
225+
// proxy reaction utility functions
226+
private removeUserFromReaction = (reaction: IReaction, username: string): IReaction | null => {
227+
const usernames = reaction.usernames.filter(u => u !== username);
228+
const names = usernames;
227229

228-
if (!onReactionPress) {
229-
return;
230+
if (usernames.length === 0) return null; // remove entirely
231+
232+
return { ...reaction, usernames, names };
233+
};
234+
235+
private addUserToReaction = (reaction: IReaction, username: string): IReaction => ({
236+
...reaction,
237+
usernames: [...reaction.usernames, username],
238+
names: [...reaction.usernames, username]
239+
});
240+
241+
private toggleReactionInList = (reactions: IReaction[], emoji: string, username: string): IReaction[] => {
242+
let updated = [...reactions];
243+
const index = updated.findIndex(r => r.emoji === emoji);
244+
245+
if (index === -1) {
246+
// New reaction
247+
updated.push({ _id: `${emoji}-${Date.now()}-${Math.random()}`, emoji, usernames: [username], names: [username] });
248+
return updated;
230249
}
231250

232-
this.setState(prev => {
233-
const current = prev.proxyReactions ?? item.reactions ?? [];
234-
235-
const updated = [...current];
236-
const index = updated.findIndex(r => r.emoji === emoji);
237-
238-
if (index > -1) {
239-
const alreadyReacted = updated[index].usernames.includes(username);
240-
241-
if (alreadyReacted) {
242-
// remove
243-
const currentReaction = updated[index];
244-
console.log(currentReaction)
245-
const newUsers = currentReaction.usernames.filter(u => u !== username);
246-
const newNames = currentReaction.usernames.filter((_, i) => currentReaction.usernames[i] !== username);
247-
248-
if (newUsers.length === 0) {
249-
updated.splice(index, 1);
250-
} else {
251-
updated[index] = {
252-
...currentReaction,
253-
usernames: newUsers,
254-
names: newNames
255-
};
256-
}
257-
} else {
258-
// add
259-
const currentReaction = updated[index];
260-
updated[index] = {
261-
...currentReaction,
262-
usernames: [...currentReaction.usernames, username],
263-
names: [...currentReaction.usernames, username]
264-
};
265-
}
266-
} else {
267-
updated.push({
268-
_id: `${emoji}-${Date.now()}`,
269-
emoji,
270-
usernames: [username],
271-
names: [username]
272-
});
251+
const alreadyReacted = updated[index].usernames.includes(username);
252+
253+
if (alreadyReacted) {
254+
const next = this.removeUserFromReaction(updated[index], username);
255+
256+
if (next === null) {
257+
updated = updated.filter((_, i) => i !== index);
258+
return updated;
273259
}
274260

275-
return { proxyReactions: updated };
261+
updated = updated.map((r, i) => (i === index ? next : r));
262+
return updated;
263+
}
264+
265+
updated[index] = this.addUserToReaction(updated[index], username);
266+
return updated;
267+
};
268+
269+
private applyOptimisticReaction = (emoji: string, username: string) => {
270+
this.setState(prev => {
271+
const current = prev.proxyReactions ?? this.props.item.reactions ?? [];
272+
return { proxyReactions: this.toggleReactionInList(current, emoji, username) };
276273
});
274+
};
277275

278-
// update on server
279-
const success = await onReactionPress(emoji, item.id);
276+
private rollbackReaction = () => {
277+
Alert.alert(i18n.t('Error'), i18n.t('Reaction_Failed'));
278+
this.setState({ proxyReactions: undefined });
279+
};
280280

281-
// if fails use server's state as source of truth
282-
if (!success) {
283-
Alert.alert(i18n.t('Error'), i18n.t('Reaction_Failed'));
284-
// rollback on failure
285-
this.setState({ proxyReactions: undefined });
286-
return;
287-
}
281+
onReactionPress = async (emoji: string) => {
282+
const {
283+
onReactionPress,
284+
item,
285+
user: { username }
286+
} = this.props;
287+
if (!onReactionPress) return;
288288

289-
if (!this.subscription) {
290-
this.setState({ proxyReactions: undefined });
291-
}
289+
// proxy reactions first for instant update
290+
this.applyOptimisticReaction(emoji, username);
291+
292+
// then update on server
293+
const success = await onReactionPress(emoji, item.id);
294+
295+
if (!success) return this.rollbackReaction();
296+
if (!this.subscription) this.setState({ proxyReactions: undefined });
292297
};
293298

294299
onReactionLongPress = () => {

0 commit comments

Comments
 (0)