Skip to content

Commit 330b622

Browse files
feat(Attachments): introduce UnsupportedAttachment component (#1952)
Introduces "UnsupportedAttachment" component to handle unsupported attachment types. This component is disabled by default.
1 parent cd52420 commit 330b622

File tree

4 files changed

+82
-35
lines changed

4 files changed

+82
-35
lines changed

src/components/Attachment/Attachment.tsx

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ import {
1919
GalleryContainer,
2020
ImageContainer,
2121
MediaContainer,
22+
UnsupportedAttachmentContainer,
2223
} from './AttachmentContainer';
2324

2425
import type { AttachmentActionsProps } from './AttachmentActions';
2526
import type { AudioProps } from './Audio';
2627
import type { CardProps } from './Card';
2728
import type { FileAttachmentProps } from './FileAttachment';
2829
import type { GalleryProps, ImageProps } from '../Gallery';
30+
import type { UnsupportedAttachmentProps } from './UnsupportedAttachment';
2931
import type { ActionHandlerReturnType } from '../Message/hooks/useActionHandler';
3032

3133
import type { DefaultStreamChatGenerics } from '../../types/types';
@@ -35,6 +37,7 @@ const CONTAINER_MAP = {
3537
card: CardContainer,
3638
file: FileContainer,
3739
media: MediaContainer,
40+
unsupported: UnsupportedAttachmentContainer,
3841
} as const;
3942

4043
export const ATTACHMENT_GROUPS_ORDER = [
@@ -44,6 +47,7 @@ export const ATTACHMENT_GROUPS_ORDER = [
4447
'media',
4548
'audio',
4649
'file',
50+
'unsupported',
4751
] as const;
4852

4953
export type AttachmentProps<
@@ -67,6 +71,8 @@ export type AttachmentProps<
6771
Image?: React.ComponentType<ImageProps>;
6872
/** Custom UI component for displaying a media type attachment, defaults to `ReactPlayer` from 'react-player' */
6973
Media?: React.ComponentType<ReactPlayerProps>;
74+
/** Custom UI component for displaying unsupported attachment types, defaults to NullComponent */
75+
UnsupportedAttachment?: React.ComponentType<UnsupportedAttachmentProps>;
7076
};
7177

7278
/**
@@ -97,41 +103,44 @@ const renderGroupedAttachments = <
97103
attachments,
98104
...rest
99105
}: AttachmentProps<StreamChatGenerics>): GroupedRenderedAttachment => {
100-
const uploadedImages: StreamAttachment<StreamChatGenerics>[] = [];
101-
102-
const containers = attachments.reduce<GroupedRenderedAttachment>(
103-
(acc, attachment) => {
104-
if (isUploadedImage(attachment)) {
105-
uploadedImages.push({
106-
...attachment,
107-
image_url: sanitizeUrl(attachment.image_url),
108-
thumb_url: sanitizeUrl(attachment.thumb_url),
109-
});
110-
} else {
106+
const uploadedImages: StreamAttachment<StreamChatGenerics>[] = attachments
107+
.filter((attachment) => isUploadedImage(attachment))
108+
.map((attachment) => ({
109+
...attachment,
110+
image_url: sanitizeUrl(attachment.image_url),
111+
thumb_url: sanitizeUrl(attachment.thumb_url),
112+
}));
113+
114+
const containers = attachments
115+
.filter((attachment) => !isUploadedImage(attachment))
116+
.reduce<GroupedRenderedAttachment>(
117+
(typeMap, attachment) => {
111118
const attachmentType = getAttachmentType(attachment);
112119

113-
if (attachmentType) {
114-
const Container = CONTAINER_MAP[attachmentType];
115-
acc[attachmentType].push(
116-
<Container
117-
key={`${attachmentType}-${acc[attachmentType].length}`}
118-
{...rest}
119-
attachment={attachment}
120-
/>,
121-
);
122-
}
123-
}
124-
return acc;
125-
},
126-
{
127-
audio: [],
128-
card: [],
129-
file: [],
130-
gallery: [],
131-
image: [],
132-
media: [],
133-
},
134-
);
120+
const Container = CONTAINER_MAP[attachmentType];
121+
typeMap[attachmentType].push(
122+
<Container
123+
key={`${attachmentType}-${typeMap[attachmentType].length}`}
124+
{...rest}
125+
attachment={attachment}
126+
/>,
127+
);
128+
129+
return typeMap;
130+
},
131+
{
132+
audio: [],
133+
card: [],
134+
file: [],
135+
media: [],
136+
unsupported: [],
137+
// not used in reduce
138+
// eslint-disable-next-line sort-keys
139+
image: [],
140+
// eslint-disable-next-line sort-keys
141+
gallery: [],
142+
},
143+
);
135144

136145
if (uploadedImages.length > 1) {
137146
containers['gallery'] = [
@@ -149,14 +158,15 @@ const renderGroupedAttachments = <
149158
<ImageContainer key='image-container' {...rest} attachment={uploadedImages[0]} />,
150159
];
151160
}
161+
152162
return containers;
153163
};
154164

155165
const getAttachmentType = <
156166
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
157167
>(
158168
attachment: AttachmentProps<StreamChatGenerics>['attachments'][number],
159-
): keyof typeof CONTAINER_MAP | null => {
169+
): keyof typeof CONTAINER_MAP => {
160170
if (isScrapedContent(attachment)) {
161171
return 'card';
162172
} else if (isMediaAttachment(attachment)) {
@@ -167,5 +177,5 @@ const getAttachmentType = <
167177
return 'file';
168178
}
169179

170-
return null;
180+
return 'unsupported';
171181
};

src/components/Attachment/AttachmentContainer.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Audio as DefaultAudio } from './Audio';
99
import { Gallery as DefaultGallery, ImageComponent as DefaultImage } from '../Gallery';
1010
import { Card as DefaultCard } from './Card';
1111
import { FileAttachment as DefaultFile } from './FileAttachment';
12+
import { NullComponent as DefaultUnsupportedAttachment } from './UnsupportedAttachment';
1213
import {
1314
AttachmentContainerProps,
1415
isGalleryAttachmentType,
@@ -292,3 +293,14 @@ export const MediaContainer = <
292293
</AttachmentWithinContainer>
293294
);
294295
};
296+
297+
export const UnsupportedAttachmentContainer = <
298+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
299+
>({
300+
attachment,
301+
UnsupportedAttachment = DefaultUnsupportedAttachment,
302+
}: RenderAttachmentProps<StreamChatGenerics>) => (
303+
<>
304+
<UnsupportedAttachment attachment={attachment} />
305+
</>
306+
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import type { Attachment } from 'stream-chat';
3+
import type { DefaultStreamChatGenerics } from '../../types/types';
4+
5+
export type UnsupportedAttachmentProps<
6+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
7+
> = {
8+
attachment: Attachment<StreamChatGenerics>;
9+
};
10+
11+
export const UnsupportedAttachment = <
12+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
13+
>({
14+
attachment,
15+
}: UnsupportedAttachmentProps<StreamChatGenerics>) => (
16+
<div>
17+
<div>
18+
Unsupported attachment type <strong>{attachment.type ?? 'unknown'}</strong>
19+
</div>
20+
<code>{JSON.stringify(attachment, null, 4)}</code>;
21+
</div>
22+
);
23+
24+
export const NullComponent = () => null;

src/components/Attachment/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export * from './AttachmentActions';
33
export * from './AttachmentContainer';
44
export * from './Audio';
55
export * from './Card';
6+
export * from './UnsupportedAttachment';
67
export * from './FileAttachment';
78
export * from './utils';

0 commit comments

Comments
 (0)