stream-chat-react-native-core 5.24.0-beta.3 → 5.24.0-beta.4

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 (119) hide show
  1. package/lib/commonjs/components/Channel/Channel.js +30 -22
  2. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  3. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +2 -0
  4. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  5. package/lib/commonjs/components/Message/Message.js +28 -6
  6. package/lib/commonjs/components/Message/Message.js.map +1 -1
  7. package/lib/commonjs/components/Message/MessageSimple/MessageBounce.js +84 -0
  8. package/lib/commonjs/components/Message/MessageSimple/MessageBounce.js.map +1 -0
  9. package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  10. package/lib/commonjs/components/Message/hooks/useMessageActions.js.map +1 -1
  11. package/lib/commonjs/components/index.js +11 -0
  12. package/lib/commonjs/components/index.js.map +1 -1
  13. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +18 -8
  14. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  15. package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js +4 -8
  16. package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
  17. package/lib/commonjs/contexts/messagesContext/MessagesContext.js +2 -2
  18. package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
  19. package/lib/commonjs/i18n/en.json +4 -1
  20. package/lib/commonjs/i18n/es.json +4 -1
  21. package/lib/commonjs/i18n/fr.json +49 -46
  22. package/lib/commonjs/i18n/he.json +4 -1
  23. package/lib/commonjs/i18n/hi.json +49 -46
  24. package/lib/commonjs/i18n/it.json +49 -46
  25. package/lib/commonjs/i18n/ja.json +4 -1
  26. package/lib/commonjs/i18n/ko.json +4 -1
  27. package/lib/commonjs/i18n/nl.json +49 -46
  28. package/lib/commonjs/i18n/ru.json +49 -46
  29. package/lib/commonjs/i18n/tr.json +49 -46
  30. package/lib/commonjs/utils/removeReservedFields.js +1 -1
  31. package/lib/commonjs/utils/removeReservedFields.js.map +1 -1
  32. package/lib/commonjs/utils/utils.js +5 -1
  33. package/lib/commonjs/utils/utils.js.map +1 -1
  34. package/lib/commonjs/version.json +1 -1
  35. package/lib/module/components/Channel/Channel.js +30 -22
  36. package/lib/module/components/Channel/Channel.js.map +1 -1
  37. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +2 -0
  38. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  39. package/lib/module/components/Message/Message.js +28 -6
  40. package/lib/module/components/Message/Message.js.map +1 -1
  41. package/lib/module/components/Message/MessageSimple/MessageBounce.js +84 -0
  42. package/lib/module/components/Message/MessageSimple/MessageBounce.js.map +1 -0
  43. package/lib/module/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  44. package/lib/module/components/Message/hooks/useMessageActions.js.map +1 -1
  45. package/lib/module/components/index.js +11 -0
  46. package/lib/module/components/index.js.map +1 -1
  47. package/lib/module/contexts/messageInputContext/MessageInputContext.js +18 -8
  48. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  49. package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js +4 -8
  50. package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
  51. package/lib/module/contexts/messagesContext/MessagesContext.js +2 -2
  52. package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
  53. package/lib/module/i18n/en.json +4 -1
  54. package/lib/module/i18n/es.json +4 -1
  55. package/lib/module/i18n/fr.json +49 -46
  56. package/lib/module/i18n/he.json +4 -1
  57. package/lib/module/i18n/hi.json +49 -46
  58. package/lib/module/i18n/it.json +49 -46
  59. package/lib/module/i18n/ja.json +4 -1
  60. package/lib/module/i18n/ko.json +4 -1
  61. package/lib/module/i18n/nl.json +49 -46
  62. package/lib/module/i18n/ru.json +49 -46
  63. package/lib/module/i18n/tr.json +49 -46
  64. package/lib/module/utils/removeReservedFields.js +1 -1
  65. package/lib/module/utils/removeReservedFields.js.map +1 -1
  66. package/lib/module/utils/utils.js +5 -1
  67. package/lib/module/utils/utils.js.map +1 -1
  68. package/lib/module/version.json +1 -1
  69. package/lib/typescript/components/Channel/Channel.d.ts +1 -1
  70. package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts +1 -1
  71. package/lib/typescript/components/Message/Message.d.ts +1 -1
  72. package/lib/typescript/components/Message/MessageSimple/MessageBounce.d.ts +12 -0
  73. package/lib/typescript/components/MessageList/utils/getReadStates.d.ts +1 -0
  74. package/lib/typescript/components/index.d.ts +1 -0
  75. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +6 -2
  76. package/lib/typescript/contexts/messageInputContext/hooks/useMessageDetailsForState.d.ts +1 -2
  77. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +7 -2
  78. package/lib/typescript/i18n/en.json +4 -1
  79. package/lib/typescript/i18n/es.json +4 -1
  80. package/lib/typescript/i18n/fr.json +49 -46
  81. package/lib/typescript/i18n/he.json +4 -1
  82. package/lib/typescript/i18n/hi.json +49 -46
  83. package/lib/typescript/i18n/it.json +49 -46
  84. package/lib/typescript/i18n/ja.json +4 -1
  85. package/lib/typescript/i18n/ko.json +4 -1
  86. package/lib/typescript/i18n/nl.json +49 -46
  87. package/lib/typescript/i18n/ru.json +49 -46
  88. package/lib/typescript/i18n/tr.json +49 -46
  89. package/lib/typescript/utils/Streami18n.d.ts +3 -0
  90. package/lib/typescript/utils/removeReservedFields.d.ts +2 -1
  91. package/lib/typescript/utils/utils.d.ts +11 -0
  92. package/package.json +2 -2
  93. package/src/components/Channel/Channel.tsx +26 -7
  94. package/src/components/Channel/hooks/useCreateMessagesContext.ts +2 -0
  95. package/src/components/Message/Message.tsx +26 -2
  96. package/src/components/Message/MessageSimple/MessageBounce.tsx +119 -0
  97. package/src/components/Message/hooks/useMessageActionHandlers.ts +1 -2
  98. package/src/components/Message/hooks/useMessageActions.tsx +1 -1
  99. package/src/components/index.ts +1 -0
  100. package/src/contexts/messageInputContext/MessageInputContext.tsx +24 -6
  101. package/src/contexts/messageInputContext/__tests__/sendMessage.test.tsx +8 -8
  102. package/src/contexts/messageInputContext/__tests__/updateMessage.test.tsx +1 -1
  103. package/src/contexts/messageInputContext/__tests__/useMessageDetailsForState.test.tsx +6 -1
  104. package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts +3 -9
  105. package/src/contexts/messagesContext/MessagesContext.tsx +7 -2
  106. package/src/i18n/en.json +4 -1
  107. package/src/i18n/es.json +4 -1
  108. package/src/i18n/fr.json +49 -46
  109. package/src/i18n/he.json +4 -1
  110. package/src/i18n/hi.json +49 -46
  111. package/src/i18n/it.json +49 -46
  112. package/src/i18n/ja.json +4 -1
  113. package/src/i18n/ko.json +4 -1
  114. package/src/i18n/nl.json +49 -46
  115. package/src/i18n/ru.json +49 -46
  116. package/src/i18n/tr.json +49 -46
  117. package/src/utils/removeReservedFields.ts +5 -2
  118. package/src/utils/utils.ts +16 -0
  119. package/src/version.json +1 -1
