Skip to content

Commit 83f8710

Browse files
committed
feat: add bottomSheetItem to props of renderMessage
1 parent c83e2b4 commit 83f8710

File tree

1 file changed

+142
-114
lines changed
  • packages/uikit-react-native/src/components/ChannelMessageList

1 file changed

+142
-114
lines changed

packages/uikit-react-native/src/components/ChannelMessageList/index.tsx

Lines changed: 142 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ import type { CommonComponent } from '../../types';
3636
import ChatFlatList from '../ChatFlatList';
3737
import { ReactionAddons } from '../ReactionAddons';
3838

39-
type PressActions = { onPress?: () => void; onLongPress?: () => void };
39+
type PressActions = { onPress?: () => void; onLongPress?: () => void; bottomSheetItem?: BottomSheetItem };
4040
type HandleableMessage = SendbirdUserMessage | SendbirdFileMessage;
41+
type CreateMessagePressActions = (params: { message: SendbirdMessage }) => PressActions;
4142
export type ChannelMessageListProps<T extends SendbirdGroupChannel | SendbirdOpenChannel> = {
4243
enableMessageGrouping: boolean;
4344
currentUserId?: string;
@@ -74,6 +75,7 @@ export type ChannelMessageListProps<T extends SendbirdGroupChannel | SendbirdOpe
7475
channel: T;
7576
currentUserId?: ChannelMessageListProps<T>['currentUserId'];
7677
enableMessageGrouping: ChannelMessageListProps<T>['enableMessageGrouping'];
78+
bottomSheetItem?: BottomSheetItem;
7779
}) => React.ReactElement | null;
7880
renderNewMessagesButton: null | CommonComponent<{
7981
visible: boolean;
@@ -121,7 +123,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
121123
const { colors } = useUIKitTheme();
122124
const { show } = useUserProfile();
123125
const { left, right } = useSafeAreaInsets();
124-
const getMessagePressActions = useGetMessagePressActions({
126+
const createMessagePressActions = useCreateMessagePressActions({
125127
channel,
126128
currentUserId,
127129
onEditMessage,
@@ -134,7 +136,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
134136
const safeAreaLayout = { paddingLeft: left, paddingRight: right };
135137

136138
const renderItem: ListRenderItem<SendbirdMessage> = useFreshCallback(({ item, index }) => {
137-
const { onPress, onLongPress } = getMessagePressActions(item);
139+
const { onPress, onLongPress, bottomSheetItem } = createMessagePressActions({ message: item });
138140
return renderMessage({
139141
message: item,
140142
prevMessage: messages[index + 1],
@@ -147,6 +149,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
147149
channel,
148150
currentUserId,
149151
focused: (searchItem?.startingPoint ?? -1) === item.createdAt,
152+
bottomSheetItem,
150153
});
151154
});
152155

@@ -191,7 +194,7 @@ const ChannelMessageList = <T extends SendbirdGroupChannel | SendbirdOpenChannel
191194
);
192195
};
193196

194-
const useGetMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpenChannel>({
197+
const useCreateMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpenChannel>({
195198
channel,
196199
currentUserId,
197200
onResendFailedMessage,
@@ -208,7 +211,7 @@ const useGetMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpen
208211
| 'onDeleteMessage'
209212
| 'onResendFailedMessage'
210213
| 'onPressMediaMessage'
211-
>) => {
214+
>): CreateMessagePressActions => {
212215
const { colors } = useUIKitTheme();
213216
const { STRINGS } = useLocalization();
214217
const toast = useToast();
@@ -217,161 +220,186 @@ const useGetMessagePressActions = <T extends SendbirdGroupChannel | SendbirdOpen
217220
const { clipboardService, fileService } = usePlatformService();
218221
const { sbOptions } = useSendbirdChat();
219222

220-
const onFailureToReSend = (error: Error) => {
223+
const onResendFailure = (error: Error) => {
221224
toast.show(STRINGS.TOAST.RESEND_MSG_ERROR, 'error');
222225
Logger.error(STRINGS.TOAST.RESEND_MSG_ERROR, error);
223226
};
224227

225-
const handleFailedMessage = (message: HandleableMessage) => {
228+
const onDeleteFailure = (error: Error) => {
229+
toast.show(STRINGS.TOAST.DELETE_MSG_ERROR, 'error');
230+
Logger.error(STRINGS.TOAST.DELETE_MSG_ERROR, error);
231+
};
232+
233+
const onCopyText = (message: HandleableMessage) => {
234+
if (message.isUserMessage()) {
235+
clipboardService.setString(message.message || '');
236+
toast.show(STRINGS.TOAST.COPY_OK, 'success');
237+
}
238+
};
239+
240+
const onDownloadFile = (message: HandleableMessage) => {
241+
if (message.isFileMessage()) {
242+
if (toMegabyte(message.size) > 4) {
243+
toast.show(STRINGS.TOAST.DOWNLOAD_START, 'success');
244+
}
245+
246+
fileService
247+
.save({ fileUrl: message.url, fileName: message.name, fileType: message.type })
248+
.then((response) => {
249+
toast.show(STRINGS.TOAST.DOWNLOAD_OK, 'success');
250+
Logger.log('File saved to', response);
251+
})
252+
.catch((err) => {
253+
toast.show(STRINGS.TOAST.DOWNLOAD_ERROR, 'error');
254+
Logger.log('File save failure', err);
255+
});
256+
}
257+
};
258+
259+
const onOpenFile = (message: HandleableMessage) => {
260+
if (message.isFileMessage()) {
261+
const fileType = getFileType(message.type || getFileExtension(message.name));
262+
if (['image', 'video', 'audio'].includes(fileType)) {
263+
onPressMediaMessage?.(message, () => onDeleteMessage(message), getAvailableUriFromFileMessage(message));
264+
} else {
265+
SBUUtils.openURL(message.url);
266+
}
267+
}
268+
};
269+
270+
const openSheetForFailedMessage = (message: HandleableMessage) => {
226271
openSheet({
227272
sheetItems: [
228273
{
229274
title: STRINGS.LABELS.CHANNEL_MESSAGE_FAILED_RETRY,
230-
onPress: () => {
231-
onResendFailedMessage(message).catch(onFailureToReSend);
232-
},
275+
onPress: () => onResendFailedMessage(message).catch(onResendFailure),
233276
},
234277
{
235278
title: STRINGS.LABELS.CHANNEL_MESSAGE_FAILED_REMOVE,
236279
titleColor: colors.ui.dialog.default.none.destructive,
237-
onPress: () => confirmDelete(message),
280+
onPress: () => alertForMessageDelete(message),
238281
},
239282
],
240283
});
241284
};
242-
const confirmDelete = (message: HandleableMessage) => {
285+
286+
const alertForMessageDelete = (message: HandleableMessage) => {
243287
alert({
244288
title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_TITLE,
245289
buttons: [
246-
{
247-
text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_CANCEL,
248-
},
290+
{ text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_CANCEL },
249291
{
250292
text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_OK,
251293
style: 'destructive',
252294
onPress: () => {
253-
onDeleteMessage(message).catch(() => toast.show(STRINGS.TOAST.DELETE_MSG_ERROR, 'error'));
295+
onDeleteMessage(message).catch(onDeleteFailure);
254296
},
255297
},
256298
],
257299
});
258300
};
259301

260-
return (msg: SendbirdMessage) => {
261-
if (!msg.isUserMessage() && !msg.isFileMessage()) {
262-
return { onPress: undefined, onLongPress: undefined };
263-
}
302+
return ({ message }) => {
303+
if (!message.isUserMessage() && !message.isFileMessage()) return {};
264304

265305
const sheetItems: BottomSheetItem['sheetItems'] = [];
266-
const response: PressActions = {
267-
onPress: undefined,
268-
onLongPress: undefined,
269-
};
270-
271-
if (msg.isUserMessage()) {
272-
sheetItems.push({
273-
icon: 'copy',
306+
const menu = {
307+
copy: (message: HandleableMessage) => ({
308+
icon: 'copy' as const,
274309
title: STRINGS.LABELS.CHANNEL_MESSAGE_COPY,
275-
onPress: () => {
276-
clipboardService.setString(msg.message || '');
277-
toast.show(STRINGS.TOAST.COPY_OK, 'success');
278-
},
279-
});
280-
}
281-
if (!isVoiceMessage(msg) && msg.isFileMessage()) {
282-
sheetItems.push({
283-
icon: 'download',
310+
onPress: () => onCopyText(message),
311+
}),
312+
edit: (message: HandleableMessage) => ({
313+
icon: 'edit' as const,
314+
title: STRINGS.LABELS.CHANNEL_MESSAGE_EDIT,
315+
onPress: () => onEditMessage(message),
316+
}),
317+
delete: (message: HandleableMessage) => ({
318+
disabled: message.threadInfo ? message.threadInfo.replyCount > 0 : undefined,
319+
icon: 'delete' as const,
320+
title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE,
321+
onPress: () => alertForMessageDelete(message),
322+
}),
323+
reply: (message: HandleableMessage) => ({
324+
disabled: Boolean(message.parentMessageId),
325+
icon: 'reply' as const,
326+
title: STRINGS.LABELS.CHANNEL_MESSAGE_REPLY,
327+
onPress: () => onReplyMessage?.(message),
328+
}),
329+
download: (message: HandleableMessage) => ({
330+
icon: 'download' as const,
284331
title: STRINGS.LABELS.CHANNEL_MESSAGE_SAVE,
285-
onPress: async () => {
286-
if (toMegabyte(msg.size) > 4) {
287-
toast.show(STRINGS.TOAST.DOWNLOAD_START, 'success');
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-
}
332+
onPress: () => onDownloadFile(message),
333+
}),
334+
};
303335

304-
if (!channel.isEphemeral) {
305-
if (isMyMessage(msg, currentUserId) && msg.sendingStatus === 'succeeded') {
306-
if (msg.isUserMessage()) {
307-
sheetItems.push({
308-
icon: 'edit',
309-
title: STRINGS.LABELS.CHANNEL_MESSAGE_EDIT,
310-
onPress: () => onEditMessage(msg),
311-
});
336+
if (message.isUserMessage()) {
337+
sheetItems.push(menu.copy(message));
338+
if (!channel.isEphemeral) {
339+
if (isMyMessage(message, currentUserId) && message.sendingStatus === 'succeeded') {
340+
sheetItems.push(menu.edit(message));
341+
sheetItems.push(menu.delete(message));
342+
}
343+
if (channel.isGroupChannel() && sbOptions.uikit.groupChannel.channel.replyType === 'quote_reply') {
344+
sheetItems.push(menu.reply(message));
312345
}
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-
});
327346
}
328347
}
329348

330-
if (msg.isFileMessage()) {
331-
const fileType = getFileType(msg.type || getFileExtension(msg.name));
332-
switch (fileType) {
333-
case 'image':
334-
case 'video':
335-
case 'audio': {
336-
response.onPress = () => {
337-
onPressMediaMessage?.(msg, () => onDeleteMessage(msg), getAvailableUriFromFileMessage(msg));
338-
};
339-
break;
349+
if (message.isFileMessage()) {
350+
if (!isVoiceMessage(message)) {
351+
sheetItems.push(menu.download(message));
352+
}
353+
if (!channel.isEphemeral) {
354+
if (isMyMessage(message, currentUserId) && message.sendingStatus === 'succeeded') {
355+
sheetItems.push(menu.delete(message));
340356
}
341-
default: {
342-
response.onPress = () => SBUUtils.openURL(msg.url);
343-
break;
357+
if (channel.isGroupChannel() && sbOptions.uikit.groupChannel.channel.replyType === 'quote_reply') {
358+
sheetItems.push(menu.reply(message));
344359
}
345360
}
346361
}
347362

348-
if (sheetItems.length > 0) {
349-
response.onLongPress = () => {
350-
openSheet({
351-
sheetItems,
352-
HeaderComponent: shouldRenderReaction(
353-
channel,
354-
sbOptions.uikitWithAppInfo.groupChannel.channel.enableReactions,
355-
)
356-
? ({ onClose }) => <ReactionAddons.BottomSheet message={msg} channel={channel} onClose={onClose} />
357-
: undefined,
358-
});
359-
};
360-
}
363+
const bottomSheetItem: BottomSheetItem = {
364+
sheetItems,
365+
HeaderComponent: shouldRenderReaction(channel, sbOptions.uikitWithAppInfo.groupChannel.channel.enableReactions)
366+
? ({ onClose }) => <ReactionAddons.BottomSheet message={message} channel={channel} onClose={onClose} />
367+
: undefined,
368+
};
361369

362-
if (msg.sendingStatus === 'failed') {
363-
response.onLongPress = () => handleFailedMessage(msg);
364-
response.onPress = () => {
365-
onResendFailedMessage(msg).catch(onFailureToReSend);
366-
};
367-
}
370+
switch (true) {
371+
case message.sendingStatus === 'pending': {
372+
return {
373+
onPress: undefined,
374+
onLongPress: undefined,
375+
bottomSheetItem: undefined,
376+
};
377+
}
368378

369-
if (msg.sendingStatus === 'pending') {
370-
response.onLongPress = undefined;
371-
response.onPress = undefined;
372-
}
379+
case message.sendingStatus === 'failed': {
380+
return {
381+
onPress: () => onResendFailedMessage(message).catch(onResendFailure),
382+
onLongPress: () => openSheetForFailedMessage(message),
383+
bottomSheetItem,
384+
};
385+
}
386+
387+
case message.isFileMessage(): {
388+
return {
389+
onPress: () => onOpenFile(message),
390+
onLongPress: () => openSheet(bottomSheetItem),
391+
bottomSheetItem,
392+
};
393+
}
373394

374-
return response;
395+
default: {
396+
return {
397+
onPress: undefined,
398+
onLongPress: () => openSheet(bottomSheetItem),
399+
bottomSheetItem,
400+
};
401+
}
402+
}
375403
};
376404
};
377405

0 commit comments

Comments
 (0)