agora-appbuilder-core 4.0.30 → 4.0.31-beta-2

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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/template/customization-api/sub-components.ts +1 -0
  3. package/template/defaultConfig.js +2 -2
  4. package/template/package.json +1 -1
  5. package/template/src/components/ChatContext.ts +3 -0
  6. package/template/src/components/Controls.tsx +80 -18
  7. package/template/src/components/Navbar.tsx +29 -1
  8. package/template/src/components/chat/chatConfigure.native.tsx +85 -25
  9. package/template/src/components/chat/chatConfigure.tsx +99 -9
  10. package/template/src/components/chat-messages/useChatMessages.tsx +32 -39
  11. package/template/src/components/chat-ui/useChatUIControls.tsx +21 -0
  12. package/template/src/components/precall/joinWaitingRoomBtn.tsx +25 -6
  13. package/template/src/components/room-info/useRoomInfo.tsx +12 -0
  14. package/template/src/pages/Create.tsx +26 -1
  15. package/template/src/pages/video-call/VideoCallMobileView.tsx +3 -2
  16. package/template/src/pages/video-call/VideoCallScreen.tsx +3 -1
  17. package/template/src/subComponents/ChatBubble.tsx +388 -110
  18. package/template/src/subComponents/ChatContainer.tsx +76 -53
  19. package/template/src/subComponents/ChatInput.native.tsx +93 -14
  20. package/template/src/subComponents/ChatInput.tsx +41 -7
  21. package/template/src/subComponents/caption/Caption.tsx +14 -2
  22. package/template/src/subComponents/caption/CaptionContainer.tsx +24 -3
  23. package/template/src/subComponents/caption/CaptionText.tsx +13 -1
  24. package/template/src/subComponents/chat/ChatActionMenu.tsx +40 -3
  25. package/template/src/subComponents/chat/ChatAttachment.native.tsx +4 -2
  26. package/template/src/subComponents/chat/ChatEmoji.native.tsx +181 -22
  27. package/template/src/subComponents/chat/ChatEmoji.tsx +4 -3
  28. package/template/src/subComponents/chat/ChatQuickActionsMenu.tsx +70 -34
  29. package/template/src/subComponents/chat/ChatSendButton.tsx +42 -1
  30. package/template/src/subComponents/chat/PinnedMessage.tsx +207 -0
  31. package/template/src/utils/useCreateRoom.ts +23 -4
  32. package/template/src/utils/useJoinRoom.ts +76 -23
@@ -26,7 +26,12 @@ import Hyperlink from 'react-native-hyperlink';
26
26
  import {useString} from '../utils/useString';
27
27
  import {ChatBubbleProps} from '../components/ChatContext';
28
28
  import {isMobileUA, isWebInternal, trimText} from '../utils/common';
29
- import {useChatUIControls, useContent, useLocalUid} from 'customization-api';
29
+ import {
30
+ useChatUIControls,
31
+ useContent,
32
+ useLocalUid,
33
+ IconButton,
34
+ } from 'customization-api';
30
35
  import ThemeConfig from '../theme';
31
36
  import hexadecimalTransparency from '../utils/hexadecimalTransparency';
32
37
  import {containsOnlyEmojis, formatAMPM, isURL} from '../utils';
@@ -34,14 +39,18 @@ import {ChatType, UploadStatus} from '../components/chat-ui/useChatUIControls';
34
39
  import ImageIcon from '../atoms/ImageIcon';
35
40
  import {ChatActionMenu, MoreMenu} from './chat/ChatActionMenu';
36
41
  import ImagePopup from './chat/ImagePopup';
37
- import {ChatMessageType} from '../components/chat-messages/useChatMessages';
42
+ import {
43
+ ChatMessageType,
44
+ useChatMessages,
45
+ } from '../components/chat-messages/useChatMessages';
38
46
  import {
39
47
  chatMsgDeletedText,
40
48
  videoRoomUserFallbackText,
41
49
  } from '../language/default-labels/videoCallScreenLabels';