@@ -1,19 +1,21 @@
1
1
  {
2
- "1 Reply": "1 Cevap",
2
+ "1 Reply": "",
3
3
  "1 Thread Reply": "",
4
- "Allow access to your Gallery": "Galerinize erişime izin verin",
5
- "Allow camera access in device settings": "Cihaz ayarlarında kamera erişimine izin ver",
6
- "Also send to channel": "",
7
- "Are you sure you want to permanently delete this message?": "",
8
- "Block User": "",
9
- "Cancel": "",
10
- "Cannot Flag Message": "",
11
- "Copy Message": "",
12
- "Delete": "",
13
- "Delete Message": "",
14
- "Device camera is used to take photos or videos.": "Cihaz kamerası fotoğraf veya video çekmek için kullanılır.",
15
- "Do you want to send a copy of this message to a moderator for further investigation?": "",
16
- "Edit Message": "",
4
+ "Allow access to your Gallery": "",
5
+ "Allow camera access in device settings": "",
6
+ "Also send to channel": "Kanala da gönder",
7
+ "Are you sure you want to permanently delete this message?": "Bu mesajı kalıcı olarak silmek istediğinizden emin misiniz?",
8
+ "Are you sure?": "Emin misiniz?",
9
+ "Block User": "Kullanıcıyı engelle",
10
+ "Cancel": "İptal",
11
+ "Cannot Flag Message": "Raporlama Başarısız",
12
+ "Consider how your comment might make others feel and be sure to follow our Community Guidelines": "Yorumunuzun diğerlerini nasıl hissettirebileceğini düşünün ve topluluk kurallarımızı takip ettiğinizden emin olun",
13
+ "Copy Message": "Mesajı Kopyala",
14
+ "Delete": "Sil",
15
+ "Delete Message": "Mesajı Sil",
16
+ "Device camera is used to take photos or videos.": "",
17
+ "Do you want to send a copy of this message to a moderator for further investigation?": "Detaylı inceleme için bu mesajın kopyasını moderatöre göndermek istiyor musunuz?",
18
+ "Edit Message": "Mesajı Düzenle",
17
19
  "Editing Message": "",
18
20
  "Emoji matching": "",
19
21
  "Empty message...": "",
@@ -21,10 +23,10 @@
21
23
  "Error loading channel list...": "",
22
24
  "Error loading messages for this channel...": "",
23
25
  "Error while loading, please reload/refresh": "",
24
- "File type not supported": "Dosya türü desteklenmiyor",
25
- "Flag": "",
26
- "Flag Message": "",
27
- "Flag action failed either due to a network issue or the message is already flagged": "",
26
+ "File type not supported": "",
27
+ "Flag": "Raporla",
28
+ "Flag Message": "Mesajı Raporla",
29
+ "Flag action failed either due to a network issue or the message is already flagged": "Mesajın daha önce raporlanmış olması veya bir ağ bağlantısı sorunu nedeniyle raporlama işlemi başarısız oldu.",
28
30
  "How about sending your first message to a friend?": "",
29
31
  "Instant Commands": "",
30
32
  "Let's start chatting!": "",
@@ -32,44 +34,45 @@
32
34
  "Loading channels...": "",
33
35
  "Loading messages...": "",
34
36
  "Loading...": "",
35
- "Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "",
37
+ "Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Maksimum dosya boyutu yükleme sınırına ulaşıldı. Lütfen {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB'ın altında bir dosya yükleyin.",
36
38
  "Message Reactions": "",
37
39
  "Message deleted": "",
38
- "Message flagged": "",
39
- "Mute User": "",
40
- "Not supported": "Desteklenmiyor",
40
+ "Message flagged": "Mesaj işaretlendi",
41
+ "Mute User": "Kullanıcıyı sessize al",
42
+ "Not supported": "",
41
43
  "Nothing yet...": "",
42
- "Ok": "",
43
- "Only visible to you": "Sadece siz görebilirsiniz",
44
- "Open Settings": "Ayarları aç",
44
+ "Ok": "Tamam",
45
+ "Only visible to you": "",
46
+ "Open Settings": "",
45
47
  "Photo": "",
46
- "Photos and Videos": "Fotoğraflar ve Videolar",
47
- "Pin to Conversation": "",
48
- "Pinned by": "Tarafından sabitlendi",
49
- "Please enable access to your photos and videos so you can share them.": "Paylaşım yapabilmek için lutfen fotoğraflarınıza ve videolarınıza erişimi etkinleştirin.",
48
+ "Photos and Videos": "",
49
+ "Pin to Conversation": "Konuşmaya sabitle",
50
+ "Pinned by": "",
51
+ "Please enable access to your photos and videos so you can share them.": "",
50
52
  "Please select a channel first": "Lütfen önce bir kanal seçiniz",
51
53
  "Reconnecting...": "",
52
- "Reply": "",
53
- "Reply to Message": "Mesajı Yanıtla",
54
- "Resend": "",
55
- "Search GIFs": "GIF Ara",
56
- "Select More Photos": "Daha Fazla Fotoğraf Seçin",
57
- "Send a message": "Mesaj gönder",
54
+ "Reply": "Yanıtla",
55
+ "Reply to Message": "",
56
+ "Resend": "Yeniden gönder",
57
+ "Search GIFs": "",
58
+ "Select More Photos": "",
59
+ "Send Anyway": "Yine de Gönder",
60
+ "Send a message": "",
58
61
  "Sending links is not allowed in this conversation": "Bu konuşmada bağlantı göndermek desteklenmiyor",
59
- "Slow mode ON": "Yavaş Mod Açık",
60
- "The message has been reported to a moderator.": "",
61
- "Thread Reply": "",
62
- "Unblock User": "",
63
- "Unknown User": "Bilinmeyen kullanıcı",
64
- "Unmute User": "",
65
- "Unpin from Conversation": "",
66
- "Unread Messages": "",
62
+ "Slow mode ON": "",
63
+ "The message has been reported to a moderator.": "Mesaj moderatöre bildirildi.",
64
+ "Thread Reply": "Konu Yanıtı",
65
+ "Unblock User": "Kullanıcının engelini kaldır",
66
+ "Unknown User": "",
67
+ "Unmute User": "Kullanıcının sesini aç",
68
+ "Unpin from Conversation": "Sabitlemeyi kaldır",
69
+ "Unread Messages": "Okunmamış Mesajlar",
67
70
  "Video": "",
68
71
  "You": "",
69
- "You can't send messages in this channel": "",
72
+ "You can't send messages in this channel": "Bu konuşmaya mesaj gönderemezsiniz",
70
73
  "{{ firstUser }} and {{ nonSelfUserLength }} more are typing": "",
71
- "{{ index }} of {{ photoLength }}": "{{ index }} / {{ photoLength }}",
72
- "{{ replyCount }} Replies": "{{ replyCount }} Cevap",
74
+ "{{ index }} of {{ photoLength }}": "",
75
+ "{{ replyCount }} Replies": "",
73
76
  "{{ replyCount }} Thread Replies": "",
74
77
  "{{ user }} is typing": "",
75
78
  "🏙 Attachment...": ""
@@ -119,9 +119,11 @@ export declare class Streami18n {
119
119
  "Allow camera access in device settings": string;
120
120
  "Also send to channel": string;
121
121
  "Are you sure you want to permanently delete this message?": string;
122
+ "Are you sure?": string;
122
123
  "Block User": string;
123
124
  Cancel: string;
124
125
  "Cannot Flag Message": string;
126
+ "Consider how your comment might make others feel and be sure to follow our Community Guidelines": string;
125
127
  "Copy Message": string;
126
128
  Delete: string;
127
129
  "Delete Message": string;
@@ -168,6 +170,7 @@ export declare class Streami18n {
168
170
  Resend: string;
169
171
  "Search GIFs": string;
170
172
  "Select More Photos": string;
173
+ "Send Anyway": string;
171
174
  "Send a message": string;
172
175
  "Sending links is not allowed in this conversation": string;
173
176
  "Slow mode ON": string;
@@ -1,3 +1,4 @@
1
+ import type { MessageResponse } from 'stream-chat';
1
2
  import type { MessageType } from '../components/MessageList/hooks/useMessageList';
2
3
  import type { DefaultStreamChatGenerics } from '../types/types';
3
- export declare const removeReservedFields: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: MessageType<StreamChatGenerics>) => MessageType<StreamChatGenerics>;
4
+ export declare const removeReservedFields: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: MessageType<StreamChatGenerics> | MessageResponse<StreamChatGenerics>) => MessageType<StreamChatGenerics> | MessageResponse<StreamChatGenerics>;
@@ -33,7 +33,18 @@ export declare const MessageStatusTypes: {
33
33
  export declare type FileStateValue = typeof FileState[keyof typeof FileState];
34
34
  declare type Progress = ValueOf<typeof ProgressIndicatorTypes>;
35
35
  export declare const getIndicatorTypeForFileState: (fileState: FileStateValue, enableOfflineSupport: boolean) => Progress | null;
36
+ /**
37
+ * Utility to check if the message is a Blocked message.
38
+ * @param message
39
+ * @returns boolean
40
+ */
36
41
  export declare const isBlockedMessage: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: TableRowJoinedUser<"messages"> | MessageType<StreamChatGenerics>) => boolean | "" | undefined;
42
+ /**
43
+ * Utility to check if the message is a Bounced message.
44
+ * @param message
45
+ * @returns boolean
46
+ */
47
+ export declare const isBouncedMessage: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: MessageType<StreamChatGenerics>) => boolean;
37
48
  export declare const queryMembersDebounced: DebouncedFunc<(<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(channel: Channel<StreamChatGenerics>, query: SuggestionUser<StreamChatGenerics>["name"], onReady?: ((users: SuggestionUser<StreamChatGenerics>[]) => void) | undefined, options?: {
38
49
  limit?: number;
39
50
  }) => Promise<void>)>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stream-chat-react-native-core",
