Skip to content

Commit ff0c911

Browse files
committed
feat: include sending reactions in llc
1 parent f28adc2 commit ff0c911

File tree

8 files changed

+96
-52
lines changed

8 files changed

+96
-52
lines changed

package/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
"path": "0.12.7",
7878
"react-native-markdown-package": "1.8.2",
7979
"react-native-url-polyfill": "^1.3.0",
80-
"stream-chat": "9.0.0-rc.8",
80+
"stream-chat": "https://github.com/GetStream/stream-chat-js.git#ab43f541954904c4efcf797a4fac8b162e3f342d",
8181
"use-sync-external-store": "^1.4.0"
8282
},
8383
"peerDependencies": {
@@ -152,6 +152,7 @@
152152
"uuid": "^11.1.0"
153153
},
154154
"resolutions": {
155-
"@types/react": "^19.0.0"
155+
"@types/react": "^19.0.0",
156+
"@babel/runtime": "^7.26.9"
156157
}
157158
}

package/src/components/Channel/Channel.tsx

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,30 +1478,20 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
14781478
{ enforce_unique: enforceUniqueReaction },
14791479
];
14801480

1481-
if (!enableOfflineSupport) {
1482-
await channel.sendReaction(...payload);
1483-
return;
1484-
}
1481+
if (enableOfflineSupport) {
1482+
addReactionToLocalState({
1483+
channel,
1484+
enforceUniqueReaction,
1485+
messageId,
1486+
reactionType: type,
1487+
user: client.user,
1488+
});
14851489

1486-
addReactionToLocalState({
1487-
channel,
1488-
enforceUniqueReaction,
1489-
messageId,
1490-
reactionType: type,
1491-
user: client.user,
1492-
});
1490+
copyMessagesStateFromChannel(channel);
1491+
}
14931492

1494-
copyMessagesStateFromChannel(channel);
1493+
const sendReactionResponse = await channel.sendReaction(...payload);
14951494

1496-
const sendReactionResponse = await client.offlineDb.syncManager.queueTask({
1497-
task: {
1498-
channelId: channel.id,
1499-
channelType: channel.type,
1500-
messageId,
1501-
payload,
1502-
type: 'send-reaction',
1503-
},
1504-
});
15051495
if (sendReactionResponse?.message) {
15061496
threadInstance?.upsertReplyLocally?.({ message: sendReactionResponse.message });
15071497
}

