@@ -36,8 +36,9 @@ import type { CommonComponent } from '../../types';
3636import ChatFlatList from '../ChatFlatList' ;
3737import { ReactionAddons } from '../ReactionAddons' ;
3838
39- type PressActions = { onPress ?: ( ) => void ; onLongPress ?: ( ) => void } ;
39+ type PressActions = { onPress ?: ( ) => void ; onLongPress ?: ( ) => void ; bottomSheetItem ?: BottomSheetItem } ;
4040type HandleableMessage = SendbirdUserMessage | SendbirdFileMessage ;
41+ type CreateMessagePressActions = ( params : { message : SendbirdMessage } ) => PressActions ;
4142export 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