stream-chat-react-native-core 9.5.1-beta.7 → 9.5.1-beta.8

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 (33) hide show
  1. package/lib/commonjs/components/MessageList/MessageFlashList.js +7 -2
  2. package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
  3. package/lib/commonjs/components/MessageList/MessageList.js +7 -2
  4. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  5. package/lib/commonjs/components/Notifications/index.js +11 -0
  6. package/lib/commonjs/components/Notifications/index.js.map +1 -1
  7. package/lib/commonjs/components/Notifications/notificationFilters.js +9 -0
  8. package/lib/commonjs/components/Notifications/notificationFilters.js.map +1 -0
  9. package/lib/commonjs/version.json +1 -1
  10. package/lib/module/components/MessageList/MessageFlashList.js +7 -2
  11. package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
  12. package/lib/module/components/MessageList/MessageList.js +7 -2
  13. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  14. package/lib/module/components/Notifications/index.js +11 -0
  15. package/lib/module/components/Notifications/index.js.map +1 -1
  16. package/lib/module/components/Notifications/notificationFilters.js +9 -0
  17. package/lib/module/components/Notifications/notificationFilters.js.map +1 -0
  18. package/lib/module/version.json +1 -1
  19. package/lib/typescript/components/MessageList/MessageFlashList.d.ts +1 -1
  20. package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
  21. package/lib/typescript/components/MessageList/MessageList.d.ts +1 -1
  22. package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
  23. package/lib/typescript/components/Notifications/index.d.ts +1 -0
  24. package/lib/typescript/components/Notifications/index.d.ts.map +1 -1
  25. package/lib/typescript/components/Notifications/notificationFilters.d.ts +10 -0
  26. package/lib/typescript/components/Notifications/notificationFilters.d.ts.map +1 -0
  27. package/package.json +1 -1
  28. package/src/components/MessageList/MessageFlashList.tsx +13 -3
  29. package/src/components/MessageList/MessageList.tsx +13 -3
  30. package/src/components/Notifications/__tests__/notificationFilters.test.ts +50 -0
  31. package/src/components/Notifications/index.ts +1 -0
  32. package/src/components/Notifications/notificationFilters.ts +26 -0
  33. package/src/version.json +1 -1
@@ -9,7 +9,7 @@ import { MessagesContextValue } from '../../contexts/messagesContext/MessagesCon
9
9
  import { OwnCapabilitiesContextValue } from '../../contexts/ownCapabilitiesContext/OwnCapabilitiesContext';
10
10
  import { PaginatedMessageListContextValue } from '../../contexts/paginatedMessageListContext/PaginatedMessageListContext';
11
11
  import { ThreadContextValue } from '../../contexts/threadContext/ThreadContext';