package/src/components/Chat/Chat.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,9 @@ const ChatWithContext = (props: PropsWithChildren<ChatProps>) => {
214214

215215
const initializeDatabase = () => {
216216
// TODO: Rethink this, it looks ugly
217-
client.setOfflineDBApi(new OfflineDB({ client }));
217+
if (!client.offlineDb.initialized) {
218+
client.setOfflineDBApi(new OfflineDB({ client }));
219+
}
218220
// This acts as a lock for some very rare occurrences of concurrency
219221
// issues we've encountered before with the QuickSqliteClient being
220222
// uninitialized before it's being invoked.

package/src/components/Chat/__tests__/Chat.test.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -240,18 +240,22 @@ describe('TranslationContext', () => {
240240

241241
let unsubscribeSpy;
242242
let listenersAfterInitialMount;
243+
const initSpy = jest.spyOn(chatClientWithUser.offlineDb.syncManager, 'init');
243244

244245
await waitFor(() => {
245246
// the unsubscribe fn changes during init(), so we keep a reference to the spy
246-
unsubscribeSpy = jest.spyOn(DBSyncManager.connectionChangedListener, 'unsubscribe');
247+
unsubscribeSpy = jest.spyOn(
248+
chatClientWithUser.offlineDb.syncManager.connectionChangedListener,
249+
'unsubscribe',
250+
);
247251
listenersAfterInitialMount = chatClientWithUser.listeners['connection.changed'];
248252
});
249253

250254
// remount
251255
rerender(<Chat client={chatClientWithUser} enableOfflineSupport key={2} />);
252256

253257
await waitFor(() => {
254-
expect(DBSyncManager.init).toHaveBeenCalledTimes(2);
258+
expect(initSpy).toHaveBeenCalledTimes(2);
255259
expect(unsubscribeSpy).toHaveBeenCalledTimes(2);
256260
expect(chatClientWithUser.listeners['connection.changed'].length).toBe(
257261
listenersAfterInitialMount.length,
@@ -267,10 +271,14 @@ describe('TranslationContext', () => {
267271

268272
let unsubscribeSpy;
269273
let listenersAfterInitialMount;
274+
const initSpy = jest.spyOn(chatClientWithUser.offlineDb.syncManager, 'init');
270275

271276
await waitFor(() => {
272277
// the unsubscribe fn changes during init(), so we keep a reference to the spy
273-
unsubscribeSpy = jest.spyOn(DBSyncManager.connectionChangedListener, 'unsubscribe');
278+
unsubscribeSpy = jest.spyOn(
279+
chatClientWithUser.offlineDb.syncManager.connectionChangedListener,
280+
'unsubscribe',
281+
);
274282
listenersAfterInitialMount = chatClientWithUser.listeners['connection.changed'];
275283
});
276284

@@ -282,7 +290,7 @@ describe('TranslationContext', () => {
282290
rerender(<Chat client={chatClientWithUser} enableOfflineSupport />);
283291

284292
await waitFor(() => {
285-
expect(DBSyncManager.init).toHaveBeenCalledTimes(2);
293+
expect(initSpy).toHaveBeenCalledTimes(2);
286294
expect(unsubscribeSpy).toHaveBeenCalledTimes(1);
287295
expect(chatClientWithUser.listeners['connection.changed'].length).toBe(
288296
listenersAfterInitialMount.length,
@@ -297,9 +305,14 @@ describe('TranslationContext', () => {
297305
const { rerender } = render(<Chat client={chatClientWithUser} enableOfflineSupport />);
298306

299307
let unsubscribeSpy;
308+
const initSpy = jest.spyOn(chatClientWithUser.offlineDb.syncManager, 'init');
309+
300310
await waitFor(() => {
301311
// the unsubscribe fn changes during init(), so we keep a reference to the spy
302-
unsubscribeSpy = jest.spyOn(DBSyncManager.connectionChangedListener, 'unsubscribe');
312+
unsubscribeSpy = jest.spyOn(
313+
chatClientWithUser.offlineDb.syncManager.connectionChangedListener,
314+
'unsubscribe',
315+
);
303316
});
304317

305318
const listenersAfterInitialMount = chatClientWithUser.listeners['connection.changed'];
@@ -308,8 +321,8 @@ describe('TranslationContext', () => {
308321
rerender(<Chat client={chatClientWithUser} enableOfflineSupport />);
309322

310323
await waitFor(() => {
311-
expect(DBSyncManager.init).toHaveBeenCalledTimes(1);
312-
expect(unsubscribeSpy).toHaveBeenCalledTimes(1);
324+
expect(initSpy).toHaveBeenCalledTimes(1);
325+
expect(unsubscribeSpy).toHaveBeenCalledTimes(0);
313326
expect(chatClientWithUser.listeners['connection.changed'].length).toBe(
314327
listenersAfterInitialMount.length,
315328
);

package/src/store/OfflineDB.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1-
import { AbstractOfflineDB, GetLastSyncedAtType, UpsertUserSyncStatusType } from 'stream-chat';
2-
import type { GetChannelsForQueryType, GetChannelsType } from 'stream-chat';
1+
import {
2+
AbstractOfflineDB,
3+
GetLastSyncedAtType,
4+
type ReactionResponse,
5+
StreamChat,
6+
UpsertUserSyncStatusType,
7+
} from 'stream-chat';
8+
import type { GetChannelsForQueryType, GetChannelsType, UpsertReactionType } from 'stream-chat';
39

410
import * as api from './apis';
511
import { SqliteClient } from './SqliteClient';
612

713
export class OfflineDB extends AbstractOfflineDB {
14+
public initialized = false;
15+
16+
constructor({ client }: { client: StreamChat }) {
17+
super({ client });
18+
19+
this.initialized = true;
20+
}
21+
822
upsertCidsForQuery = api.upsertCidsForQuery;
923
upsertChannels = api.upsertChannels;
1024
// FIXME
@@ -22,6 +36,43 @@ export class OfflineDB extends AbstractOfflineDB {
2236
upsertUserSyncStatus = ({ userId, lastSyncedAt }: UpsertUserSyncStatusType) =>
2337
api.upsertUserSyncStatus({ currentUserId: userId, lastSyncedAt });
2438

39+
upsertReaction = async ({
40+
channel,
41+
enforceUniqueReaction,
42+
messageId,
43+
reactionType,
44+
user,
45+
}: UpsertReactionType) => {
46+
const message = channel.state.messages.find(({ id }) => id === messageId);
47+
48+
if (!message) {
49+
return;
50+
}
51+
52+
const hasOwnReaction = message.own_reactions && message.own_reactions.length > 0;
53+
54+
const reaction: ReactionResponse = {
55+
created_at: new Date().toISOString(),
56+
message_id: messageId,
57+
type: reactionType,
58+
updated_at: new Date().toISOString(),
59+
user,
60+
user_id: user?.id,
61+
};
62+
63+
if (enforceUniqueReaction && hasOwnReaction) {
64+
await api.updateReaction({
65+
message,
66+
reaction,
67+
});
68+
} else {
69+
await api.insertReaction({
70+
message,
71+
reaction,
72+
});
73+
}
74+
};
75+
2576
addPendingTask = api.addPendingTask;
2677

2778
deletePendingTask = api.deletePendingTask;

package/src/store/apis/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export * from './getMembers';
1313
export * from './getReads';
1414
export * from './updateMessage';
1515
export * from './updateReaction';
16+
export * from './insertReaction';
17+
export * from './deleteReaction';
1618
export * from './upsertAppSettings';
1719
export * from './upsertChannelData';
1820
export * from './upsertChannels';

package/src/utils/addReactionToLocalState.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import type { Channel, ReactionResponse, UserResponse } from 'stream-chat';
22

3-
import { updateReaction } from '../store/apis';
4-
import { insertReaction } from '../store/apis/insertReaction';
5-
63
export const addReactionToLocalState = ({
74
channel,
85
enforceUniqueReaction,
@@ -31,7 +28,6 @@ export const addReactionToLocalState = ({
3128
user_id: user?.id,
3229
};
3330

34-
const hasOwnReaction = message.own_reactions && message.own_reactions.length > 0;
3531
if (!message.own_reactions) {
3632
message.own_reactions = [];
3733
}
@@ -92,15 +88,5 @@ export const addReactionToLocalState = ({
9288
message.own_reactions = [...message.own_reactions, reaction];
9389
message.latest_reactions = [...message.latest_reactions, reaction];
9490

95-
if (enforceUniqueReaction && hasOwnReaction) {
96-
updateReaction({
97-
message,
98-
reaction,
99-
});
100-
} else {
101-
insertReaction({
102-
message,
103-
reaction,
104-
});
105-
}
91+
return { message, reaction };
10692
};

package/yarn.lock

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7680,10 +7680,9 @@ statuses@~1.5.0:
76807680
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
76817681
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
76827682

7683-
7684-
version "9.0.0-rc.8"
7685-
resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.0.0-rc.8.tgz#e188e481841493584691ae491916843d0ef5f9cd"
7686-
integrity sha512-P+Ksnu1cQQfL1t2/QTJ5rr/z2Jehvd2ap41xZgtfbJssHSD7ahe14TCF/1L7q4jjaNlZcTtLcKXCWbbOdKjDcg==
7683+
"stream-chat@https://github.com/GetStream/stream-chat-js.git#ab43f541954904c4efcf797a4fac8b162e3f342d":
7684+
version "0.0.0-development"
7685+
resolved "https://github.com/GetStream/stream-chat-js.git#ab43f541954904c4efcf797a4fac8b162e3f342d"
76877686
dependencies:
76887687
"@types/jsonwebtoken" "^9.0.8"
76897688
"@types/ws" "^8.5.14"

0 commit comments

Comments
 (0)