stream-chat-react-native-core 6.7.3-beta.2 → 6.7.3-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/Channel/Channel.js +273 -281
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js +133 -147
- package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js.map +1 -1
- package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js +7 -12
- package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +167 -179
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/components/MessageList/hooks/useMessageList.js +60 -37
- package/lib/commonjs/components/MessageList/hooks/useMessageList.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +450 -459
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/commonjs/hooks/index.js +11 -0
- package/lib/commonjs/hooks/index.js.map +1 -1
- package/lib/commonjs/hooks/useStableCallback.js +13 -0
- package/lib/commonjs/hooks/useStableCallback.js.map +1 -0
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Channel/Channel.js +273 -281
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/hooks/useMessageListPagination.js +133 -147
- package/lib/module/components/Channel/hooks/useMessageListPagination.js.map +1 -1
- package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js +7 -12
- package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +167 -179
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/MessageList/hooks/useMessageList.js +60 -37
- package/lib/module/components/MessageList/hooks/useMessageList.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +450 -459
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/module/hooks/index.js +11 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useStableCallback.js +13 -0
- package/lib/module/hooks/useStableCallback.js.map +1 -0
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts +3 -3
- package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts.map +1 -1
- package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts +3 -0
- package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts +4 -0
- package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts.map +1 -1
- package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +1 -1
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts.map +1 -1
- package/lib/typescript/hooks/index.d.ts +1 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -1
- package/lib/typescript/hooks/useStableCallback.d.ts +26 -0
- package/lib/typescript/hooks/useStableCallback.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/Channel/Channel.tsx +424 -408
- package/src/components/Channel/hooks/useMessageListPagination.tsx +152 -147
- package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx +6 -4
- package/src/components/MessageList/MessageList.tsx +147 -112
- package/src/components/MessageList/hooks/useMessageList.ts +69 -38
- package/src/contexts/messageInputContext/MessageInputContext.tsx +293 -267
- package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx +2 -1
- package/src/contexts/messagesContext/MessagesContext.tsx +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useStableCallback.ts +37 -0
- package/src/version.json +1 -1
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
useTranslationContext,
|
|
75
75
|
} from '../../contexts/translationContext/TranslationContext';
|
|
76
76
|
import { TypingProvider } from '../../contexts/typingContext/TypingContext';
|
|
77
|
+
import { useStableCallback } from '../../hooks';
|
|
77
78
|
import { useAppStateListener } from '../../hooks/useAppStateListener';
|
|
78
79
|
|
|
79
80
|
import {
|
|
@@ -716,6 +717,12 @@ const ChannelWithContext = <
|
|
|
716
717
|
* Its a map of filename to AbortController
|
|
717
718
|
*/
|
|
718
719
|
const uploadAbortControllerRef = useRef<Map<string, AbortController>>(new Map());
|
|
720
|
+
/**
|
|
721
|
+
* This ref keeps track of message IDs which have already been optimistically updated.
|
|
722
|
+
* We need it to make sure we don't react on message.new/notification.message_new events
|
|
723
|
+
* if this is indeed the case, as it's a full list update for nothing.
|
|
724
|
+
*/
|
|
725
|
+
const optimisticallyUpdatedNewMessages = useMemo<Set<string>>(() => new Set(), []);
|
|
719
726
|
|
|
720
727
|
const channelId = channel?.id || '';
|
|
721
728
|
const pollCreationEnabled = !channel.disconnected && !!channel?.id && channel?.getConfig()?.polls;
|
|
@@ -784,7 +791,7 @@ const ChannelWithContext = <
|
|
|
784
791
|
[stateUpdateThrottleInterval, channel, copyStateFromChannel, copyMessagesStateFromChannel],
|
|
785
792
|
);
|
|
786
793
|
|
|
787
|
-
const handleEvent: EventHandler<StreamChatGenerics> = (event) => {
|
|
794
|
+
const handleEvent: EventHandler<StreamChatGenerics> = useStableCallback((event) => {
|
|
788
795
|
if (shouldSyncChannel) {
|
|
789
796
|
/**
|
|
790
797
|
* Ignore user.watching.start and user.watching.stop as we should not copy the entire state when
|
|
@@ -838,8 +845,16 @@ const ChannelWithContext = <
|
|
|
838
845
|
|
|
839
846
|
// only update channel state if the events are not the previously subscribed useEffect's subscription events
|
|
840
847
|
if (channel && channel.initialized) {
|
|
841
|
-
|
|
842
|
-
|
|
848
|
+
// we skip the new message events if we've already done an optimistic update for the new message
|
|
849
|
+
if (event.type === 'message.new' || event.type === 'notification.message_new') {
|
|
850
|
+
const messageId = event.message?.id ?? '';
|
|
851
|
+
if (
|
|
852
|
+
event.user?.id !== client.userID ||
|
|
853
|
+
!optimisticallyUpdatedNewMessages.has(messageId)
|
|
854
|
+
) {
|
|
855
|
+
copyMessagesStateFromChannelThrottled();
|
|
856
|
+
}
|
|
857
|
+
optimisticallyUpdatedNewMessages.delete(messageId);
|
|
843
858
|
return;
|
|
844
859
|
}
|
|
845
860
|
|
|
@@ -851,7 +866,7 @@ const ChannelWithContext = <
|
|
|
851
866
|
copyChannelState();
|
|
852
867
|
}
|
|
853
868
|
}
|
|
854
|
-
};
|
|
869
|
+
});
|
|
855
870
|
|
|
856
871
|
useEffect(() => {
|
|
857
872
|
let listener: ReturnType<typeof channel.on>;
|
|
@@ -961,7 +976,7 @@ const ChannelWithContext = <
|
|
|
961
976
|
/**
|
|
962
977
|
* CHANNEL METHODS
|
|
963
978
|
*/
|
|
964
|
-
const
|
|
979
|
+
const markReadInternal: ChannelContextValue<StreamChatGenerics>['markRead'] = throttle(
|
|
965
980
|
async (options?: MarkReadFunctionOptions) => {
|
|
966
981
|
const { updateChannelUnreadState = true } = options ?? {};
|
|
967
982
|
if (!channel || channel?.disconnected || !clientChannelConfig?.read_events) {
|
|
@@ -990,7 +1005,9 @@ const ChannelWithContext = <
|
|
|
990
1005
|
throttleOptions,
|
|
991
1006
|
);
|
|
992
1007
|
|
|
993
|
-
const
|
|
1008
|
+
const markRead = useStableCallback(markReadInternal);
|
|
1009
|
+
|
|
1010
|
+
const reloadThread = useStableCallback(async () => {
|
|
994
1011
|
if (!channel || !thread?.id) {
|
|
995
1012
|
return;
|
|
996
1013
|
}
|
|
@@ -1023,9 +1040,9 @@ const ChannelWithContext = <
|
|
|
1023
1040
|
setThreadLoadingMore(false);
|
|
1024
1041
|
throw err;
|
|
1025
1042
|
}
|
|
1026
|
-
};
|
|
1043
|
+
});
|
|
1027
1044
|
|
|
1028
|
-
const resyncChannel = async () => {
|
|
1045
|
+
const resyncChannel = useStableCallback(async () => {
|
|
1029
1046
|
if (!channel || syncingChannelRef.current) {
|
|
1030
1047
|
return;
|
|
1031
1048
|
}
|
|
@@ -1081,7 +1098,7 @@ const ChannelWithContext = <
|
|
|
1081
1098
|
}
|
|
1082
1099
|
|
|
1083
1100
|
syncingChannelRef.current = false;
|
|
1084
|
-
};
|
|
1101
|
+
});
|
|
1085
1102
|
|
|
1086
1103
|
// resync channel is added to ref so that it can be used in useEffect without adding it as a dependency
|
|
1087
1104
|
const resyncChannelRef = useRef(resyncChannel);
|
|
@@ -1132,16 +1149,16 @@ const ChannelWithContext = <
|
|
|
1132
1149
|
*/
|
|
1133
1150
|
const clientChannelConfig = getChannelConfigSafely();
|
|
1134
1151
|
|
|
1135
|
-
const reloadChannel = async () => {
|
|
1152
|
+
const reloadChannel = useStableCallback(async () => {
|
|
1136
1153
|
try {
|
|
1137
1154
|
await loadLatestMessages();
|
|
1138
1155
|
} catch (err) {
|
|
1139
1156
|
console.warn('Reloading channel failed with error:', err);
|
|
1140
1157
|
}
|
|
1141
|
-
};
|
|
1158
|
+
});
|
|
1142
1159
|
|
|
1143
1160
|
const loadChannelAroundMessage: ChannelContextValue<StreamChatGenerics>['loadChannelAroundMessage'] =
|
|
1144
|
-
async ({ messageId: messageIdToLoadAround }): Promise<void> => {
|
|
1161
|
+
useStableCallback(async ({ messageId: messageIdToLoadAround }): Promise<void> => {
|
|
1145
1162
|
if (!messageIdToLoadAround) {
|
|
1146
1163
|
return;
|
|
1147
1164
|
}
|
|
@@ -1172,350 +1189,354 @@ const ChannelWithContext = <
|
|
|
1172
1189
|
} catch (err) {
|
|
1173
1190
|
console.warn('Loading channel around message failed with error:', err);
|
|
1174
1191
|
}
|
|
1175
|
-
};
|
|
1192
|
+
});
|
|
1176
1193
|
|
|
1177
1194
|
/**
|
|
1178
1195
|
* MESSAGE METHODS
|
|
1179
1196
|
*/
|
|
1180
|
-
const updateMessage: MessagesContextValue<StreamChatGenerics>['updateMessage'] =
|
|
1181
|
-
updatedMessage,
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
return;
|
|
1186
|
-
}
|
|
1197
|
+
const updateMessage: MessagesContextValue<StreamChatGenerics>['updateMessage'] =
|
|
1198
|
+
useStableCallback((updatedMessage, extraState = {}, throttled = false) => {
|
|
1199
|
+
if (!channel) {
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1187
1202
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1203
|
+
channel.state.addMessageSorted(updatedMessage, true);
|
|
1204
|
+
if (throttled) {
|
|
1205
|
+
copyMessagesStateFromChannelThrottled();
|
|
1206
|
+
} else {
|
|
1207
|
+
copyMessagesStateFromChannel(channel);
|
|
1208
|
+
}
|
|
1190
1209
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1210
|
+
if (thread && updatedMessage.parent_id) {
|
|
1211
|
+
extraState.threadMessages = channel.state.threads[updatedMessage.parent_id] || [];
|
|
1212
|
+
setThreadMessages(extraState.threadMessages);
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1196
1215
|
|
|
1197
|
-
const replaceMessage = (
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
channel
|
|
1203
|
-
|
|
1204
|
-
|
|
1216
|
+
const replaceMessage = useStableCallback(
|
|
1217
|
+
(
|
|
1218
|
+
oldMessage: MessageResponse<StreamChatGenerics>,
|
|
1219
|
+
newMessage: MessageResponse<StreamChatGenerics>,
|
|
1220
|
+
) => {
|
|
1221
|
+
if (channel) {
|
|
1222
|
+
channel.state.removeMessage(oldMessage);
|
|
1223
|
+
channel.state.addMessageSorted(newMessage, true);
|
|
1224
|
+
copyMessagesStateFromChannel(channel);
|
|
1205
1225
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1226
|
+
if (thread && newMessage.parent_id) {
|
|
1227
|
+
const threadMessages = channel.state.threads[newMessage.parent_id] || [];
|
|
1228
|
+
setThreadMessages(threadMessages);
|
|
1229
|
+
}
|
|
1209
1230
|
}
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1231
|
+
},
|
|
1232
|
+
);
|
|
1212
1233
|
|
|
1213
|
-
const createMessagePreview = (
|
|
1214
|
-
|
|
1215
|
-
mentioned_users,
|
|
1216
|
-
parent_id,
|
|
1217
|
-
poll,
|
|
1218
|
-
poll_id,
|
|
1219
|
-
text,
|
|
1220
|
-
...extraFields
|
|
1221
|
-
}: Partial<StreamMessage<StreamChatGenerics>>) => {
|
|
1222
|
-
// Exclude following properties from message.user within message preview,
|
|
1223
|
-
// since they could be long arrays and have no meaning as sender of message.
|
|
1224
|
-
// Storing such large value within user's table may cause sqlite queries to crash.
|
|
1225
|
-
// @ts-ignore
|
|
1226
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1227
|
-
const { channel_mutes, devices, mutes, ...messageUser } = client.user;
|
|
1228
|
-
|
|
1229
|
-
const preview = {
|
|
1230
|
-
__html: text,
|
|
1234
|
+
const createMessagePreview = useStableCallback(
|
|
1235
|
+
({
|
|
1231
1236
|
attachments,
|
|
1232
|
-
|
|
1233
|
-
html: text,
|
|
1234
|
-
id: `${client.userID}-${generateRandomId()}`,
|
|
1235
|
-
mentioned_users:
|
|
1236
|
-
mentioned_users?.map((userId) => ({
|
|
1237
|
-
id: userId,
|
|
1238
|
-
})) || [],
|
|
1237
|
+
mentioned_users,
|
|
1239
1238
|
parent_id,
|
|
1240
1239
|
poll,
|
|
1241
1240
|
poll_id,
|
|
1242
|
-
reactions: [],
|
|
1243
|
-
status: MessageStatusTypes.SENDING,
|
|
1244
1241
|
text,
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1242
|
+
...extraFields
|
|
1243
|
+
}: Partial<StreamMessage<StreamChatGenerics>>) => {
|
|
1244
|
+
// Exclude following properties from message.user within message preview,
|
|
1245
|
+
// since they could be long arrays and have no meaning as sender of message.
|
|
1246
|
+
// Storing such large value within user's table may cause sqlite queries to crash.
|
|
1247
|
+
// @ts-ignore
|
|
1248
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1249
|
+
const { channel_mutes, devices, mutes, ...messageUser } = client.user;
|
|
1250
|
+
|
|
1251
|
+
const preview = {
|
|
1252
|
+
__html: text,
|
|
1253
|
+
attachments,
|
|
1254
|
+
created_at: new Date(),
|
|
1255
|
+
html: text,
|
|
1256
|
+
id: `${client.userID}-${generateRandomId()}`,
|
|
1257
|
+
mentioned_users:
|
|
1258
|
+
mentioned_users?.map((userId) => ({
|
|
1259
|
+
id: userId,
|
|
1260
|
+
})) || [],
|
|
1261
|
+
parent_id,
|
|
1262
|
+
poll,
|
|
1263
|
+
poll_id,
|
|
1264
|
+
reactions: [],
|
|
1265
|
+
status: MessageStatusTypes.SENDING,
|
|
1266
|
+
text,
|
|
1267
|
+
type: 'regular',
|
|
1268
|
+
user: {
|
|
1269
|
+
...messageUser,
|
|
1270
|
+
id: client.userID,
|
|
1271
|
+
},
|
|
1272
|
+
...extraFields,
|
|
1273
|
+
} as unknown as MessageResponse<StreamChatGenerics>;
|
|
1262
1274
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1275
|
+
/**
|
|
1276
|
+
* This is added to the message for local rendering prior to the message
|
|
1277
|
+
* being returned from the backend, it is removed when the message is sent
|
|
1278
|
+
* as quoted_message is a reserved field.
|
|
1279
|
+
*/
|
|
1280
|
+
if (preview.quoted_message_id) {
|
|
1281
|
+
const quotedMessage = channelMessagesState.messages?.find(
|
|
1282
|
+
(message) => message.id === preview.quoted_message_id,
|
|
1283
|
+
);
|
|
1268
1284
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
const file = attachment.originalFile;
|
|
1276
|
-
// check if image_url is not a remote url
|
|
1277
|
-
if (
|
|
1278
|
-
attachment.type === FileTypes.Image &&
|
|
1279
|
-
image?.uri &&
|
|
1280
|
-
attachment.image_url &&
|
|
1281
|
-
isLocalUrl(attachment.image_url)
|
|
1282
|
-
) {
|
|
1283
|
-
const filename = image.name ?? getFileNameFromPath(image.uri);
|
|
1284
|
-
// if any upload is in progress, cancel it
|
|
1285
|
-
const controller = uploadAbortControllerRef.current.get(filename);
|
|
1286
|
-
if (controller) {
|
|
1287
|
-
controller.abort();
|
|
1288
|
-
uploadAbortControllerRef.current.delete(filename);
|
|
1289
|
-
}
|
|
1290
|
-
const compressedUri = await compressedImageURI(image, compressImageQuality);
|
|
1291
|
-
const contentType = lookup(filename) || 'multipart/form-data';
|
|
1285
|
+
preview.quoted_message =
|
|
1286
|
+
quotedMessage as MessageResponse<StreamChatGenerics>['quoted_message'];
|
|
1287
|
+
}
|
|
1288
|
+
return preview;
|
|
1289
|
+
},
|
|
1290
|
+
);
|
|
1292
1291
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1292
|
+
const uploadPendingAttachments = useStableCallback(
|
|
1293
|
+
async (message: MessageResponse<StreamChatGenerics>) => {
|
|
1294
|
+
const updatedMessage = { ...message };
|
|
1295
|
+
if (updatedMessage.attachments?.length) {
|
|
1296
|
+
for (let i = 0; i < updatedMessage.attachments?.length; i++) {
|
|
1297
|
+
const attachment = updatedMessage.attachments[i];
|
|
1298
|
+
const image = attachment.originalImage;
|
|
1299
|
+
const file = attachment.originalFile;
|
|
1300
|
+
// check if image_url is not a remote url
|
|
1301
|
+
if (
|
|
1302
|
+
attachment.type === FileTypes.Image &&
|
|
1303
|
+
image?.uri &&
|
|
1304
|
+
attachment.image_url &&
|
|
1305
|
+
isLocalUrl(attachment.image_url)
|
|
1306
|
+
) {
|
|
1307
|
+
const filename = image.name ?? getFileNameFromPath(image.uri);
|
|
1308
|
+
// if any upload is in progress, cancel it
|
|
1309
|
+
const controller = uploadAbortControllerRef.current.get(filename);
|
|
1310
|
+
if (controller) {
|
|
1311
|
+
controller.abort();
|
|
1312
|
+
uploadAbortControllerRef.current.delete(filename);
|
|
1313
|
+
}
|
|
1314
|
+
const compressedUri = await compressedImageURI(image, compressImageQuality);
|
|
1315
|
+
const contentType = lookup(filename) || 'multipart/form-data';
|
|
1296
1316
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1317
|
+
const uploadResponse = doImageUploadRequest
|
|
1318
|
+
? await doImageUploadRequest(image, channel)
|
|
1319
|
+
: await channel.sendImage(compressedUri, filename, contentType);
|
|
1299
1320
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1321
|
+
attachment.image_url = uploadResponse.file;
|
|
1322
|
+
delete attachment.originalFile;
|
|
1304
1323
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
attachment.type === FileTypes.VoiceRecording ||
|
|
1309
|
-
attachment.type === FileTypes.Video) &&
|
|
1310
|
-
attachment.asset_url &&
|
|
1311
|
-
isLocalUrl(attachment.asset_url) &&
|
|
1312
|
-
file?.uri
|
|
1313
|
-
) {
|
|
1314
|
-
// if any upload is in progress, cancel it
|
|
1315
|
-
const controller = uploadAbortControllerRef.current.get(file.name);
|
|
1316
|
-
if (controller) {
|
|
1317
|
-
controller.abort();
|
|
1318
|
-
uploadAbortControllerRef.current.delete(file.name);
|
|
1319
|
-
}
|
|
1320
|
-
const response = doDocUploadRequest
|
|
1321
|
-
? await doDocUploadRequest(file, channel)
|
|
1322
|
-
: await channel.sendFile(file.uri, file.name, file.mimeType);
|
|
1323
|
-
attachment.asset_url = response.file;
|
|
1324
|
-
if (response.thumb_url) {
|
|
1325
|
-
attachment.thumb_url = response.thumb_url;
|
|
1324
|
+
await dbApi.updateMessage({
|
|
1325
|
+
message: { ...updatedMessage, cid: channel.cid },
|
|
1326
|
+
});
|
|
1326
1327
|
}
|
|
1327
1328
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1329
|
+
if (
|
|
1330
|
+
(attachment.type === FileTypes.File ||
|
|
1331
|
+
attachment.type === FileTypes.Audio ||
|
|
1332
|
+
attachment.type === FileTypes.VoiceRecording ||
|
|
1333
|
+
attachment.type === FileTypes.Video) &&
|
|
1334
|
+
attachment.asset_url &&
|
|
1335
|
+
isLocalUrl(attachment.asset_url) &&
|
|
1336
|
+
file?.uri
|
|
1337
|
+
) {
|
|
1338
|
+
// if any upload is in progress, cancel it
|
|
1339
|
+
const controller = uploadAbortControllerRef.current.get(file.name);
|
|
1340
|
+
if (controller) {
|
|
1341
|
+
controller.abort();
|
|
1342
|
+
uploadAbortControllerRef.current.delete(file.name);
|
|
1343
|
+
}
|
|
1344
|
+
const response = doDocUploadRequest
|
|
1345
|
+
? await doDocUploadRequest(file, channel)
|
|
1346
|
+
: await channel.sendFile(file.uri, file.name, file.mimeType);
|
|
1347
|
+
attachment.asset_url = response.file;
|
|
1348
|
+
if (response.thumb_url) {
|
|
1349
|
+
attachment.thumb_url = response.thumb_url;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
delete attachment.originalFile;
|
|
1353
|
+
await dbApi.updateMessage({
|
|
1354
|
+
message: { ...updatedMessage, cid: channel.cid },
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1332
1357
|
}
|
|
1333
1358
|
}
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
return updatedMessage;
|
|
1337
|
-
};
|
|
1338
1359
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
) => {
|
|
1343
|
-
try {
|
|
1344
|
-
const updatedMessage = await uploadPendingAttachments(message);
|
|
1345
|
-
const extraFields = omit(updatedMessage, [
|
|
1346
|
-
'__html',
|
|
1347
|
-
'attachments',
|
|
1348
|
-
'created_at',
|
|
1349
|
-
'deleted_at',
|
|
1350
|
-
'html',
|
|
1351
|
-
'id',
|
|
1352
|
-
'latest_reactions',
|
|
1353
|
-
'mentioned_users',
|
|
1354
|
-
'own_reactions',
|
|
1355
|
-
'parent_id',
|
|
1356
|
-
'quoted_message',
|
|
1357
|
-
'reaction_counts',
|
|
1358
|
-
'reaction_groups',
|
|
1359
|
-
'reactions',
|
|
1360
|
-
'status',
|
|
1361
|
-
'text',
|
|
1362
|
-
'type',
|
|
1363
|
-
'updated_at',
|
|
1364
|
-
'user',
|
|
1365
|
-
]);
|
|
1366
|
-
const { attachments, id, mentioned_users, parent_id, text } = updatedMessage;
|
|
1367
|
-
if (!channel.id) {
|
|
1368
|
-
return;
|
|
1369
|
-
}
|
|
1360
|
+
return updatedMessage;
|
|
1361
|
+
},
|
|
1362
|
+
);
|
|
1370
1363
|
|
|
1371
|
-
|
|
1364
|
+
const sendMessageRequest = useStableCallback(
|
|
1365
|
+
async (message: MessageResponse<StreamChatGenerics>, retrying?: boolean) => {
|
|
1366
|
+
try {
|
|
1367
|
+
const updatedMessage = await uploadPendingAttachments(message);
|
|
1368
|
+
const extraFields = omit(updatedMessage, [
|
|
1369
|
+
'__html',
|
|
1370
|
+
'attachments',
|
|
1371
|
+
'created_at',
|
|
1372
|
+
'deleted_at',
|
|
1373
|
+
'html',
|
|
1374
|
+
'id',
|
|
1375
|
+
'latest_reactions',
|
|
1376
|
+
'mentioned_users',
|
|
1377
|
+
'own_reactions',
|
|
1378
|
+
'parent_id',
|
|
1379
|
+
'quoted_message',
|
|
1380
|
+
'reaction_counts',
|
|
1381
|
+
'reaction_groups',
|
|
1382
|
+
'reactions',
|
|
1383
|
+
'status',
|
|
1384
|
+
'text',
|
|
1385
|
+
'type',
|
|
1386
|
+
'updated_at',
|
|
1387
|
+
'user',
|
|
1388
|
+
]);
|
|
1389
|
+
const { attachments, id, mentioned_users, parent_id, text } = updatedMessage;
|
|
1390
|
+
if (!channel.id) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1372
1393
|
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1394
|
+
const mentionedUserIds = mentioned_users?.map((user) => user.id) || [];
|
|
1395
|
+
|
|
1396
|
+
const messageData = {
|
|
1397
|
+
attachments,
|
|
1398
|
+
id,
|
|
1399
|
+
mentioned_users: mentionedUserIds,
|
|
1400
|
+
parent_id,
|
|
1401
|
+
text: patchMessageTextCommand(text ?? '', mentionedUserIds),
|
|
1402
|
+
...extraFields,
|
|
1403
|
+
} as StreamMessage<StreamChatGenerics>;
|
|
1404
|
+
|
|
1405
|
+
let messageResponse = {} as SendMessageAPIResponse<StreamChatGenerics>;
|
|
1406
|
+
if (doSendMessageRequest) {
|
|
1407
|
+
messageResponse = await doSendMessageRequest(channel?.cid || '', messageData);
|
|
1408
|
+
} else if (channel) {
|
|
1409
|
+
messageResponse = await channel.sendMessage(messageData);
|
|
1410
|
+
}
|
|
1381
1411
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
messageResponse = await doSendMessageRequest(channel?.cid || '', messageData);
|
|
1385
|
-
} else if (channel) {
|
|
1386
|
-
messageResponse = await channel.sendMessage(messageData);
|
|
1387
|
-
}
|
|
1412
|
+
if (messageResponse.message) {
|
|
1413
|
+
messageResponse.message.status = MessageStatusTypes.RECEIVED;
|
|
1388
1414
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1415
|
+
if (enableOfflineSupport) {
|
|
1416
|
+
await dbApi.updateMessage({
|
|
1417
|
+
message: { ...messageResponse.message, cid: channel.cid },
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
if (retrying) {
|
|
1421
|
+
replaceMessage(message, messageResponse.message);
|
|
1422
|
+
} else {
|
|
1423
|
+
updateMessage(messageResponse.message, {}, true);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
} catch (err) {
|
|
1427
|
+
console.log(err);
|
|
1428
|
+
message.status = MessageStatusTypes.FAILED;
|
|
1429
|
+
const updatedMessage = { ...message, cid: channel.cid };
|
|
1430
|
+
updateMessage(updatedMessage);
|
|
1431
|
+
threadInstance?.upsertReplyLocally?.({ message: updatedMessage });
|
|
1432
|
+
optimisticallyUpdatedNewMessages.delete(message.id);
|
|
1391
1433
|
|
|
1392
1434
|
if (enableOfflineSupport) {
|
|
1393
1435
|
await dbApi.updateMessage({
|
|
1394
|
-
message: { ...
|
|
1436
|
+
message: { ...message, cid: channel.cid },
|
|
1395
1437
|
});
|
|
1396
1438
|
}
|
|
1397
|
-
if (retrying) {
|
|
1398
|
-
replaceMessage(message, messageResponse.message);
|
|
1399
|
-
} else {
|
|
1400
|
-
updateMessage(messageResponse.message);
|
|
1401
|
-
}
|
|
1402
1439
|
}
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
message.status = MessageStatusTypes.FAILED;
|
|
1406
|
-
const updatedMessage = { ...message, cid: channel.cid };
|
|
1407
|
-
updateMessage(updatedMessage);
|
|
1408
|
-
threadInstance?.upsertReplyLocally?.({ message: updatedMessage });
|
|
1440
|
+
},
|
|
1441
|
+
);
|
|
1409
1442
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1443
|
+
const sendMessage: InputMessageInputContextValue<StreamChatGenerics>['sendMessage'] =
|
|
1444
|
+
useStableCallback(async (message) => {
|
|
1445
|
+
if (channel?.state?.filterErrorMessages) {
|
|
1446
|
+
channel.state.filterErrorMessages();
|
|
1414
1447
|
}
|
|
1415
|
-
}
|
|
1416
|
-
};
|
|
1417
1448
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
channel.state.filterErrorMessages();
|
|
1423
|
-
}
|
|
1449
|
+
const messagePreview = createMessagePreview({
|
|
1450
|
+
...message,
|
|
1451
|
+
attachments: message.attachments || [],
|
|
1452
|
+
});
|
|
1424
1453
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1454
|
+
updateMessage(messagePreview, {
|
|
1455
|
+
commands: [],
|
|
1456
|
+
messageInput: '',
|
|
1457
|
+
});
|
|
1458
|
+
threadInstance?.upsertReplyLocally?.({ message: messagePreview });
|
|
1459
|
+
optimisticallyUpdatedNewMessages.add(messagePreview.id);
|
|
1429
1460
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1461
|
+
if (enableOfflineSupport) {
|
|
1462
|
+
// While sending a message, we add the message to local db with failed status, so that
|
|
1463
|
+
// if app gets closed before message gets sent and next time user opens the app
|
|
1464
|
+
// then user can see that message in failed state and can retry.
|
|
1465
|
+
// If succesfull, it will be updated with received status.
|
|
1466
|
+
await dbApi.upsertMessages({
|
|
1467
|
+
messages: [{ ...messagePreview, cid: channel.cid, status: MessageStatusTypes.FAILED }],
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1435
1470
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
// if app gets closed before message gets sent and next time user opens the app
|
|
1439
|
-
// then user can see that message in failed state and can retry.
|
|
1440
|
-
// If succesfull, it will be updated with received status.
|
|
1441
|
-
await dbApi.upsertMessages({
|
|
1442
|
-
messages: [{ ...messagePreview, cid: channel.cid, status: MessageStatusTypes.FAILED }],
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1471
|
+
await sendMessageRequest(messagePreview);
|
|
1472
|
+
});
|
|
1445
1473
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1474
|
+
const retrySendMessage: MessagesContextValue<StreamChatGenerics>['retrySendMessage'] =
|
|
1475
|
+
useStableCallback(async (message) => {
|
|
1476
|
+
const statusPendingMessage = {
|
|
1477
|
+
...message,
|
|
1478
|
+
status: MessageStatusTypes.SENDING,
|
|
1479
|
+
};
|
|
1448
1480
|
|
|
1449
|
-
|
|
1450
|
-
message,
|
|
1451
|
-
) => {
|
|
1452
|
-
const statusPendingMessage = {
|
|
1453
|
-
...message,
|
|
1454
|
-
status: MessageStatusTypes.SENDING,
|
|
1455
|
-
};
|
|
1481
|
+
const messageWithoutReservedFields = removeReservedFields(statusPendingMessage);
|
|
1456
1482
|
|
|
1457
|
-
|
|
1483
|
+
// For bounced messages, we don't need to update the message, instead always send a new message.
|
|
1484
|
+
if (!isBouncedMessage(message)) {
|
|
1485
|
+
updateMessage(messageWithoutReservedFields as MessageResponse<StreamChatGenerics>);
|
|
1486
|
+
}
|
|
1458
1487
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1488
|
+
await sendMessageRequest(
|
|
1489
|
+
messageWithoutReservedFields as MessageResponse<StreamChatGenerics>,
|
|
1490
|
+
true,
|
|
1491
|
+
);
|
|
1492
|
+
});
|
|
1463
1493
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1494
|
+
const editMessage: InputMessageInputContextValue<StreamChatGenerics>['editMessage'] =
|
|
1495
|
+
useStableCallback((updatedMessage) =>
|
|
1496
|
+
doUpdateMessageRequest
|
|
1497
|
+
? doUpdateMessageRequest(channel?.cid || '', updatedMessage)
|
|
1498
|
+
: client.updateMessage(updatedMessage),
|
|
1467
1499
|
);
|
|
1468
|
-
};
|
|
1469
1500
|
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
: client.updateMessage(updatedMessage);
|
|
1476
|
-
|
|
1477
|
-
const setEditingState: MessagesContextValue<StreamChatGenerics>['setEditingState'] = (
|
|
1478
|
-
message,
|
|
1479
|
-
) => {
|
|
1480
|
-
clearQuotedMessageState();
|
|
1481
|
-
setEditing(message);
|
|
1482
|
-
};
|
|
1501
|
+
const setEditingState: MessagesContextValue<StreamChatGenerics>['setEditingState'] =
|
|
1502
|
+
useStableCallback((message) => {
|
|
1503
|
+
clearQuotedMessageState();
|
|
1504
|
+
setEditing(message);
|
|
1505
|
+
});
|
|
1483
1506
|
|
|
1484
|
-
const setQuotedMessageState: MessagesContextValue<StreamChatGenerics>['setQuotedMessageState'] =
|
|
1485
|
-
messageOrBoolean
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
};
|
|
1507
|
+
const setQuotedMessageState: MessagesContextValue<StreamChatGenerics>['setQuotedMessageState'] =
|
|
1508
|
+
useStableCallback((messageOrBoolean) => {
|
|
1509
|
+
setQuotedMessage(messageOrBoolean);
|
|
1510
|
+
});
|
|
1489
1511
|
|
|
1490
1512
|
const clearEditingState: InputMessageInputContextValue<StreamChatGenerics>['clearEditingState'] =
|
|
1491
|
-
() => setEditing(undefined);
|
|
1513
|
+
useStableCallback(() => setEditing(undefined));
|
|
1492
1514
|
|
|
1493
1515
|
const clearQuotedMessageState: InputMessageInputContextValue<StreamChatGenerics>['clearQuotedMessageState'] =
|
|
1494
|
-
() => setQuotedMessage(undefined);
|
|
1516
|
+
useStableCallback(() => setQuotedMessage(undefined));
|
|
1495
1517
|
|
|
1496
1518
|
/**
|
|
1497
1519
|
* Removes the message from local state
|
|
1498
1520
|
*/
|
|
1499
|
-
const removeMessage: MessagesContextValue<StreamChatGenerics>['removeMessage'] =
|
|
1500
|
-
message
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
copyMessagesStateFromChannel(channel);
|
|
1521
|
+
const removeMessage: MessagesContextValue<StreamChatGenerics>['removeMessage'] =
|
|
1522
|
+
useStableCallback(async (message) => {
|
|
1523
|
+
if (channel) {
|
|
1524
|
+
channel.state.removeMessage(message);
|
|
1525
|
+
copyMessagesStateFromChannel(channel);
|
|
1505
1526
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1527
|
+
if (thread) {
|
|
1528
|
+
setThreadMessages(channel.state.threads[thread.id] || []);
|
|
1529
|
+
}
|
|
1508
1530
|
}
|
|
1509
|
-
}
|
|
1510
1531
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1532
|
+
if (enableOfflineSupport) {
|
|
1533
|
+
await dbApi.deleteMessage({
|
|
1534
|
+
id: message.id,
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1517
1538
|
|
|
1518
|
-
const sendReaction = async (type: string, messageId: string) => {
|
|
1539
|
+
const sendReaction = useStableCallback(async (type: string, messageId: string) => {
|
|
1519
1540
|
if (!channel?.id || !client.user) {
|
|
1520
1541
|
throw new Error('Channel has not been initialized');
|
|
1521
1542
|
}
|
|
@@ -1556,91 +1577,88 @@ const ChannelWithContext = <
|
|
|
1556
1577
|
if (sendReactionResponse?.message) {
|
|
1557
1578
|
threadInstance?.upsertReplyLocally?.({ message: sendReactionResponse.message });
|
|
1558
1579
|
}
|
|
1559
|
-
};
|
|
1580
|
+
});
|
|
1560
1581
|
|
|
1561
|
-
const deleteMessage: MessagesContextValue<StreamChatGenerics>['deleteMessage'] =
|
|
1562
|
-
message
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1582
|
+
const deleteMessage: MessagesContextValue<StreamChatGenerics>['deleteMessage'] =
|
|
1583
|
+
useStableCallback(async (message) => {
|
|
1584
|
+
if (!channel.id) {
|
|
1585
|
+
throw new Error('Channel has not been initialized yet');
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
if (!enableOfflineSupport) {
|
|
1589
|
+
if (message.status === MessageStatusTypes.FAILED) {
|
|
1590
|
+
await removeMessage(message);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
await client.deleteMessage(message.id);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1567
1596
|
|
|
1568
|
-
if (!enableOfflineSupport) {
|
|
1569
1597
|
if (message.status === MessageStatusTypes.FAILED) {
|
|
1598
|
+
await DBSyncManager.dropPendingTasks({ messageId: message.id });
|
|
1570
1599
|
await removeMessage(message);
|
|
1600
|
+
} else {
|
|
1601
|
+
const updatedMessage = {
|
|
1602
|
+
...message,
|
|
1603
|
+
cid: channel.cid,
|
|
1604
|
+
deleted_at: new Date().toISOString(),
|
|
1605
|
+
type: 'deleted',
|
|
1606
|
+
};
|
|
1607
|
+
updateMessage(updatedMessage);
|
|
1608
|
+
|
|
1609
|
+
threadInstance?.upsertReplyLocally({ message: updatedMessage });
|
|
1610
|
+
|
|
1611
|
+
const data = await DBSyncManager.queueTask<StreamChatGenerics>({
|
|
1612
|
+
client,
|
|
1613
|
+
task: {
|
|
1614
|
+
channelId: channel.id,
|
|
1615
|
+
channelType: channel.type,
|
|
1616
|
+
messageId: message.id,
|
|
1617
|
+
payload: [message.id],
|
|
1618
|
+
type: 'delete-message',
|
|
1619
|
+
},
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1622
|
+
if (data?.message) {
|
|
1623
|
+
updateMessage({ ...data.message });
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
const deleteReaction: MessagesContextValue<StreamChatGenerics>['deleteReaction'] =
|
|
1629
|
+
useStableCallback(async (type: string, messageId: string) => {
|
|
1630
|
+
if (!channel?.id || !client.user) {
|
|
1631
|
+
throw new Error('Channel has not been initialized');
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
const payload: Parameters<ChannelClass['deleteReaction']> = [messageId, type];
|
|
1635
|
+
|
|
1636
|
+
if (!enableOfflineSupport) {
|
|
1637
|
+
await channel.deleteReaction(...payload);
|
|
1571
1638
|
return;
|
|
1572
1639
|
}
|
|
1573
|
-
await client.deleteMessage(message.id);
|
|
1574
|
-
return;
|
|
1575
|
-
}
|
|
1576
1640
|
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
cid: channel.cid,
|
|
1584
|
-
deleted_at: new Date().toISOString(),
|
|
1585
|
-
type: 'deleted',
|
|
1586
|
-
};
|
|
1587
|
-
updateMessage(updatedMessage);
|
|
1641
|
+
removeReactionFromLocalState({
|
|
1642
|
+
channel,
|
|
1643
|
+
messageId,
|
|
1644
|
+
reactionType: type,
|
|
1645
|
+
user: client.user,
|
|
1646
|
+
});
|
|
1588
1647
|
|
|
1589
|
-
|
|
1648
|
+
copyMessagesStateFromChannel(channel);
|
|
1590
1649
|
|
|
1591
|
-
|
|
1650
|
+
await DBSyncManager.queueTask<StreamChatGenerics>({
|
|
1592
1651
|
client,
|
|
1593
1652
|
task: {
|
|
1594
1653
|
channelId: channel.id,
|
|
1595
1654
|
channelType: channel.type,
|
|
1596
|
-
messageId
|
|
1597
|
-
payload
|
|
1598
|
-
type: 'delete-
|
|
1655
|
+
messageId,
|
|
1656
|
+
payload,
|
|
1657
|
+
type: 'delete-reaction',
|
|
1599
1658
|
},
|
|
1600
1659
|
});
|
|
1601
|
-
|
|
1602
|
-
if (data?.message) {
|
|
1603
|
-
updateMessage({ ...data.message });
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
};
|
|
1607
|
-
|
|
1608
|
-
const deleteReaction: MessagesContextValue<StreamChatGenerics>['deleteReaction'] = async (
|
|
1609
|
-
type: string,
|
|
1610
|
-
messageId: string,
|
|
1611
|
-
) => {
|
|
1612
|
-
if (!channel?.id || !client.user) {
|
|
1613
|
-
throw new Error('Channel has not been initialized');
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
const payload: Parameters<ChannelClass['deleteReaction']> = [messageId, type];
|
|
1617
|
-
|
|
1618
|
-
if (!enableOfflineSupport) {
|
|
1619
|
-
await channel.deleteReaction(...payload);
|
|
1620
|
-
return;
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
removeReactionFromLocalState({
|
|
1624
|
-
channel,
|
|
1625
|
-
messageId,
|
|
1626
|
-
reactionType: type,
|
|
1627
|
-
user: client.user,
|
|
1628
1660
|
});
|
|
1629
1661
|
|
|
1630
|
-
copyMessagesStateFromChannel(channel);
|
|
1631
|
-
|
|
1632
|
-
await DBSyncManager.queueTask<StreamChatGenerics>({
|
|
1633
|
-
client,
|
|
1634
|
-
task: {
|
|
1635
|
-
channelId: channel.id,
|
|
1636
|
-
channelType: channel.type,
|
|
1637
|
-
messageId,
|
|
1638
|
-
payload,
|
|
1639
|
-
type: 'delete-reaction',
|
|
1640
|
-
},
|
|
1641
|
-
});
|
|
1642
|
-
};
|
|
1643
|
-
|
|
1644
1662
|
/**
|
|
1645
1663
|
* THREAD METHODS
|
|
1646
1664
|
*/
|
|
@@ -1681,46 +1699,47 @@ const ChannelWithContext = <
|
|
|
1681
1699
|
),
|
|
1682
1700
|
).current;
|
|
1683
1701
|
|
|
1684
|
-
const loadMoreThread: ThreadContextValue<StreamChatGenerics>['loadMoreThread'] =
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1702
|
+
const loadMoreThread: ThreadContextValue<StreamChatGenerics>['loadMoreThread'] =
|
|
1703
|
+
useStableCallback(async () => {
|
|
1704
|
+
if (threadLoadingMore || !thread?.id) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
setThreadLoadingMore(true);
|
|
1689
1708
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1709
|
+
try {
|
|
1710
|
+
if (channel) {
|
|
1711
|
+
const parentID = thread.id;
|
|
1712
|
+
|
|
1713
|
+
/**
|
|
1714
|
+
* In the channel is re-initializing, then threads may get wiped out during the process
|
|
1715
|
+
* (check `addMessagesSorted` method on channel.state). In those cases, we still want to
|
|
1716
|
+
* preserve the messages on active thread, so lets simply copy messages from UI state to
|
|
1717
|
+
* `channel.state`.
|
|
1718
|
+
*/
|
|
1719
|
+
channel.state.threads[parentID] = threadMessages;
|
|
1720
|
+
const oldestMessageID = threadMessages?.[0]?.id;
|
|
1721
|
+
|
|
1722
|
+
const limit = 50;
|
|
1723
|
+
const queryResponse = await channel.getReplies(parentID, {
|
|
1724
|
+
id_lt: oldestMessageID,
|
|
1725
|
+
limit,
|
|
1726
|
+
});
|
|
1708
1727
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1728
|
+
const updatedHasMore = queryResponse.messages.length === limit;
|
|
1729
|
+
const updatedThreadMessages = channel.state.threads[parentID] || [];
|
|
1730
|
+
loadMoreThreadFinished(updatedHasMore, updatedThreadMessages);
|
|
1731
|
+
}
|
|
1732
|
+
} catch (err) {
|
|
1733
|
+
console.warn('Message pagination request failed with error', err);
|
|
1734
|
+
if (err instanceof Error) {
|
|
1735
|
+
setError(err);
|
|
1736
|
+
} else {
|
|
1737
|
+
setError(true);
|
|
1738
|
+
}
|
|
1739
|
+
setThreadLoadingMore(false);
|
|
1740
|
+
throw err;
|
|
1719
1741
|
}
|
|
1720
|
-
|
|
1721
|
-
throw err;
|
|
1722
|
-
}
|
|
1723
|
-
};
|
|
1742
|
+
});
|
|
1724
1743
|
|
|
1725
1744
|
const ownCapabilitiesContext = useCreateOwnCapabilitiesContext({
|
|
1726
1745
|
channel,
|
|
@@ -1772,14 +1791,11 @@ const ChannelWithContext = <
|
|
|
1772
1791
|
// but it is definitely not trivial, especially considering it depends on other inline functions that
|
|
1773
1792
|
// are not wrapped in a useCallback() themselves hence creating a huge cascading change. Can be removed
|
|
1774
1793
|
// once our memoization issues are fixed in most places in the app or we move to a reactive state store.
|
|
1775
|
-
const sendMessageRef =
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
>((...args) => {
|
|
1781
|
-
return sendMessageRef.current(...args);
|
|
1782
|
-
}, []);
|
|
1794
|
+
// const sendMessageRef = useRef<InputMessageInputContextValue['sendMessage']>(sendMessage);
|
|
1795
|
+
// sendMessageRef.current = sendMessage;
|
|
1796
|
+
// const sendMessageStable = useCallback<InputMessageInputContextValue['sendMessage']>((...args) => {
|
|
1797
|
+
// return sendMessageRef.current(...args);
|
|
1798
|
+
// }, []);
|
|
1783
1799
|
|
|
1784
1800
|
const inputMessageInputContext = useCreateInputMessageInputContext<StreamChatGenerics>({
|
|
1785
1801
|
additionalTextInputProps,
|
|
@@ -1833,7 +1849,7 @@ const ChannelWithContext = <
|
|
|
1833
1849
|
quotedMessage,
|
|
1834
1850
|
SendButton,
|
|
1835
1851
|
sendImageAsync,
|
|
1836
|
-
sendMessage
|
|
1852
|
+
sendMessage,
|
|
1837
1853
|
SendMessageDisallowedIndicator,
|
|
1838
1854
|
setInputRef,
|
|
1839
1855
|
setQuotedMessageState,
|