3
3
  "description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
4
- "version": "5.24.0-beta.3",
4
+ "version": "5.24.0-beta.4",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -80,7 +80,7 @@
80
80
  "path": "0.12.7",
81
81
  "react-native-markdown-package": "1.8.2",
82
82
  "react-native-url-polyfill": "^1.3.0",
83
- "stream-chat": "8.14.4"
83
+ "stream-chat": "8.15.0"
84
84
  },
85
85
  "peerDependencies": {
86
86
  "react-native-quick-sqlite": ">=5.1.0",
@@ -82,7 +82,14 @@ import { compressedImageURI } from '../../utils/compressImage';
82
82
  import { DBSyncManager } from '../../utils/DBSyncManager';
83
83
  import { patchMessageTextCommand } from '../../utils/patchMessageTextCommand';
84
84
  import { removeReactionFromLocalState } from '../../utils/removeReactionFromLocalState';
85
- import { generateRandomId, isLocalUrl, MessageStatusTypes, ReactionData } from '../../utils/utils';
85
+ import { removeReservedFields } from '../../utils/removeReservedFields';
86
+ import {
87
+ generateRandomId,
88
+ isBouncedMessage,
89
+ isLocalUrl,
90
+ MessageStatusTypes,
91
+ ReactionData,
92
+ } from '../../utils/utils';
86
93
  import { Attachment as AttachmentDefault } from '../Attachment/Attachment';
87
94
  import { AttachmentActions as AttachmentActionsDefault } from '../Attachment/AttachmentActions';
88
95
  import { AudioAttachment as AudioAttachmentDefault } from '../Attachment/AudioAttachment';
@@ -107,6 +114,7 @@ import { LoadingIndicator as LoadingIndicatorDefault } from '../Indicators/Loadi
107
114
  import { KeyboardCompatibleView as KeyboardCompatibleViewDefault } from '../KeyboardCompatibleView/KeyboardCompatibleView';
108
115
  import { Message as MessageDefault } from '../Message/Message';
109
116
  import { MessageAvatar as MessageAvatarDefault } from '../Message/MessageSimple/MessageAvatar';
117
+ import { MessageBounce as MessageBounceDefault } from '../Message/MessageSimple/MessageBounce';
110
118
  import { MessageContent as MessageContentDefault } from '../Message/MessageSimple/MessageContent';
111
119
  import { MessageDeleted as MessageDeletedDefault } from '../Message/MessageSimple/MessageDeleted';
112
120
  import { MessageError as MessageErrorDefault } from '../Message/MessageSimple/MessageError';
@@ -273,6 +281,7 @@ export type ChannelPropsWithContext<
273
281
  | 'Message'
274
282
  | 'messageActions'
275
283
  | 'MessageAvatar'
284
+ | 'MessageBounce'
276
285
  | 'MessageContent'
277
286
  | 'messageContentOrder'
278
287
  | 'MessageDeleted'
@@ -495,6 +504,7 @@ const ChannelWithContext = <
495
504
  Message = MessageDefault,
496
505
  messageActions,
497
506
  MessageAvatar = MessageAvatarDefault,
507
+ MessageBounce = MessageBounceDefault,
498
508
  MessageContent = MessageContentDefault,
499
509
  messageContentOrder = ['quoted_reply', 'gallery', 'files', 'text', 'attachments'],
500
510
  MessageDeleted = MessageDeletedDefault,
@@ -564,7 +574,7 @@ const ChannelWithContext = <
564
574
  },