12
- type MessageListPropsWithContext = Pick<AttachmentPickerContextValue, 'closePicker' | 'attachmentPickerStore'> & Pick<OwnCapabilitiesContextValue, 'readEvents'> & Pick<ChannelContextValue, 'channel' | 'channelUnreadStateStore' | 'disabled' | 'hideStickyDateHeader' | 'loadChannelAroundMessage' | 'loading' | 'markRead' | 'reloadChannel' | 'scrollToFirstUnreadThreshold' | 'setChannelUnreadState' | 'setTargetedMessage' | 'targetedMessage' | 'threadList' | 'maximumMessageLimit'> & Pick<ChatContextValue, 'client'> & Pick<PaginatedMessageListContextValue, 'loadMore' | 'loadMoreRecent' | 'hasMore'> & Pick<MessagesContextValue, 'disableTypingIndicator' | 'FlatList' | 'myMessageTheme' | 'shouldShowUnreadUnderlay'> & Pick<MessageInputContextValue, 'messageInputFloating' | 'messageInputHeightStore'> & Pick<ThreadContextValue, 'loadMoreRecentThread' | 'loadMoreThread' | 'threadHasMore' | 'thread' | 'threadInstance'> & {
12
+ type MessageListPropsWithContext = Pick<AttachmentPickerContextValue, 'closePicker' | 'attachmentPickerStore'> & Pick<OwnCapabilitiesContextValue, 'readEvents'> & Pick<ChannelContextValue, 'channel' | 'channelUnreadStateStore' | 'disabled' | 'hideStickyDateHeader' | 'loadChannelAroundMessage' | 'loading' | 'markRead' | 'reloadChannel' | 'scrollToFirstUnreadThreshold' | 'setChannelUnreadState' | 'setTargetedMessage' | 'targetedMessage' | 'threadList' | 'maximumMessageLimit'> & Pick<ChatContextValue, 'client'> & Pick<PaginatedMessageListContextValue, 'loadMore' | 'loadMoreRecent' | 'hasMore'> & Pick<MessagesContextValue, 'disableTypingIndicator' | 'FlatList' | 'myMessageTheme' | 'shouldShowUnreadUnderlay'> & Pick<MessageInputContextValue, 'allowSendBeforeAttachmentsUpload' | 'messageInputFloating' | 'messageInputHeightStore'> & Pick<ThreadContextValue, 'loadMoreRecentThread' | 'loadMoreThread' | 'threadHasMore' | 'thread' | 'threadInstance'> & {
13
13
  /**
14
14
  * Besides existing (default) UX behavior of underlying FlatList of MessageList component, if you want
15
15
  * to attach some additional props to underlying FlatList, you can add it to following prop.
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageList/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,EAEL,aAAa,EACb,QAAQ,IAAI,YAAY,EAExB,eAAe,EAKhB,MAAM,cAAc,CAAC;AAgBtB,OAAO,EAEL,6BAA6B,EAC9B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EACL,4BAA4B,EAE7B,MAAM,gEAAgE,CAAC;AACxE,OAAO,EACL,mBAAmB,EAEpB,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAkB,MAAM,wCAAwC,CAAC;AAI1F,OAAO,EACL,wBAAwB,EAEzB,MAAM,wDAAwD,CAAC;AAKhE,OAAO,EACL,oBAAoB,EAErB,MAAM,gDAAgD,CAAC;AACxD,OAAO,EACL,2BAA2B,EAE5B,MAAM,8DAA8D,CAAC;AACtE,OAAO,EACL,gCAAgC,EAEjC,MAAM,wEAAwE,CAAC;AAEhF,OAAO,EAAE,kBAAkB,EAAoB,MAAM,4CAA4C,CAAC;AAyIlG,KAAK,2BAA2B,GAAG,IAAI,CACrC,4BAA4B,EAC5B,aAAa,GAAG,uBAAuB,CACxC,GACC,IAAI,CAAC,2BAA2B,EAAE,YAAY,CAAC,GAC/C,IAAI,CACF,mBAAmB,EACjB,SAAS,GACT,yBAAyB,GACzB,UAAU,GACV,sBAAsB,GACtB,0BAA0B,GAC1B,SAAS,GACT,UAAU,GACV,eAAe,GACf,8BAA8B,GAC9B,uBAAuB,GACvB,oBAAoB,GACpB,iBAAiB,GACjB,YAAY,GACZ,qBAAqB,CACxB,GACD,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAChC,IAAI,CAAC,gCAAgC,EAAE,UAAU,GAAG,gBAAgB,GAAG,SAAS,CAAC,GACjF,IAAI,CACF,oBAAoB,EACpB,wBAAwB,GAAG,UAAU,GAAG,gBAAgB,GAAG,0BAA0B,CACtF,GACD,IAAI,CAAC,wBAAwB,EAAE,sBAAsB,GAAG,yBAAyB,CAAC,GAClF,IAAI,CACF,kBAAkB,EAClB,sBAAsB,GAAG,gBAAgB,GAAG,eAAe,GAAG,QAAQ,GAAG,gBAAgB,CAC1F,GAAG;IACF;;;;;;;;;;;;;OAaG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAChF;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACtC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACtC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACjE;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACnF;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAwkCJ,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;AAEpE,eAAO,MAAM,WAAW,GAAI,OAAO,gBAAgB,sBAyElD,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageList/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,EAEL,aAAa,EACb,QAAQ,IAAI,YAAY,EAExB,eAAe,EAKhB,MAAM,cAAc,CAAC;AAgBtB,OAAO,EAEL,6BAA6B,EAC9B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EACL,4BAA4B,EAE7B,MAAM,gEAAgE,CAAC;AACxE,OAAO,EACL,mBAAmB,EAEpB,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAkB,MAAM,wCAAwC,CAAC;AAI1F,OAAO,EACL,wBAAwB,EAEzB,MAAM,wDAAwD,CAAC;AAKhE,OAAO,EACL,oBAAoB,EAErB,MAAM,gDAAgD,CAAC;AACxD,OAAO,EACL,2BAA2B,EAE5B,MAAM,8DAA8D,CAAC;AACtE,OAAO,EACL,gCAAgC,EAEjC,MAAM,wEAAwE,CAAC;AAEhF,OAAO,EAAE,kBAAkB,EAAoB,MAAM,4CAA4C,CAAC;AA0IlG,KAAK,2BAA2B,GAAG,IAAI,CACrC,4BAA4B,EAC5B,aAAa,GAAG,uBAAuB,CACxC,GACC,IAAI,CAAC,2BAA2B,EAAE,YAAY,CAAC,GAC/C,IAAI,CACF,mBAAmB,EACjB,SAAS,GACT,yBAAyB,GACzB,UAAU,GACV,sBAAsB,GACtB,0BAA0B,GAC1B,SAAS,GACT,UAAU,GACV,eAAe,GACf,8BAA8B,GAC9B,uBAAuB,GACvB,oBAAoB,GACpB,iBAAiB,GACjB,YAAY,GACZ,qBAAqB,CACxB,GACD,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAChC,IAAI,CAAC,gCAAgC,EAAE,UAAU,GAAG,gBAAgB,GAAG,SAAS,CAAC,GACjF,IAAI,CACF,oBAAoB,EACpB,wBAAwB,GAAG,UAAU,GAAG,gBAAgB,GAAG,0BAA0B,CACtF,GACD,IAAI,CACF,wBAAwB,EACxB,kCAAkC,GAAG,sBAAsB,GAAG,yBAAyB,CACxF,GACD,IAAI,CACF,kBAAkB,EAClB,sBAAsB,GAAG,gBAAgB,GAAG,eAAe,GAAG,QAAQ,GAAG,gBAAgB,CAC1F,GAAG;IACF;;;;;;;;;;;;;OAaG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAChF;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACtC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACtC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACjE;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACnF;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AA4kCJ,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;AAEpE,eAAO,MAAM,WAAW,GAAI,OAAO,gBAAgB,sBA2ElD,CAAC"}
@@ -2,5 +2,6 @@ export * from './Notification';
2
2
  export * from './NotificationList';
3
3
  export * from './NotificationTargetContext';
4
4
  export * from './hooks';
5
+ export * from './notificationFilters';
5
6
  export * from './notificationTarget';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/Notifications/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,SAAS,CAAC;AACxB,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/Notifications/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { NotificationListFilter } from './NotificationList';
2
+ /**
3
+ * Notification list filter that keeps every notification except the spurious
4
+ * "upload failed" one produced when a user removes an attachment mid-upload
5
+ * (a filter returns `true` to keep a notification). Genuine upload failures
6
+ * (network, server) are kept because their error is neither aborted nor
7
+ * cancelled.
8
+ */
9
+ export declare const excludeCanceledUploadNotifications: NotificationListFilter;
10
+ //# sourceMappingURL=notificationFilters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notificationFilters.d.ts","sourceRoot":"","sources":["../../../../src/components/Notifications/notificationFilters.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAejE;;;;;;GAMG;AACH,eAAO,MAAM,kCAAkC,EAAE,sBACN,CAAC"}
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": "9.5.1-beta.7",
4
+ "version": "9.5.1-beta.8",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -60,6 +60,7 @@ import { MessageInputHeightState } from '../../state-store/message-input-height-
60
60
  import { primitives } from '../../theme';
61
61
  import { transitions } from '../../utils/animations/transitions';
62
62
  import { MessageWrapper } from '../Message/MessageItemView/MessageWrapper';
63
+ import { excludeCanceledUploadNotifications } from '../Notifications/notificationFilters';
63
64
  import { PortalWhileClosingView } from '../UIComponents/PortalWhileClosingView';
64
65
 
65
66
  type FlashListContextApi = { getRef?: () => FlashListRef<LocalMessage> | null } | undefined;
@@ -139,7 +140,10 @@ type MessageFlashListPropsWithContext = Pick<
139
140
  | 'maximumMessageLimit'
140
141
  > &
141
142
  Pick<ChatContextValue, 'client'> &
142
- Pick<MessageInputContextValue, 'messageInputFloating' | 'messageInputHeightStore'> &
143
+ Pick<
144
+ MessageInputContextValue,
145
+ 'allowSendBeforeAttachmentsUpload' | 'messageInputFloating' | 'messageInputHeightStore'
146
+ > &
143
147
  Pick<PaginatedMessageListContextValue, 'loadMore' | 'loadMoreRecent'> &
144
148
  Pick<
145
149
  MessagesContextValue,
@@ -259,6 +263,7 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
259
263
  ? InlineLoadingMoreRecentThreadIndicator
260
264
  : InlineLoadingMoreRecentIndicator;
261
265
  const {
266
+ allowSendBeforeAttachmentsUpload,
262
267
  attachmentPickerStore,
263
268
  additionalFlashListProps,
264
269
  channel,
@@ -1153,7 +1158,10 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
1153
1158
  <AutoCompleteSuggestionList />
1154
1159
  </PortalWhileClosingView>
1155
1160
  </Animated.View>
1156
- <NotificationList bottomOffset={messageInputFloating ? messageInputHeight + 16 : undefined} />
1161
+ <NotificationList
1162
+ bottomOffset={messageInputFloating ? messageInputHeight + 16 : undefined}
1163
+ filter={allowSendBeforeAttachmentsUpload ? excludeCanceledUploadNotifications : undefined}
1164
+ />
1157
1165
  </View>
1158
1166
  );
1159
1167
  };
@@ -1238,11 +1246,13 @@ export const MessageFlashList = (props: MessageFlashListProps) => {
1238
1246
  const { loadMore, loadMoreRecent } = usePaginatedMessageListContext();
1239
1247
  const { loadMoreRecentThread, loadMoreThread, thread, threadInstance } = useThreadContext();
1240
1248
  const { readEvents } = useOwnCapabilitiesContext();
1241
- const { messageInputFloating, messageInputHeightStore } = useMessageInputContext();
1249
+ const { allowSendBeforeAttachmentsUpload, messageInputFloating, messageInputHeightStore } =
1250
+ useMessageInputContext();
1242
1251
 
1243
1252
  return (
1244
1253
  <MessageFlashListWithContext
1245
1254
  {...{
1255
+ allowSendBeforeAttachmentsUpload,
1246
1256
  attachmentPickerStore,
1247
1257
  channel,
1248
1258
  channelUnreadStateStore,
@@ -74,6 +74,7 @@ import { primitives } from '../../theme';
74
74
  import { transitions } from '../../utils/animations/transitions';
75
75
  import { useIncomingMessageAnnouncements } from '../Accessibility/hooks/useIncomingMessageAnnouncements';
76
76
  import { MessageWrapper } from '../Message/MessageItemView/MessageWrapper';
77
+ import { excludeCanceledUploadNotifications } from '../Notifications/notificationFilters';
77
78
  import { PortalWhileClosingView } from '../UIComponents';
78
79
 
79
80
  // This is just to make sure that the scrolling happens in a different task queue.
@@ -229,7 +230,10 @@ type MessageListPropsWithContext = Pick<
229
230
  MessagesContextValue,
230
231
  'disableTypingIndicator' | 'FlatList' | 'myMessageTheme' | 'shouldShowUnreadUnderlay'
231
232
  > &
232
- Pick<MessageInputContextValue, 'messageInputFloating' | 'messageInputHeightStore'> &
233
+ Pick<
234
+ MessageInputContextValue,
235
+ 'allowSendBeforeAttachmentsUpload' | 'messageInputFloating' | 'messageInputHeightStore'
236
+ > &
233
237
  Pick<
234
238
  ThreadContextValue,
235
239
  'loadMoreRecentThread' | 'loadMoreThread' | 'threadHasMore' | 'thread' | 'threadInstance'
@@ -320,6 +324,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
320
324
  ? InlineLoadingMoreRecentThreadIndicator
321
325
  : InlineLoadingMoreRecentIndicator;
322
326
  const {
327
+ allowSendBeforeAttachmentsUpload,
323
328
  animateLayout = true,
324
329
  attachmentPickerStore,
325
330
  additionalFlatListProps,
@@ -1391,7 +1396,10 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
1391
1396
  <AutoCompleteSuggestionList />
1392
1397
  </PortalWhileClosingView>
1393
1398
  </Animated.View>
1394
- <NotificationList bottomOffset={messageInputFloating ? messageInputHeight + 16 : undefined} />
1399
+ <NotificationList
1400
+ bottomOffset={messageInputFloating ? messageInputHeight + 16 : undefined}
1401
+ filter={allowSendBeforeAttachmentsUpload ? excludeCanceledUploadNotifications : undefined}
1402
+ />
1395
1403
  </View>
1396
1404
  );
1397
1405
  };
@@ -1423,7 +1431,8 @@ export const MessageList = (props: MessageListProps) => {
1423
1431
  const { readEvents } = useOwnCapabilitiesContext();
1424
1432
  const { disableTypingIndicator, FlatList, myMessageTheme, shouldShowUnreadUnderlay } =
1425
1433
  useMessagesContext();
1426
- const { messageInputFloating, messageInputHeightStore } = useMessageInputContext();
1434
+ const { allowSendBeforeAttachmentsUpload, messageInputFloating, messageInputHeightStore } =
1435
+ useMessageInputContext();
1427
1436
  const { loadMore, loadMoreRecent, hasMore } = usePaginatedMessageListContext();
1428
1437
  const { loadMoreRecentThread, loadMoreThread, threadHasMore, thread, threadInstance } =
1429
1438
  useThreadContext();
@@ -1431,6 +1440,7 @@ export const MessageList = (props: MessageListProps) => {
1431
1440
  return (
1432
1441
  <MessageListWithContext
1433
1442
  {...{
1443
+ allowSendBeforeAttachmentsUpload,
1434
1444
  attachmentPickerStore,
1435
1445
  channel,
1436
1446
  channelUnreadStateStore,
@@ -0,0 +1,50 @@
1
+ import type { Notification } from 'stream-chat';
2
+
3
+ import { excludeCanceledUploadNotifications } from '../notificationFilters';
4
+
5
+ const buildNotification = (overrides: Partial<Notification> = {}): Notification =>
6
+ ({
7
+ createdAt: 0,
8
+ id: 'n1',
9
+ message: 'Error uploading attachment',
10
+ origin: { emitter: 'AttachmentManager' },
11
+ type: 'api:attachment:upload:failed',
12
+ ...overrides,
13
+ }) as Notification;
14
+
15
+ const canceledError = (name: 'AbortError' | 'CanceledError') => {
16
+ const error = new Error('canceled');
17
+ error.name = name;
18
+ return error;
19
+ };
20
+
21
+ // A filter returns `true` to KEEP a notification, `false` to hide it.
22
+ describe('excludeCanceledUploadNotifications', () => {
23
+ it.each(['AbortError', 'CanceledError'] as const)(
24
+ 'hides an upload-failed notification aborted with %s',
25
+ (name) => {
26
+ const notification = buildNotification({ originalError: canceledError(name) });
27
+
28
+ expect(excludeCanceledUploadNotifications(notification)).toBe(false);
29
+ },
30
+ );
31
+
32
+ it('keeps a genuine upload failure', () => {
33
+ const notification = buildNotification({ originalError: new Error('Network Error') });
34
+
35
+ expect(excludeCanceledUploadNotifications(notification)).toBe(true);
36
+ });
37
+
38
+ it('keeps an upload-failed notification with no original error', () => {
39
+ expect(excludeCanceledUploadNotifications(buildNotification())).toBe(true);
40
+ });
41
+
42
+ it('keeps a cancellation error reported on a different notification type', () => {
43
+ const notification = buildNotification({
44
+ originalError: canceledError('CanceledError'),
45
+ type: 'api:message:send:failed',
46
+ });
47
+
48
+ expect(excludeCanceledUploadNotifications(notification)).toBe(true);
49
+ });
50
+ });
@@ -2,4 +2,5 @@ export * from './Notification';
2
2
  export * from './NotificationList';
3
3
  export * from './NotificationTargetContext';
4
4
  export * from './hooks';
5
+ export * from './notificationFilters';
5
6
  export * from './notificationTarget';
@@ -0,0 +1,26 @@
1
+ import type { Notification } from 'stream-chat';
2
+
3
+ import type { NotificationListFilter } from './NotificationList';
4
+
5
+ const ATTACHMENT_UPLOAD_FAILED_TYPE = 'api:attachment:upload:failed';
6
+
7
+ /**
8
+ * A cancelled upload surfaces as an `api:attachment:upload:failed` notification
9
+ * whose original error is an `AbortError` (fetch) or `CanceledError` (axios).
10
+ * This is what happens when the user removes an attachment before its upload
11
+ * has finished - the inflight request is aborted, not genuinely failed.
12
+ */
13
+ const isCanceledAttachmentUpload = (notification: Notification) =>
14
+ notification.type === ATTACHMENT_UPLOAD_FAILED_TYPE &&
15
+ (notification.originalError?.name === 'CanceledError' ||
16
+ notification.originalError?.name === 'AbortError');
17
+
18
+ /**
19
+ * Notification list filter that keeps every notification except the spurious
20
+ * "upload failed" one produced when a user removes an attachment mid-upload
21
+ * (a filter returns `true` to keep a notification). Genuine upload failures
22
+ * (network, server) are kept because their error is neither aborted nor
23
+ * cancelled.
24
+ */
25
+ export const excludeCanceledUploadNotifications: NotificationListFilter = (notification) =>
26
+ !isCanceledAttachmentUpload(notification);
package/src/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.5.1-beta.7"
2
+ "version": "9.5.1-beta.8"
3
3
  }