@sendbird/uikit-react-native 3.2.0 → 3.3.0
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/ChannelInput/EditInput.js +2 -11
- package/lib/commonjs/components/ChannelInput/EditInput.js.map +1 -1
- package/lib/commonjs/components/ChannelInput/SendInput.js +2 -11
- package/lib/commonjs/components/ChannelInput/SendInput.js.map +1 -1
- package/lib/commonjs/components/ChannelInput/index.js +30 -3
- package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
- package/lib/commonjs/components/ChannelMessageList/index.js +148 -116
- package/lib/commonjs/components/ChannelMessageList/index.js.map +1 -1
- package/lib/commonjs/components/GroupChannelMessageRenderer/index.js +34 -1
- package/lib/commonjs/components/GroupChannelMessageRenderer/index.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/component/GroupChannelHeader.js +14 -4
- package/lib/commonjs/domain/groupChannel/component/GroupChannelHeader.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +1 -0
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/types.js.map +1 -1
- package/lib/commonjs/fragments/createGroupChannelFragment.js +4 -3
- package/lib/commonjs/fragments/createGroupChannelFragment.js.map +1 -1
- package/lib/commonjs/index.js +4 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js +7 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/utils/promise.js +138 -0
- package/lib/commonjs/utils/promise.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/components/ChannelInput/EditInput.js +3 -12
- package/lib/module/components/ChannelInput/EditInput.js.map +1 -1
- package/lib/module/components/ChannelInput/SendInput.js +3 -12
- package/lib/module/components/ChannelInput/SendInput.js.map +1 -1
- package/lib/module/components/ChannelInput/index.js +32 -5
- package/lib/module/components/ChannelInput/index.js.map +1 -1
- package/lib/module/components/ChannelMessageList/index.js +148 -116
- package/lib/module/components/ChannelMessageList/index.js.map +1 -1
- package/lib/module/components/GroupChannelMessageRenderer/index.js +34 -2
- package/lib/module/components/GroupChannelMessageRenderer/index.js.map +1 -1
- package/lib/module/domain/groupChannel/component/GroupChannelHeader.js +15 -5
- package/lib/module/domain/groupChannel/component/GroupChannelHeader.js.map +1 -1
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js +1 -0
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
- package/lib/module/domain/groupChannel/types.js.map +1 -1
- package/lib/module/fragments/createGroupChannelFragment.js +4 -3
- package/lib/module/fragments/createGroupChannelFragment.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +5 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/promise.js +132 -0
- package/lib/module/utils/promise.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/src/components/ChannelInput/index.d.ts +2 -0
- package/lib/typescript/src/components/ChannelMessageList/index.d.ts +3 -0
- package/lib/typescript/src/components/GroupChannelMessageRenderer/index.d.ts +3 -0
- package/lib/typescript/src/components/OpenChannelMessageRenderer/index.d.ts +2 -0
- package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +1 -1
- package/lib/typescript/src/domain/groupChannel/types.d.ts +3 -0
- package/lib/typescript/src/types.d.ts +4 -0
- package/lib/typescript/src/utils/promise.d.ts +7 -0
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +6 -6
- package/src/components/ChannelInput/EditInput.tsx +3 -15
- package/src/components/ChannelInput/SendInput.tsx +2 -9
- package/src/components/ChannelInput/index.tsx +27 -4
- package/src/components/ChannelMessageList/index.tsx +144 -114
- package/src/components/GroupChannelMessageRenderer/index.tsx +34 -2
- package/src/domain/groupChannel/component/GroupChannelHeader.tsx +14 -3
- package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +1 -0
- package/src/domain/groupChannel/types.ts +4 -0
- package/src/fragments/createGroupChannelFragment.tsx +11 -3
- package/src/index.ts +5 -1
- package/src/types.ts +5 -0
- package/src/utils/promise.ts +139 -0
- package/src/version.ts +1 -1
|
@@ -36,8 +36,9 @@ import type { CommonComponent } from '../../types';
|
|
|
36
36
|
import ChatFlatList from '../ChatFlatList';
|
|
37
37
|
import { ReactionAddons } from '../ReactionAddons';
|
|
38
38
|
|
|
39
|
-
type PressActions = { onPress?: () => void; onLongPress?: () => void };
|
|
39
|
+
type PressActions = { onPress?: () => void; onLongPress?: () => void; bottomSheetItem?: BottomSheetItem };
|
|
40
40
|
type HandleableMessage = SendbirdUserMessage | SendbirdFileMessage;
|
|
41
|
+
type CreateMessagePressActions = (params: { message: SendbirdMessage }) => PressActions;
|
|
41
42
|
export type ChannelMessageListProps<T extends SendbirdGroupChannel | SendbirdOpenChannel> = {
|
|
42
43
|
enableMessageGrouping: boolean;
|
|
43
44
|
currentUserId?: string;
|
|
@@ -74,6 +75,8 @@ export type ChannelMessageListProps<T extends SendbirdGroupChannel | SendbirdOpe
|
|
|
74
75
|
channel: T;
|
|
75
76
|
currentUserId?: ChannelMessageListProps<T>['currentUserId'];
|
|
76
77
|
enableMessageGrouping: ChannelMessageListProps<T>['enableMessageGrouping'];
|
|
78
|
+
bottomSheetItem?: BottomSheetItem;
|
|
79
|
+
isFirstItem: boolean;
|
|
77
80
|
}) => React.ReactElement | null;
|
|
78
81
|
renderNewMessagesButton: null | CommonComponent<{
|
|
79
82
|
visible: boolean;
|
|
@@ -121,7 +124,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
|
|
|
121
124
|
const { colors } = useUIKitTheme();
|
|
122
125
|
const { show } = useUserProfile();
|
|
123
126
|
const { left, right } = useSafeAreaInsets();
|
|
124
|
-
const
|
|
127
|
+
const createMessagePressActions = useCreateMessagePressActions({
|
|
125
128
|
channel,
|
|
126
129
|
currentUserId,
|
|
127
130
|
onEditMessage,
|
|
@@ -134,7 +137,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
|
|
|
134
137
|
const safeAreaLayout = { paddingLeft: left, paddingRight: right };
|
|
135
138
|
|
|
136
139
|
const renderItem: ListRenderItem<SendbirdMessage> = useFreshCallback(({ item, index }) => {
|
|
137
|
-
const { onPress, onLongPress } =
|
|
140
|
+
const { onPress, onLongPress, bottomSheetItem } = createMessagePressActions({ message: item });
|
|
138
141
|
return renderMessage({
|
|
139
142
|
message: item,
|
|
140
143
|
prevMessage: messages[index + 1],
|
|
@@ -147,6 +150,8 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
|
|
|
147
150
|
channel,
|
|
148
151
|
currentUserId,
|
|
149
152
|
focused: (searchItem?.startingPoint ?? -1) === item.createdAt,
|
|
153
|
+
bottomSheetItem,
|
|
154
|
+
isFirstItem: index === 0,
|
|
150
155
|
});
|
|
151
156
|
});
|
|
152
157
|
|
|
@@ -191,7 +196,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
|
|
|
191
196
|
);
|
|
192
197
|
};
|
|
193
198
|
|
|
194
|
-
const
|
|
199
|
+
const useCreateMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpenChannel>({
|
|
195
200
|
channel,
|
|
196
201
|
currentUserId,
|
|
197
202
|
onResendFailedMessage,
|
|
@@ -208,7 +213,7 @@ const useGetMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpen
|
|
|
208
213
|
| 'onDeleteMessage'
|
|
209
214
|
| 'onResendFailedMessage'
|
|
210
215
|
| 'onPressMediaMessage'
|
|
211
|
-
>) => {
|
|
216
|
+
>): CreateMessagePressActions => {
|
|
212
217
|
const { colors } = useUIKitTheme();
|
|
213
218
|
const { STRINGS } = useLocalization();
|
|
214
219
|
const toast = useToast();
|
|
@@ -217,161 +222,186 @@ const useGetMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpen
|
|
|
217
222
|
const { clipboardService, fileService } = usePlatformService();
|
|
218
223
|
const { sbOptions } = useSendbirdChat();
|
|
219
224
|
|
|
220
|
-
const
|
|
225
|
+
const onResendFailure = (error: Error) => {
|
|
221
226
|
toast.show(STRINGS.TOAST.RESEND_MSG_ERROR, 'error');
|
|
222
227
|
Logger.error(STRINGS.TOAST.RESEND_MSG_ERROR, error);
|
|
223
228
|
};
|
|
224
229
|
|
|
225
|
-
const
|
|
230
|
+
const onDeleteFailure = (error: Error) => {
|
|
231
|
+
toast.show(STRINGS.TOAST.DELETE_MSG_ERROR, 'error');
|
|
232
|
+
Logger.error(STRINGS.TOAST.DELETE_MSG_ERROR, error);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const onCopyText = (message: HandleableMessage) => {
|
|
236
|
+
if (message.isUserMessage()) {
|
|
237
|
+
clipboardService.setString(message.message || '');
|
|
238
|
+
toast.show(STRINGS.TOAST.COPY_OK, 'success');
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const onDownloadFile = (message: HandleableMessage) => {
|
|
243
|
+
if (message.isFileMessage()) {
|
|
244
|
+
if (toMegabyte(message.size) > 4) {
|
|
245
|
+
toast.show(STRINGS.TOAST.DOWNLOAD_START, 'success');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fileService
|
|
249
|
+
.save({ fileUrl: message.url, fileName: message.name, fileType: message.type })
|
|
250
|
+
.then((response) => {
|
|
251
|
+
toast.show(STRINGS.TOAST.DOWNLOAD_OK, 'success');
|
|
252
|
+
Logger.log('File saved to', response);
|
|
253
|
+
})
|
|
254
|
+
.catch((err) => {
|
|
255
|
+
toast.show(STRINGS.TOAST.DOWNLOAD_ERROR, 'error');
|
|
256
|
+
Logger.log('File save failure', err);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const onOpenFile = (message: HandleableMessage) => {
|
|
262
|
+
if (message.isFileMessage()) {
|
|
263
|
+
const fileType = getFileType(message.type || getFileExtension(message.name));
|
|
264
|
+
if (['image', 'video', 'audio'].includes(fileType)) {
|
|
265
|
+
onPressMediaMessage?.(message, () => onDeleteMessage(message), getAvailableUriFromFileMessage(message));
|
|
266
|
+
} else {
|
|
267
|
+
SBUUtils.openURL(message.url);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const openSheetForFailedMessage = (message: HandleableMessage) => {
|
|
226
273
|
openSheet({
|
|
227
274
|
sheetItems: [
|
|
228
275
|
{
|
|
229
276
|
title: STRINGS.LABELS.CHANNEL_MESSAGE_FAILED_RETRY,
|
|
230
|
-
onPress: () =>
|
|
231
|
-
onResendFailedMessage(message).catch(onFailureToReSend);
|
|
232
|
-
},
|
|
277
|
+
onPress: () => onResendFailedMessage(message).catch(onResendFailure),
|
|
233
278
|
},
|
|
234
279
|
{
|
|
235
280
|
title: STRINGS.LABELS.CHANNEL_MESSAGE_FAILED_REMOVE,
|
|
236
281
|
titleColor: colors.ui.dialog.default.none.destructive,
|
|
237
|
-
onPress: () =>
|
|
282
|
+
onPress: () => alertForMessageDelete(message),
|
|
238
283
|
},
|
|
239
284
|
],
|
|
240
285
|
});
|
|
241
286
|
};
|
|
242
|
-
|
|
287
|
+
|
|
288
|
+
const alertForMessageDelete = (message: HandleableMessage) => {
|
|
243
289
|
alert({
|
|
244
290
|
title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_TITLE,
|
|
245
291
|
buttons: [
|
|
246
|
-
{
|
|
247
|
-
text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_CANCEL,
|
|
248
|
-
},
|
|
292
|
+
{ text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_CANCEL },
|
|
249
293
|
{
|
|
250
294
|
text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_OK,
|
|
251
295
|
style: 'destructive',
|
|
252
296
|
onPress: () => {
|
|
253
|
-
onDeleteMessage(message).catch(
|
|
297
|
+
onDeleteMessage(message).catch(onDeleteFailure);
|
|
254
298
|
},
|
|
255
299
|
},
|
|
256
300
|
],
|
|
257
301
|
});
|
|
258
302
|
};
|
|
259
303
|
|
|
260
|
-
return (
|
|
261
|
-
if (!
|
|
262
|
-
return { onPress: undefined, onLongPress: undefined };
|
|
263
|
-
}
|
|
304
|
+
return ({ message }) => {
|
|
305
|
+
if (!message.isUserMessage() && !message.isFileMessage()) return {};
|
|
264
306
|
|
|
265
307
|
const sheetItems: BottomSheetItem['sheetItems'] = [];
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
if (msg.isUserMessage()) {
|
|
272
|
-
sheetItems.push({
|
|
273
|
-
icon: 'copy',
|
|
308
|
+
const menu = {
|
|
309
|
+
copy: (message: HandleableMessage) => ({
|
|
310
|
+
icon: 'copy' as const,
|
|
274
311
|
title: STRINGS.LABELS.CHANNEL_MESSAGE_COPY,
|
|
275
|
-
onPress: () =>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
312
|
+
onPress: () => onCopyText(message),
|
|
313
|
+
}),
|
|
314
|
+
edit: (message: HandleableMessage) => ({
|
|
315
|
+
icon: 'edit' as const,
|
|
316
|
+
title: STRINGS.LABELS.CHANNEL_MESSAGE_EDIT,
|
|
317
|
+
onPress: () => onEditMessage(message),
|
|
318
|
+
}),
|
|
319
|
+
delete: (message: HandleableMessage) => ({
|
|
320
|
+
disabled: message.threadInfo ? message.threadInfo.replyCount > 0 : undefined,
|
|
321
|
+
icon: 'delete' as const,
|
|
322
|
+
title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE,
|
|
323
|
+
onPress: () => alertForMessageDelete(message),
|
|
324
|
+
}),
|
|
325
|
+
reply: (message: HandleableMessage) => ({
|
|
326
|
+
disabled: Boolean(message.parentMessageId),
|
|
327
|
+
icon: 'reply' as const,
|
|
328
|
+
title: STRINGS.LABELS.CHANNEL_MESSAGE_REPLY,
|
|
329
|
+
onPress: () => onReplyMessage?.(message),
|
|
330
|
+
}),
|
|
331
|
+
download: (message: HandleableMessage) => ({
|
|
332
|
+
icon: 'download' as const,
|
|
284
333
|
title: STRINGS.LABELS.CHANNEL_MESSAGE_SAVE,
|
|
285
|
-
onPress:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
fileService
|
|
291
|
-
.save({ fileUrl: msg.url, fileName: msg.name, fileType: msg.type })
|
|
292
|
-
.then((response) => {
|
|
293
|
-
toast.show(STRINGS.TOAST.DOWNLOAD_OK, 'success');
|
|
294
|
-
Logger.log('File saved to', response);
|
|
295
|
-
})
|
|
296
|
-
.catch((err) => {
|
|
297
|
-
toast.show(STRINGS.TOAST.DOWNLOAD_ERROR, 'error');
|
|
298
|
-
Logger.log('File save failure', err);
|
|
299
|
-
});
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
}
|
|
334
|
+
onPress: () => onDownloadFile(message),
|
|
335
|
+
}),
|
|
336
|
+
};
|
|
303
337
|
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
338
|
+
if (message.isUserMessage()) {
|
|
339
|
+
sheetItems.push(menu.copy(message));
|
|
340
|
+
if (!channel.isEphemeral) {
|
|
341
|
+
if (isMyMessage(message, currentUserId) && message.sendingStatus === 'succeeded') {
|
|
342
|
+
sheetItems.push(menu.edit(message));
|
|
343
|
+
sheetItems.push(menu.delete(message));
|
|
344
|
+
}
|
|
345
|
+
if (channel.isGroupChannel() && sbOptions.uikit.groupChannel.channel.replyType === 'quote_reply') {
|
|
346
|
+
sheetItems.push(menu.reply(message));
|
|
312
347
|
}
|
|
313
|
-
sheetItems.push({
|
|
314
|
-
disabled: msg.threadInfo ? msg.threadInfo.replyCount > 0 : undefined,
|
|
315
|
-
icon: 'delete',
|
|
316
|
-
title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE,
|
|
317
|
-
onPress: () => confirmDelete(msg),
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
if (channel.isGroupChannel() && sbOptions.uikit.groupChannel.channel.replyType === 'quote_reply') {
|
|
321
|
-
sheetItems.push({
|
|
322
|
-
disabled: Boolean(msg.parentMessageId),
|
|
323
|
-
icon: 'reply',
|
|
324
|
-
title: STRINGS.LABELS.CHANNEL_MESSAGE_REPLY,
|
|
325
|
-
onPress: () => onReplyMessage?.(msg),
|
|
326
|
-
});
|
|
327
348
|
}
|
|
328
349
|
}
|
|
329
350
|
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
onPressMediaMessage?.(msg, () => onDeleteMessage(msg), getAvailableUriFromFileMessage(msg));
|
|
338
|
-
};
|
|
339
|
-
break;
|
|
351
|
+
if (message.isFileMessage()) {
|
|
352
|
+
if (!isVoiceMessage(message)) {
|
|
353
|
+
sheetItems.push(menu.download(message));
|
|
354
|
+
}
|
|
355
|
+
if (!channel.isEphemeral) {
|
|
356
|
+
if (isMyMessage(message, currentUserId) && message.sendingStatus === 'succeeded') {
|
|
357
|
+
sheetItems.push(menu.delete(message));
|
|
340
358
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
break;
|
|
359
|
+
if (channel.isGroupChannel() && sbOptions.uikit.groupChannel.channel.replyType === 'quote_reply') {
|
|
360
|
+
sheetItems.push(menu.reply(message));
|
|
344
361
|
}
|
|
345
362
|
}
|
|
346
363
|
}
|
|
347
364
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
sbOptions.uikitWithAppInfo.groupChannel.channel.enableReactions,
|
|
355
|
-
)
|
|
356
|
-
? ({ onClose }) => <ReactionAddons.BottomSheet message={msg} channel={channel} onClose={onClose} />
|
|
357
|
-
: undefined,
|
|
358
|
-
});
|
|
359
|
-
};
|
|
360
|
-
}
|
|
365
|
+
const bottomSheetItem: BottomSheetItem = {
|
|
366
|
+
sheetItems,
|
|
367
|
+
HeaderComponent: shouldRenderReaction(channel, sbOptions.uikitWithAppInfo.groupChannel.channel.enableReactions)
|
|
368
|
+
? ({ onClose }) => <ReactionAddons.BottomSheet message={message} channel={channel} onClose={onClose} />
|
|
369
|
+
: undefined,
|
|
370
|
+
};
|
|
361
371
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
372
|
+
switch (true) {
|
|
373
|
+
case message.sendingStatus === 'pending': {
|
|
374
|
+
return {
|
|
375
|
+
onPress: undefined,
|
|
376
|
+
onLongPress: undefined,
|
|
377
|
+
bottomSheetItem: undefined,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
368
380
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
case message.sendingStatus === 'failed': {
|
|
382
|
+
return {
|
|
383
|
+
onPress: () => onResendFailedMessage(message).catch(onResendFailure),
|
|
384
|
+
onLongPress: () => openSheetForFailedMessage(message),
|
|
385
|
+
bottomSheetItem,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
case message.isFileMessage(): {
|
|
390
|
+
return {
|
|
391
|
+
onPress: () => onOpenFile(message),
|
|
392
|
+
onLongPress: () => openSheet(bottomSheetItem),
|
|
393
|
+
bottomSheetItem,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
373
396
|
|
|
374
|
-
|
|
397
|
+
default: {
|
|
398
|
+
return {
|
|
399
|
+
onPress: undefined,
|
|
400
|
+
onLongPress: () => openSheet(bottomSheetItem),
|
|
401
|
+
bottomSheetItem,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
375
405
|
};
|
|
376
406
|
};
|
|
377
407
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import React, { useRef } from 'react';
|
|
1
|
+
import React, { useContext, useEffect, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import type { GroupChannelMessageProps, RegexTextPattern } from '@sendbird/uikit-react-native-foundation';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Box,
|
|
6
|
+
GroupChannelMessage,
|
|
7
|
+
Text,
|
|
8
|
+
TypingIndicatorBubble,
|
|
9
|
+
useUIKitTheme,
|
|
10
|
+
} from '@sendbird/uikit-react-native-foundation';
|
|
5
11
|
import {
|
|
6
12
|
SendbirdAdminMessage,
|
|
7
13
|
SendbirdFileMessage,
|
|
@@ -17,9 +23,11 @@ import {
|
|
|
17
23
|
} from '@sendbird/uikit-utils';
|
|
18
24
|
|
|
19
25
|
import { VOICE_MESSAGE_META_ARRAY_DURATION_KEY } from '../../constants';
|
|
26
|
+
import { GroupChannelContexts } from '../../domain/groupChannel/module/moduleContext';
|
|
20
27
|
import type { GroupChannelProps } from '../../domain/groupChannel/types';
|
|
21
28
|
import { useLocalization, usePlatformService, useSendbirdChat } from '../../hooks/useContext';
|
|
22
29
|
import SBUUtils from '../../libs/SBUUtils';
|
|
30
|
+
import { TypingIndicatorType } from '../../types';
|
|
23
31
|
import { ReactionAddons } from '../ReactionAddons';
|
|
24
32
|
import GroupChannelMessageDateSeparator from './GroupChannelMessageDateSeparator';
|
|
25
33
|
import GroupChannelMessageFocusAnimation from './GroupChannelMessageFocusAnimation';
|
|
@@ -292,4 +300,28 @@ const GroupChannelMessageRenderer: GroupChannelProps['Fragment']['renderMessage'
|
|
|
292
300
|
);
|
|
293
301
|
};
|
|
294
302
|
|
|
303
|
+
export const GroupChannelTypingIndicatorBubble = () => {
|
|
304
|
+
const { sbOptions } = useSendbirdChat();
|
|
305
|
+
const { publish } = useContext(GroupChannelContexts.PubSub);
|
|
306
|
+
const { typingUsers } = useContext(GroupChannelContexts.TypingIndicator);
|
|
307
|
+
|
|
308
|
+
const shouldRenderBubble = useIIFE(() => {
|
|
309
|
+
if (typingUsers.length === 0) return false;
|
|
310
|
+
if (!sbOptions.uikit.groupChannel.channel.enableTypingIndicator) return false;
|
|
311
|
+
if (!sbOptions.uikit.groupChannel.channel.typingIndicatorTypes.has(TypingIndicatorType.Bubble)) return false;
|
|
312
|
+
return true;
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
if (shouldRenderBubble) publish({ type: 'TYPING_BUBBLE_RENDERED' });
|
|
317
|
+
}, [shouldRenderBubble]);
|
|
318
|
+
|
|
319
|
+
if (!shouldRenderBubble) return null;
|
|
320
|
+
return (
|
|
321
|
+
<Box paddingHorizontal={16} marginTop={4} marginBottom={16}>
|
|
322
|
+
<TypingIndicatorBubble typingUsers={typingUsers} />
|
|
323
|
+
</Box>
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
|
|
295
327
|
export default React.memo(GroupChannelMessageRenderer);
|
|
@@ -4,7 +4,8 @@ import { View } from 'react-native';
|
|
|
4
4
|
import { Header, Icon, createStyleSheet, useHeaderStyle } from '@sendbird/uikit-react-native-foundation';
|
|
5
5
|
|
|
6
6
|
import ChannelCover from '../../../components/ChannelCover';
|
|
7
|
-
import { useLocalization } from '../../../hooks/useContext';
|
|
7
|
+
import { useLocalization, useSendbirdChat } from '../../../hooks/useContext';
|
|
8
|
+
import { TypingIndicatorType } from '../../../types';
|
|
8
9
|
import { GroupChannelContexts } from '../module/moduleContext';
|
|
9
10
|
import type { GroupChannelProps } from '../types';
|
|
10
11
|
|
|
@@ -13,11 +14,21 @@ const GroupChannelHeader = ({
|
|
|
13
14
|
onPressHeaderLeft,
|
|
14
15
|
onPressHeaderRight,
|
|
15
16
|
}: GroupChannelProps['Header']) => {
|
|
17
|
+
const { sbOptions } = useSendbirdChat();
|
|
16
18
|
const { headerTitle, channel } = useContext(GroupChannelContexts.Fragment);
|
|
17
19
|
const { typingUsers } = useContext(GroupChannelContexts.TypingIndicator);
|
|
18
20
|
const { STRINGS } = useLocalization();
|
|
19
21
|
const { HeaderComponent } = useHeaderStyle();
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
const renderSubtitle = () => {
|
|
24
|
+
const subtitle = STRINGS.LABELS.TYPING_INDICATOR_TYPINGS(typingUsers);
|
|
25
|
+
|
|
26
|
+
if (!subtitle) return null;
|
|
27
|
+
if (!sbOptions.uikit.groupChannel.channel.enableTypingIndicator) return null;
|
|
28
|
+
if (!sbOptions.uikit.groupChannel.channel.typingIndicatorTypes.has(TypingIndicatorType.Text)) return null;
|
|
29
|
+
|
|
30
|
+
return <Header.Subtitle style={styles.subtitle}>{subtitle}</Header.Subtitle>;
|
|
31
|
+
};
|
|
21
32
|
|
|
22
33
|
const isHidden = shouldHideRight();
|
|
23
34
|
|
|
@@ -29,7 +40,7 @@ const GroupChannelHeader = ({
|
|
|
29
40
|
<ChannelCover channel={channel} size={34} containerStyle={styles.avatarGroup} />
|
|
30
41
|
<View style={{ flexShrink: 1 }}>
|
|
31
42
|
<Header.Title h2>{headerTitle}</Header.Title>
|
|
32
|
-
{
|
|
43
|
+
{renderSubtitle()}
|
|
33
44
|
</View>
|
|
34
45
|
</View>
|
|
35
46
|
}
|
|
@@ -73,6 +73,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
|
|
|
73
73
|
useEffect(() => {
|
|
74
74
|
return subscribe(({ type }) => {
|
|
75
75
|
switch (type) {
|
|
76
|
+
case 'TYPING_BUBBLE_RENDERED':
|
|
76
77
|
case 'MESSAGES_RECEIVED': {
|
|
77
78
|
if (!props.scrolledAwayFromBottom) {
|
|
78
79
|
scrollToBottom(true);
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { ReplyType } from '@sendbird/chat/message';
|
|
4
4
|
import { useGroupChannelMessages } from '@sendbird/uikit-chat-hooks';
|
|
5
|
+
import { Box } from '@sendbird/uikit-react-native-foundation';
|
|
5
6
|
import {
|
|
6
7
|
NOOP,
|
|
7
8
|
PASS,
|
|
@@ -14,7 +15,9 @@ import {
|
|
|
14
15
|
useRefTracker,
|
|
15
16
|
} from '@sendbird/uikit-utils';
|
|
16
17
|
|
|
17
|
-
import GroupChannelMessageRenderer
|
|
18
|
+
import GroupChannelMessageRenderer, {
|
|
19
|
+
GroupChannelTypingIndicatorBubble,
|
|
20
|
+
} from '../components/GroupChannelMessageRenderer';
|
|
18
21
|
import NewMessagesButton from '../components/NewMessagesButton';
|
|
19
22
|
import ScrollToBottomButton from '../components/ScrollToBottomButton';
|
|
20
23
|
import StatusComposition from '../components/StatusComposition';
|
|
@@ -123,8 +126,13 @@ const createGroupChannelFragment = (initModule?: Partial<GroupChannelModule>): G
|
|
|
123
126
|
}, []);
|
|
124
127
|
|
|
125
128
|
const renderItem: GroupChannelProps['MessageList']['renderMessage'] = useFreshCallback((props) => {
|
|
126
|
-
|
|
127
|
-
return
|
|
129
|
+
const content = renderMessage ? renderMessage(props) : <GroupChannelMessageRenderer {...props} />;
|
|
130
|
+
return (
|
|
131
|
+
<Box>
|
|
132
|
+
{content}
|
|
133
|
+
{props.isFirstItem && !hasNext() && <GroupChannelTypingIndicatorBubble />}
|
|
134
|
+
</Box>
|
|
135
|
+
);
|
|
128
136
|
});
|
|
129
137
|
|
|
130
138
|
const memoizedFlatListProps = useMemo(
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Platform } from 'react-native';
|
|
|
2
2
|
|
|
3
3
|
import { Logger } from '@sendbird/uikit-utils';
|
|
4
4
|
|
|
5
|
+
import { PromisePolyfill } from './utils/promise';
|
|
6
|
+
|
|
5
7
|
/** Components **/
|
|
6
8
|
export { default as ChannelInput } from './components/ChannelInput';
|
|
7
9
|
export { default as ChannelMessageList } from './components/ChannelMessageList';
|
|
@@ -132,8 +134,10 @@ export { default as SendbirdUIKitContainer, SendbirdUIKit } from './containers/S
|
|
|
132
134
|
export type { SendbirdUIKitContainerProps } from './containers/SendbirdUIKitContainer';
|
|
133
135
|
export { default as SBUError } from './libs/SBUError';
|
|
134
136
|
export { default as SBUUtils } from './libs/SBUUtils';
|
|
135
|
-
|
|
136
137
|
export * from './types';
|
|
137
138
|
|
|
138
139
|
Logger.setLogLevel(__DEV__ ? 'warn' : 'none');
|
|
139
140
|
Logger.setTitle(`[UIKIT_${Platform.OS}]`);
|
|
141
|
+
|
|
142
|
+
// NOTE: In Hermes, not all implementations of Promise are included
|
|
143
|
+
PromisePolyfill.apply();
|