565
575
  } = useTheme();
566
576
  const [deleted, setDeleted] = useState(false);
567
- const [editing, setEditing] = useState<boolean | MessageType<StreamChatGenerics>>(false);
577
+ const [editing, setEditing] = useState<MessageType<StreamChatGenerics> | undefined>(undefined);
568
578
  const [error, setError] = useState<Error | boolean>(false);
569
579
  const [hasMore, setHasMore] = useState(true);
570
580
  const [lastRead, setLastRead] = useState<ChannelContextValue<StreamChatGenerics>['lastRead']>();
@@ -1679,9 +1689,17 @@ const ChannelWithContext = <
1679
1689
  status: MessageStatusTypes.SENDING,
1680
1690
  };
1681
1691
 
1682
- updateMessage(statusPendingMessage);
1692
+ const messageWithoutReservedFields = removeReservedFields(statusPendingMessage);
1693
+
1694
+ // For bounced messages, we don't need to update the message, instead always send a new message.
1695
+ if (!isBouncedMessage(message)) {
1696
+ updateMessage(messageWithoutReservedFields as MessageResponse<StreamChatGenerics>);
1697
+ }
1683
1698
 
1684
- await sendMessageRequest(statusPendingMessage, true);
1699
+ await sendMessageRequest(
1700
+ messageWithoutReservedFields as MessageResponse<StreamChatGenerics>,
1701
+ true,
1702
+ );
1685
1703
  };