42
- import {ReactionPicker} from './chat/ChatEmoji';
50
+ import {ReactionPicker, CustomReactionPicker} from './chat/ChatEmoji';
43
51
  import {useChatConfigure} from '../../src/components/chat/chatConfigure';
44
52
  import Tooltip from '../../src/atoms/Tooltip';
53
+ import {MoreMessageOptions} from './chat/ChatQuickActionsMenu';
45
54
 
46
55
  type AttachmentBubbleProps = {
47
56
  fileName: string;
@@ -103,6 +112,177 @@ export const AttachmentBubble: React.FC<AttachmentBubbleProps> = ({
103
112
  );
104
113
  };
105
114
 
115
+ export const ReplyMessageBubble = ({
116
+ repliedMsgId,
117
+ replyTxt,
118
+ showCoseIcon = false,
119
+ showPreview = true,
120
+ }) => {
121
+ const {messageStore, privateMessageStore} = useChatMessages();
122
+ const {defaultContent} = useContent();
123
+ const {setReplyToMsgId, chatType, privateChatUser} = useChatUIControls();
124
+ const localUid = useLocalUid();
125
+ let repliedMsg = [];
126
+ const msgStore =
127
+ chatType === ChatType.Group
128
+ ? messageStore
129
+ : privateMessageStore[privateChatUser] || messageStore;
130
+ repliedMsg = msgStore.filter(msg => msg.msgId === repliedMsgId);
131
+ if (repliedMsg.length == 0 && chatType === ChatType.Private) {
132
+ repliedMsg = messageStore.filter(msg => msg.msgId === repliedMsgId);
133
+ }
134
+ const isAttachMsg = repliedMsg[0]?.type !== ChatMessageType.TXT;
135
+
136
+ let time = formatAMPM(new Date(repliedMsg[0]?.createdTimestamp));
137
+ const name =
138
+ localUid === repliedMsg[0]?.uid
139
+ ? 'You'
140
+ : trimText(defaultContent[repliedMsg[0]?.uid]?.name);
141
+ const text = repliedMsg[0]?.msg;
142
+ const fileName = `[${repliedMsg[0]?.fileName}]`;
143
+ const fileExt = repliedMsg[0]?.ext;
144
+
145
+ return (
146
+ <View style={style.repliedMsgContainer}>
147
+ <View
148
+ style={{
149
+ flexDirection: 'row',
150
+ gap: 8,
151
+ alignItems: 'center',
152
+ }}>
153
+ <View style={style.repliedMsgContent}>
154
+ {isAttachMsg ? (
155
+ repliedMsg[0]?.type === ChatMessageType.IMAGE ? (
156
+ <Image
157
+ source={{uri: repliedMsg[0]?.thumb}}
158
+ style={style.replyPreviewImg}
159
+ />
160
+ ) : (
161
+ <ImageIcon
162
+ base64={true}
163
+ iconSize={32}
164
+ iconType="plain"
165
+ name={
166
+ fileExt === 'pdf'
167
+ ? 'chat_attachment_pdf'
168
+ : fileExt === 'doc' || fileExt === 'docx'
169
+ ? 'chat_attachment_doc'
170
+ : 'chat_attachment_unknown'
171
+ }
172
+ tintColor={$config.SEMANTIC_NEUTRAL}
173
+ />
174
+ )
175
+ ) : (
176
+ // <Text style={style.messageStyle}>{text}</Text>
177
+ <></>
178
+ )}
179
+ </View>
180
+ <View>
181
+ <View style={{display: 'flex', flexDirection: 'row'}}>
182
+ <Text
183
+ style={style.userNameStyle}
184
+ numberOfLines={1}
185
+ ellipsizeMode="tail">
186
+ {name}
187
+ </Text>
188
+ <Text
189
+ style={style.timestampStyle}
190
+ numberOfLines={1}
191
+ ellipsizeMode="tail">
192
+ {time}
193
+ </Text>
194
+ </View>
195
+ {isAttachMsg ? (
196
+ <Text
197
+ style={style.replyMessageStyle}
198
+ numberOfLines={1}
199
+ ellipsizeMode="tail">
200
+ {fileName + ' ' + text}
201
+ </Text>
202
+ ) : (
203
+ <Text style={[style.messageStyle, {maxWidth: 265}]}>{text}</Text>
204
+ )}
205
+ </View>
206
+ </View>
207
+ {showCoseIcon && (
208
+ <View>
209
+ <IconButton
210
+ hoverEffect={false}
211
+ hoverEffectStyle={{
212
+ backgroundColor: $config.ICON_BG_COLOR,
213
+ borderRadius: 20,
214
+ }}
215
+ iconProps={{
216
+ iconType: 'plain',
217
+ iconContainerStyle: {
218
+ padding: 5,
219
+ },
220
+ iconSize: 20,
221
+ name: 'close',
222
+ tintColor: $config.SECONDARY_ACTION_COLOR,
223
+ }}
224
+ onPress={() => {
225
+ // handleEmojiClose();
226
+ // setShowEmojiPicker(false);
227
+ setReplyToMsgId('');
228
+ }}
229
+ />
230
+ </View>
231
+ )}
232
+ </View>
233
+ );
234
+ };
235
+
236
+ const ChatLastMsgOptions = ({msgId, isLocal, userId, type, message}) => {
237
+ const {setReplyToMsgId} = useChatUIControls();
238
+ return (
239
+ <View
240
+ style={[
241
+ style.chatLastMsgOptions,
242
+ isLocal ? style.lastOptionsLocalView : style.lastOptionsRemoteView,
243
+ ]}>
244
+ <View>
245
+ <IconButton
246
+ hoverEffect={false}
247
+ hoverEffectStyle={{
248
+ backgroundColor: $config.ICON_BG_COLOR,
249
+ borderRadius: 20,
250
+ }}
251
+ iconProps={{
252
+ iconType: 'plain',
253
+ iconContainerStyle: {
254
+ padding: 2,
255
+ },
256
+ iconSize: 20,
257
+ name: 'reply',
258
+ tintColor: $config.SECONDARY_ACTION_COLOR,
259
+ }}
260
+ onPress={() => {
261
+ setReplyToMsgId(msgId);
262
+ }}
263
+ />
264
+ </View>
265
+ <View>
266
+ <CustomReactionPicker
267
+ isLocal={isLocal}
268
+ messageId={msgId}
269
+ setIsHovered={() => {}}
270
+ />
271
+ </View>
272
+
273
+ <MoreMessageOptions
274
+ userId={userId}
275
+ isLocal={isLocal}
276
+ messageId={msgId}
277
+ type={type}
278
+ message={message}
279
+ showReplyOption={false}
280
+ setIsHovered={() => {}}
281
+ />
282
+ </View>
283
+ );
284
+ };
285
+
106
286
  const ChatBubble = (props: ChatBubbleProps) => {
107
287
  const {defaultContent} = useContent();
108
288
  const {chatType, privateChatUser} = useChatUIControls();
@@ -130,6 +310,8 @@ const ChatBubble = (props: ChatBubbleProps) => {
130
310
  ext,
131
311
  reactions,
132
312
  scrollOffset,
313
+ replyToMsgId,
314
+ isLastMsg,
133
315
  } = props;
134
316
 
135
317
  const localUid = useLocalUid();
@@ -190,6 +372,7 @@ const ChatBubble = (props: ChatBubbleProps) => {
190
372
  ext,
191
373
  previousMessageCreatedTimestamp,
192
374
  reactions,
375
+ replyToMsgId,
193
376
  )
194
377
  ) : (
195
378
  <>
@@ -284,19 +467,33 @@ const ChatBubble = (props: ChatBubbleProps) => {
284
467
  textDecorationLine: 'underline',
285
468
  }}>
286
469
  {type === ChatMessageType.TXT && (
287
- <Text
288
- style={[
289
- style.messageStyle,
290
- containsOnlyEmojis(message)
291
- ? {fontSize: 24, lineHeight: 32}
292
- : {fontSize: 14, lineHeight: 20},
293
- ]}
294
- selectable={true}>
295
- {message}
296
- </Text>
470
+ <>
471
+ {replyToMsgId && (
472
+ <ReplyMessageBubble
473
+ repliedMsgId={replyToMsgId}
474
+ replyTxt={message}
475
+ />
476
+ )}
477
+ <Text
478
+ style={[
479
+ style.messageStyle,
480
+ containsOnlyEmojis(message)
481
+ ? {fontSize: 24, lineHeight: 32}
482
+ : {fontSize: 14, lineHeight: 20},
483
+ ]}
484
+ selectable={true}>
485
+ {message}
486
+ </Text>
487
+ </>
297
488
  )}
298
489
  {type === ChatMessageType.IMAGE && (
299
490
  <View>
491
+ {replyToMsgId && (
492
+ <ReplyMessageBubble
493
+ repliedMsgId={replyToMsgId}
494
+ replyTxt={message}
495
+ />
496
+ )}
300
497
  <TouchableOpacity
301
498
  style={{
302
499
  justifyContent: 'center',
@@ -343,30 +540,38 @@ const ChatBubble = (props: ChatBubbleProps) => {
343
540
  </View>
344
541
  )}
345
542
  {type === ChatMessageType.FILE && (
346
- <AttachmentBubble
347
- fileName={fileName}
348
- fileExt={ext}
349
- msg={message}
350
- secondaryComponent={
351
- <View>
352
- <MoreMenu
353
- ref={moreIconRef}
354
- setActionMenuVisible={setActionMenuVisible}
355
- />
356
- <ChatActionMenu
357
- actionMenuVisible={actionMenuVisible}
358
- setActionMenuVisible={setActionMenuVisible}
359
- btnRef={moreIconRef}
360
- fileName={fileName}
361
- fileUrl={url}
362
- msgId={msgId}
363
- privateChatUser={privateChatUser}
364
- isLocal={isLocal}
365
- userId={uid}
366
- />
367
- </View>
368
- }
369
- />
543
+ <>
544
+ {replyToMsgId && (
545
+ <ReplyMessageBubble
546
+ repliedMsgId={replyToMsgId}
547
+ replyTxt={message}
548
+ />
549
+ )}
550
+ <AttachmentBubble
551
+ fileName={fileName}
552
+ fileExt={ext}
553
+ msg={message}
554
+ secondaryComponent={
555
+ <View>
556
+ <MoreMenu
557
+ ref={moreIconRef}
558
+ setActionMenuVisible={setActionMenuVisible}
559
+ />
560
+ <ChatActionMenu
561
+ actionMenuVisible={actionMenuVisible}
562
+ setActionMenuVisible={setActionMenuVisible}
563
+ btnRef={moreIconRef}
564
+ fileName={fileName}
565
+ fileUrl={url}
566
+ msgId={msgId}
567
+ privateChatUser={privateChatUser}
568
+ isLocal={isLocal}
569
+ userId={uid}
570
+ />
571
+ </View>
572
+ }
573
+ />
574
+ </>
370
575
  )}
371
576
  </Hyperlink>
372
577
  )}
@@ -376,80 +581,96 @@ const ChatBubble = (props: ChatBubbleProps) => {
376
581
  }}
377
582
  </PlatformWrapper>
378
583
  {!isDeleted && (
379
- <View
380
- style={[
381
- style.reactionContainer,
382
- isLocal ? style.reactionLocalView : style.reactionRemoteView,
383
- ]}>
384
- {reactions?.map((reactionObj, index) => {
385
- const msg =
386
- reactionObj.userList.length > 3
387
- ? `${reactionObj.userList
388
- .slice(0, 2)
389
- .map(uid => {
390
- return Number(uid) === localUid
391
- ? 'You(click to remove)'
392
- : defaultContent[uid]?.name || 'User';
393
- })
394
- .join(', ')} and ${reactionObj.userList.length - 2} others`
395
- : `${reactionObj.userList
396
- .map(uid => {
397
- return Number(uid) === localUid
398
- ? 'You(click to remove)'
399
- : defaultContent[uid]?.name || 'User';
400
- })
401
- .join(', ')}`;
584
+ <View>
585
+ <View
586
+ style={[
587
+ style.reactionContainer,
588
+ isLocal ? style.reactionLocalView : style.reactionRemoteView,
589
+ ]}>
590
+ {reactions?.map((reactionObj, index) => {
591
+ const msg =
592
+ reactionObj.userList.length > 3
593
+ ? `${reactionObj.userList
594
+ .slice(0, 2)
595
+ .map(uid => {
596
+ return Number(uid) === localUid
597
+ ? 'You(click to remove)'
598
+ : defaultContent[uid]?.name || 'User';
599
+ })
600
+ .join(', ')} and ${
601
+ reactionObj.userList.length - 2
602
+ } others`
603
+ : `${reactionObj.userList
604
+ .map(uid => {
605
+ return Number(uid) === localUid
606
+ ? 'You(click to remove)'
607
+ : defaultContent[uid]?.name || 'User';
608
+ })
609
+ .join(', ')}`;
402
610
 
403
- return reactionObj.count > 0 ? (
404
- <Tooltip
405
- scrollY={scrollOffset}
406
- key={`${uid}-${reactionObj.reaction}`}
407
- // toolTipMessage={msg}
408
- toolTipMessage={
409
- <Text>
410
- <Text style={{color: $config.FONT_COLOR, fontSize: 12}}>
411
- {msg}
412
- </Text>
413
- <Text
414
- style={{
415
- color:
416
- $config.FONT_COLOR + hexadecimalTransparency['40%'],
417
- fontSize: 12,
418
- }}>
419
- {' '}
420
- reacted with{' '}
421
- </Text>
422
- <Text>{reactionObj.reaction}</Text>
423
- </Text>
424
- }
425
- containerStyle={`max-width:200px;z-index:100000;`}
426
- placement={`${isLocal ? 'left' : 'right'}`}
427
- fontSize={12}
428
- renderContent={(isToolTipVisible, setToolTipVisible) => {
429
- return (
430
- <TouchableOpacity
431
- style={style.reactionWrapper}
432
- onPress={() => {
433
- if (
434
- reactionObj.userList.includes(localUid.toString())
435
- ) {
436
- removeReaction(msgId, reactionObj.reaction);
437
- } else {
438
- addReaction(msgId, reactionObj.reaction);
439
- }
440
- }}>
441
- <Text style={{fontSize: 10}}>{reactionObj.reaction}</Text>
442
- <Text style={style.reactionCount}>
443
- {reactionObj.count}
611
+ return reactionObj.count > 0 ? (
612
+ <Tooltip
613
+ scrollY={scrollOffset}
614
+ key={`${uid}-${reactionObj.reaction}`}
615
+ // toolTipMessage={msg}
616
+ toolTipMessage={
617
+ <Text>
618
+ <Text style={{color: $config.FONT_COLOR, fontSize: 12}}>
619
+ {msg}
444
620
  </Text>
445
- </TouchableOpacity>
446
- );
447
- }}
448
- />
449
- ) : (
450
- <></>
451
- );
452
- })}
621
+ <Text
622
+ style={{
623
+ color:
624
+ $config.FONT_COLOR + hexadecimalTransparency['40%'],
625
+ fontSize: 12,
626
+ }}>
627
+ {' '}
628
+ reacted with{' '}
629
+ </Text>
630
+ <Text>{reactionObj.reaction}</Text>
631
+ </Text>
632
+ }
633
+ containerStyle={`max-width:200px;z-index:100000;`}
634
+ placement={`${isLocal ? 'left' : 'right'}`}
635
+ fontSize={12}
636
+ renderContent={(isToolTipVisible, setToolTipVisible) => {
637
+ return (
638
+ <TouchableOpacity
639
+ style={style.reactionWrapper}
640
+ onPress={() => {
641
+ if (
642
+ reactionObj.userList.includes(localUid.toString())
643
+ ) {
644
+ removeReaction(msgId, reactionObj.reaction);
645
+ } else {
646
+ addReaction(msgId, reactionObj.reaction);
647
+ }
648
+ }}>
649
+ <Text style={{fontSize: 10}}>
650
+ {reactionObj.reaction}
651
+ </Text>
652
+ <Text style={style.reactionCount}>
653
+ {reactionObj.count}
654
+ </Text>
655
+ </TouchableOpacity>
656
+ );
657
+ }}
658
+ />
659
+ ) : (
660
+ <></>
661
+ );
662
+ })}
663
+ </View>
664
+
665
+ {isLastMsg && (
666
+ <ChatLastMsgOptions
667
+ msgId={msgId}
668
+ isLocal={isLocal}
669
+ userId={uid}
670
+ type={type}
671
+ message={message}
672
+ />
673
+ )}
453
674
  </View>
454
675
  )}
455
676
  </>
@@ -478,7 +699,13 @@ const PlatformWrapper = ({children, isLocal, isChatBubble = true}) => {
478
699
  onMouseLeave: handleMouseLeave,
479
700
  })
480
701
  ) : (
481
- <>{children(false)}</>
702
+ <Pressable
703
+ onPress={() => {
704
+ setIsHovered(prev => !prev);
705
+ }}
706
+ style={{}}>
707
+ {children(isHovered, setIsHovered)}
708
+ </Pressable>
482
709
  );
483
710
  };
484
711
 
@@ -570,6 +797,16 @@ const style = StyleSheet.create({
570
797
  fontSize: ThemeConfig.FontSize.small,
571
798
  lineHeight: ThemeConfig.FontSize.small * 1.45,
572
799
  color: $config.FONT_COLOR,
800
+ flexWrap: 'wrap',
801
+ },
802
+ replyMessageStyle: {
803
+ fontFamily: ThemeConfig.FontFamily.sansPro,
804
+ fontWeight: '400',
805
+ fontSize: ThemeConfig.FontSize.tiny,
806
+ lineHeight: ThemeConfig.FontSize.small,
807
+ color: $config.FONT_COLOR + hexadecimalTransparency['40%'],
808
+ marginTop: 4,
809
+ maxWidth: 200,
573
810
  },
574
811
  previewImg: {
575
812
  width: 256,
@@ -577,6 +814,13 @@ const style = StyleSheet.create({
577
814
  resizeMode: 'cover',
578
815
  borderRadius: 4,
579
816
  },
817
+ replyPreviewImg: {
818
+ width: 36,
819
+ height: 36,
820
+ borderRadius: 2,
821
+ resizeMode: 'cover',
822
+ },
823
+
580
824
  chatBubbleViewImg: {
581
825
  paddingHorizontal: 6,
582
826
  paddingVertical: 6,
@@ -665,6 +909,40 @@ const style = StyleSheet.create({
665
909
  reactionUserList: {
666
910
  position: 'absolute',
667
911
  },
912
+ repliedMsgContainer: {
913
+ backgroundColor:
914
+ $config.CARD_LAYER_1_COLOR + hexadecimalTransparency['45%'],
915
+ borderRadius: 4,
916
+ borderLeftWidth: 2,
917
+ padding: 8,
918
+ borderLeftColor: $config.SEMANTIC_NEUTRAL + hexadecimalTransparency['80%'],
919
+ display: 'flex',
920
+ flexDirection: 'row',
921
+ justifyContent: 'space-between',
922
+ alignItems: 'flex-start',
923
+ marginBottom: 8,
924
+ },
925
+ repliedMsgContent: {
926
+ maxHeight: 100,
927
+ overflow: 'scroll',
928
+ },
929
+ chatLastMsgOptions: {
930
+ flexDirection: 'row',
931
+ justifyContent: 'space-evenly',
932
+ alignItems: 'center',
933
+ },
934
+ lastOptionsLocalView: {
935
+ alignSelf: 'flex-end',
936
+ marginVertical: 2,
937
+ marginHorizontal: 12,
938
+ position: 'relative',
939
+ },
940
+ lastOptionsRemoteView: {
941
+ alignSelf: 'flex-start',
942
+ marginVertical: 2,
943
+ marginHorizontal: 12,
944
+ position: 'relative',
945
+ },
668
946
  });
669
947
 
670
948
  export default ChatBubble;