1686
1704
 
1687
1705
  // hard limit to prevent you from scrolling faster than 1 page per 2 seconds
@@ -1843,10 +1861,10 @@ const ChannelWithContext = <
1843
1861
  : client.updateMessage(updatedMessage);
1844
1862
 
1845
1863
  const setEditingState: MessagesContextValue<StreamChatGenerics>['setEditingState'] = (
1846
- messageOrBoolean,
1864
+ message,
1847
1865
  ) => {
1848
1866
  clearQuotedMessageState();
1849
- setEditing(messageOrBoolean);
1867
+ setEditing(message);
1850
1868
  };
1851
1869
 
1852
1870
  const setQuotedMessageState: MessagesContextValue<StreamChatGenerics>['setQuotedMessageState'] = (
@@ -1856,7 +1874,7 @@ const ChannelWithContext = <
1856
1874
  };
1857
1875
 
1858
1876
  const clearEditingState: InputMessageInputContextValue<StreamChatGenerics>['clearEditingState'] =
1859
- () => setEditing(false);
1877
+ () => setEditing(undefined);
1860
1878
 
1861
1879
  const clearQuotedMessageState: InputMessageInputContextValue<StreamChatGenerics>['clearQuotedMessageState'] =
1862
1880
  () => setQuotedMessage(false);
@@ -2223,6 +2241,7 @@ const ChannelWithContext = <
2223
2241
  Message,
2224
2242
  messageActions,
2225
2243
  MessageAvatar,
2244
+ MessageBounce,
2226
2245
  MessageContent,
2227
2246
  messageContentOrder,
2228
2247
  MessageDeleted,
@@ -54,6 +54,7 @@ export const useCreateMessagesContext = <
54
54
  Message,
55
55
  messageActions,
56
56
  MessageAvatar,
57
+ MessageBounce,
57
58
  MessageContent,
58
59
  messageContentOrder,
59
60
  MessageDeleted,
@@ -149,6 +150,7 @@ export const useCreateMessagesContext = <
149
150
  Message,
150
151
  messageActions,
151
152
  MessageAvatar,
153
+ MessageBounce,
152
154
  MessageContent,
153
155
  messageContentOrder,
154
156
  MessageDeleted,
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import { GestureResponderEvent, Keyboard, StyleProp, View, ViewStyle } from 'react-native';
3
3
 
4
4
  import type { Attachment, UserResponse } from 'stream-chat';
@@ -44,7 +44,12 @@ import {
44
44
 
45
45
  import { isVideoPackageAvailable, triggerHaptic } from '../../native';
46
46
  import type { DefaultStreamChatGenerics } from '../../types/types';
47
- import { hasOnlyEmojis, isBlockedMessage, MessageStatusTypes } from '../../utils/utils';
47
+ import {
48
+ hasOnlyEmojis,
49
+ isBlockedMessage,
50
+ isBouncedMessage,
51
+ MessageStatusTypes,
52
+ } from '../../utils/utils';
48
53
 
49
54
  import {
50
55
  isMessageWithStylesReadByAndDateSeparator,
@@ -141,6 +146,7 @@ export type MessagePropsWithContext<
141
146
  | 'isAttachmentEqual'
142
147
  | 'messageActions'
143
148
  | 'messageContentOrder'
149
+ | 'MessageBounce'
144
150
  | 'MessageSimple'
145
151
  | 'onLongPressMessage'
146
152
  | 'onPressInMessage'
@@ -217,6 +223,7 @@ const MessageWithContext = <
217
223
  >(
218
224
  props: MessagePropsWithContext<StreamChatGenerics>,
219
225
  ) => {
226
+ const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false);
220
227
  const isMessageTypeDeleted = props.message.type === 'deleted';
221
228
 
222
229
  const {
@@ -249,6 +256,7 @@ const MessageWithContext = <
249
256
  messageActions: messageActionsProp = defaultMessageActions,
250
257
  messageContentOrder: messageContentOrderProp,
251
258
  messagesContext,
259
+ MessageBounce,
252
260
  MessageSimple,
253
261
  onLongPress: onLongPressProp,
254
262
  onLongPressMessage: onLongPressMessageProp,
@@ -319,9 +327,19 @@ const MessageWithContext = <
319
327
  }
320
328
  const quotedMessage = message.quoted_message as MessageType<StreamChatGenerics>;
321
329
  if (error) {
330
+ /**
331
+ * If its a Blocked message, we don't do anything as per specs.
332
+ */
322
333
  if (isBlockedMessage(message)) {
323
334
  return;
324
335
  }
336
+ /**
337
+ * If its a Bounced message, we open the message bounced options modal.
338
+ */
339
+ if (isBouncedMessage(message)) {
340
+ setIsBounceDialogOpen(true);
341
+ return;
342
+ }
325
343
  showMessageOverlay(false, true);
326
344
  } else if (quotedMessage) {
327
345
  onPressQuotedMessage(quotedMessage);
@@ -622,6 +640,11 @@ const MessageWithContext = <
622
640
  })
623
641
  : enableLongPress
624
642
  ? () => {
643
+ // If a message is bounced, on long press the message bounce options modal should open.
644
+ if (isBouncedMessage(message)) {
645
+ setIsBounceDialogOpen(true);
646
+ return;
647
+ }
625
648
  triggerHaptic('impactMedium');
626
649
  showMessageOverlay(false);
627
650
  }
@@ -730,6 +753,7 @@ const MessageWithContext = <
730
753
  >
731
754
  <MessageProvider value={messageContext}>
732
755
  <MessageSimple />
756
+ {isBounceDialogOpen && <MessageBounce setIsBounceDialogOpen={setIsBounceDialogOpen} />}
733
757
  </MessageProvider>
734
758
  </View>
735
759
  </View>
@@ -0,0 +1,119 @@
1
+ import React from 'react';
2
+ import { Alert } from 'react-native';
3
+
4
+ import {
5
+ MessageContextValue,
6
+ useMessageContext,
7
+ } from '../../../contexts/messageContext/MessageContext';
8
+ import {
9
+ MessagesContextValue,
10
+ useMessagesContext,
11
+ } from '../../../contexts/messagesContext/MessagesContext';
12
+ import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
13
+
14
+ import type { DefaultStreamChatGenerics } from '../../../types/types';
15
+
16
+ export type MessageBouncePropsWithContext<
17
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
18
+ > = Pick<
19
+ MessagesContextValue<StreamChatGenerics>,
20
+ 'setEditingState' | 'removeMessage' | 'retrySendMessage'
21
+ > &
22
+ Pick<MessageContextValue<StreamChatGenerics>, 'message'> & {
23
+ setIsBounceDialogOpen: React.Dispatch<React.SetStateAction<boolean>>;
24
+ };
25
+
26
+ export const MessageBounceWithContext = <
27
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
28
+ >(
29
+ props: MessageBouncePropsWithContext<StreamChatGenerics>,
30
+ ) => {
31
+ const { t } = useTranslationContext();
32
+ const { message, removeMessage, retrySendMessage, setEditingState, setIsBounceDialogOpen } =
33
+ props;
34
+
35
+ const handleEditMessage = () => {
36
+ setEditingState(message);
37
+ if (setIsBounceDialogOpen) {
38
+ setIsBounceDialogOpen(false);
39
+ }
40
+ };
41
+
42
+ const handleResend = () => {
43
+ retrySendMessage(message);
44
+ if (setIsBounceDialogOpen) {
45
+ setIsBounceDialogOpen(false);
46
+ }
47
+ };
48
+
49
+ const handleRemoveMessage = () => {
50
+ removeMessage(message);
51
+ if (setIsBounceDialogOpen) {
52
+ setIsBounceDialogOpen(false);
53
+ }
54
+ };
55
+
56
+ return (
57
+ <>
58
+ {Alert.alert(
59
+ t('Are you sure?'),
60
+ t(
61
+ 'Consider how your comment might make others feel and be sure to follow our Community Guidelines',
62
+ ),
63
+ [
64
+ { onPress: handleResend, text: t('Send Anyway') },
65
+ { onPress: handleEditMessage, text: t('Edit Message') },
66
+ { onPress: handleRemoveMessage, text: t('Delete Message') },
67
+ ],
68
+ { cancelable: true },
69
+ )}
70
+ </>
71
+ );
72
+ };
73
+
74
+ const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(
75
+ prevProps: MessageBouncePropsWithContext<StreamChatGenerics>,
76
+ nextProps: MessageBouncePropsWithContext<StreamChatGenerics>,
77
+ ) => {
78
+ const { message: prevMessage } = prevProps;
79
+ const { message: nextMessage } = nextProps;
80
+ const messageEqual =
81
+ prevMessage.cid === nextMessage.cid &&
82
+ prevMessage.type === nextMessage.type &&
83
+ prevMessage.text === nextMessage.text;
84
+ if (!messageEqual) return false;
85
+
86
+ return true;
87
+ };
88
+
89
+ const MemoizedMessageBounce = React.memo(
90
+ MessageBounceWithContext,
91
+ areEqual,
92
+ ) as typeof MessageBounceWithContext;
93
+
94
+ export type MessageBounceProps<
95
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
96
+ > = Partial<MessageBouncePropsWithContext<StreamChatGenerics>> & {
97
+ setIsBounceDialogOpen: React.Dispatch<React.SetStateAction<boolean>>;
98
+ };
99
+
100
+ export const MessageBounce = <
101
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
102
+ >(
103
+ props: MessageBounceProps<StreamChatGenerics>,
104
+ ) => {
105
+ const { message } = useMessageContext<StreamChatGenerics>();
106
+ const { removeMessage, retrySendMessage, setEditingState } =
107
+ useMessagesContext<StreamChatGenerics>();
108
+ return (
109
+ <MemoizedMessageBounce<StreamChatGenerics>
110
+ {...{
111
+ message,
112
+ removeMessage,
113
+ retrySendMessage,
114
+ setEditingState,
115
+ }}
116
+ {...props}
117
+ />
118
+ );
119
+ };
@@ -32,8 +32,7 @@ export const useMessageActionHandlers = <
32
32
  Pick<ChannelContextValue<StreamChatGenerics>, 'channel' | 'enforceUniqueReaction'> &
33
33
  Pick<ChatContextValue<StreamChatGenerics>, 'client'> &
34
34
  Pick<MessageContextValue<StreamChatGenerics>, 'message'>) => {
35
- const handleResendMessage = () =>
36
- retrySendMessage(message as MessageResponse<StreamChatGenerics>);
35
+ const handleResendMessage = () => retrySendMessage(message);
37
36
 
38
37
  const handleQuotedReplyMessage = () => {
39
38
  setQuotedMessageState(message);
@@ -339,7 +339,7 @@ export const useMessageActions = <
339
339
  setOverlay('none');
340
340
  const messageWithoutReservedFields = removeReservedFields(message);
341
341
  if (handleRetry) {
342
- handleRetry(messageWithoutReservedFields);
342
+ handleRetry(messageWithoutReservedFields as MessageType<StreamChatGenerics>);
343
343
  }
344
344
 
345
345
  await handleResendMessage();
@@ -96,6 +96,7 @@ export * from './Message/hooks/useMessageActions';
96
96
  export * from './Message/hooks/useMessageActionHandlers';
97
97
  export * from './Message/Message';
98
98
  export * from './Message/MessageSimple/MessageAvatar';
99
+ export * from './Message/MessageSimple/MessageBounce';
99
100
  export * from './Message/MessageSimple/MessageContent';
100
101
  export * from './Message/MessageSimple/MessageDeleted';
101
102
  export * from './Message/MessageSimple/MessageError';
@@ -18,7 +18,7 @@ import {
18
18
  } from 'stream-chat';
19
19
 
20
20
  import { useCreateMessageInputContext } from './hooks/useCreateMessageInputContext';
21
- import { isEditingBoolean, useMessageDetailsForState } from './hooks/useMessageDetailsForState';
21
+ import { useMessageDetailsForState } from './hooks/useMessageDetailsForState';
22
22
 
23
23
  import { parseLinksFromText } from '../../components/Message/MessageSimple/utils/parseLinks';
24
24
  import type { AttachButtonProps } from '../../components/MessageInput/AttachButton';
@@ -53,11 +53,13 @@ import {
53
53
  FileState,
54
54
  FileStateValue,
55
55
  generateRandomId,
56
+ isBouncedMessage,
56
57
  TriggerSettings,
57
58
  } from '../../utils/utils';
58
59
  import { useAttachmentPickerContext } from '../attachmentPickerContext/AttachmentPickerContext';
59
60
  import { ChannelContextValue, useChannelContext } from '../channelContext/ChannelContext';
60
61
  import { useChatContext } from '../chatContext/ChatContext';
62
+ import { useMessagesContext } from '../messagesContext/MessagesContext';
61
63
  import { useOwnCapabilitiesContext } from '../ownCapabilitiesContext/OwnCapabilitiesContext';
62
64
  import { useThreadContext } from '../threadContext/ThreadContext';
63
65
  import { useTranslationContext } from '../translationContext/TranslationContext';
@@ -226,7 +228,6 @@ export type InputMessageInputContextValue<
226
228
  * **default** [CooldownTimer](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/MessageInput/CooldownTimer.tsx)
227
229
  */
228
230
  CooldownTimer: React.ComponentType<CooldownTimerProps>;
229
- editing: boolean | MessageType<StreamChatGenerics>;
230
231
  editMessage: StreamChat<StreamChatGenerics>['updateMessage'];
231
232
 
232
233
  /**
@@ -331,6 +332,12 @@ export type InputMessageInputContextValue<
331
332
  channel: ChannelContextValue<StreamChatGenerics>['channel'],
332
333
  ) => Promise<SendFileAPIResponse>;
333
334
 
335
+ /**
336
+ * Variable that tracks the editing state.
337
+ * It is defined with message type if the editing state is true, else its undefined.
338
+ */
339
+ editing?: MessageType<StreamChatGenerics>;
340
+
334
341
  /** Initial value to set on input */
335
342
  initialValue?: string;
336
343
  /**
@@ -398,6 +405,7 @@ export const MessageInputProvider = <
398
405
  const { closePicker, openPicker, selectedPicker, setSelectedPicker } =
399
406
  useAttachmentPickerContext();
400
407
  const { appSettings, client, enableOfflineSupport } = useChatContext<StreamChatGenerics>();
408
+ const { removeMessage } = useMessagesContext();
401
409
 
402
410
  const getFileUploadConfig = () => {
403
411
  const fileConfig = appSettings?.app?.file_upload_config;
@@ -625,6 +633,9 @@ export const MessageInputProvider = <
625
633
  (prevNumberOfUploads) => prevNumberOfUploads - (pendingAttachments?.length || 0),
626
634
  );
627
635
  setText('');
636
+ if (value.editing) {
637
+ value.clearEditingState();
638
+ }
628
639
  };
629
640
 
630
641
  const mapImageUploadToAttachment = (image: ImageUpload): Attachment<StreamChatGenerics> => {
@@ -771,9 +782,10 @@ export const MessageInputProvider = <
771
782
  return;
772
783
  }
773
784
 
774
- if (value.editing && !isEditingBoolean(value.editing)) {
785
+ const message = value.editing;
786
+ if (message && message.type !== 'error') {
775
787
  const updatedMessage = {
776
- ...value.editing,
788
+ ...message,
777
789
  attachments,
778
790
  mentioned_users: mentionedUsers,
779
791
  quoted_message: undefined,
@@ -795,6 +807,12 @@ export const MessageInputProvider = <
795
807
  sending.current = false;
796
808
  } else {
797
809
  try {
810
+ /**
811
+ * If the message is bounced by moderation, we firstly remove the message from message list and then send a new message.
812
+ */
813
+ if (message && isBouncedMessage(message as MessageType<StreamChatGenerics>)) {
814
+ removeMessage(message);
815
+ }
798
816
  value.sendMessage({
799
817
  attachments,
800
818
  mentioned_users: uniq(mentionedUsers),
@@ -890,7 +908,7 @@ export const MessageInputProvider = <
890
908
 
891
909
  const updateMessage = async () => {
892
910
  try {
893
- if (!isEditingBoolean(value.editing)) {
911
+ if (value.editing) {
894
912
  await client.updateMessage({
895
913
  ...value.editing,
896
914
  quoted_message: undefined,
@@ -898,8 +916,8 @@ export const MessageInputProvider = <
898
916
  } as Parameters<StreamChat<StreamChatGenerics>['updateMessage']>[0]);
899
917
  }
900
918
 
901
- resetInput();
902
919
  value.clearEditingState();
920
+ resetInput();
903
921
  } catch (error) {
904
922
  console.log(error);
905
923
  }