Skip to content

Commit

Permalink
feat: convert attachment render functions into components, group atta…
Browse files Browse the repository at this point in the history
…chments in order
  • Loading branch information
MartinCupela committed Jul 6, 2022
1 parent 23c2d93 commit aeee078
Show file tree
Hide file tree
Showing 8 changed files with 787 additions and 228 deletions.
173 changes: 120 additions & 53 deletions src/components/Attachment/Attachment.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import React from 'react';
import React, { useMemo } from 'react';
import { sanitizeUrl } from '@braintree/sanitize-url';
import type { ReactPlayerProps } from 'react-player';
import type { Attachment as StreamAttachment } from 'stream-chat';

import {
AttachmentComponentType,
GroupedRenderedAttachment,
isAudioAttachment,
isFileAttachment,
isGalleryAttachmentType,
isImageAttachment,
isGiphyAttachment,
isMediaAttachment,
renderAudio,
renderCard,
renderFile,
renderGallery,
renderImage,
renderMedia,
isScrapedContent,
isUploadedImage,
} from './utils';

import type { ReactPlayerProps } from 'react-player';
import type { Attachment as StreamAttachment } from 'stream-chat';
import {
AudioContainer,
CardContainer,
FileContainer,
GalleryContainer,
ImageContainer,
MediaContainer,
} from './AttachmentContainer';

import type { AttachmentActionsProps } from './AttachmentActions';
import type { AudioProps } from './Audio';
Expand All @@ -26,6 +32,22 @@ import type { ActionHandlerReturnType } from '../Message/hooks/useActionHandler'

import type { DefaultStreamChatGenerics } from '../../types/types';

const CONTAINER_MAP = {
audio: AudioContainer,
card: CardContainer,
file: FileContainer,
media: MediaContainer,
} as const;

const ATTACHMENT_GROUPS_ORDER: AttachmentComponentType[] = [
'card',
'gallery',
'image',
'media',
'audio',
'file',
];

export type AttachmentProps<
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
> = {
Expand Down Expand Up @@ -57,54 +79,99 @@ export const Attachment = <
>(
props: AttachmentProps<StreamChatGenerics>,
) => {
const { attachments, ...rest } = props;

const gallery = {
images: attachments?.filter(
(attachment) =>
attachment.type === 'image' && !(attachment.og_scrape_url || attachment.title_link),
),
type: 'gallery',
};

const newAttachments =
gallery.images.length >= 2
? [
...attachments.filter(
(attachment) =>
!(
attachment.type === 'image' && !(attachment.og_scrape_url || attachment.title_link)
),
),
gallery,
]
: attachments;
const { attachments } = props;

const groupedAttachments = useMemo(() => renderGroupedAttachments(props), [attachments]);

return (
<>
{newAttachments.map((attachment) => {
if (isGalleryAttachmentType(attachment)) {
return renderGallery({ ...rest, attachment });
}
<div className='str-chat__attachment-list'>
{ATTACHMENT_GROUPS_ORDER.reduce(
(acc, groupName) => [...acc, ...groupedAttachments[groupName]],
[] as React.ReactNode[],
)}
</div>
);
};

if (isImageAttachment(attachment)) {
return renderImage({ ...rest, attachment });
}
const renderGroupedAttachments = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>({
attachments,
...rest
}: AttachmentProps<StreamChatGenerics>): GroupedRenderedAttachment => {
const uploadedImages: StreamAttachment<StreamChatGenerics>[] = [];

if (isAudioAttachment(attachment)) {
return renderAudio({ ...rest, attachment });
}
const containers = attachments.reduce(
(acc, attachment) => {
if (isUploadedImage(attachment)) {
uploadedImages.push({
...attachment,
image_url: sanitizeUrl(attachment.image_url),
og_scrape_url: sanitizeUrl(attachment.og_scrape_url),
thumb_url: sanitizeUrl(attachment.thumb_url),
title_link: sanitizeUrl(attachment.title_link),
});
} else {
const attachmentType = getAttachmentType(attachment);
// allow single card attachment
if (acc.card.length === 1 && attachmentType === 'card') return acc;

if (isFileAttachment(attachment)) {
return renderFile({ ...rest, attachment });
if (attachmentType) {
const Container = CONTAINER_MAP[attachmentType];
acc[attachmentType].push(
<Container
key={`${attachmentType}-${acc[attachmentType].length}`}
{...rest}
attachment={attachment}
/>,
);
}
}
return acc;
},
{
audio: [],
card: [],
file: [],
gallery: [],
image: [],
media: [],
} as GroupedRenderedAttachment,
);

if (isMediaAttachment(attachment)) {
return renderMedia({ ...rest, attachment });
}
if (uploadedImages.length > 1) {
containers['gallery'] = [
<GalleryContainer
key='gallery-container'
{...rest}
attachment={{
images: uploadedImages,
type: 'gallery',
}}
/>,
];
} else if (uploadedImages.length === 1) {
containers['image'] = [
<ImageContainer key='image-container' {...rest} attachment={uploadedImages[0]} />,
];
}
return containers;
};

return renderCard({ ...rest, attachment });
})}
</>
);
const getAttachmentType = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>(
attachment: AttachmentProps<StreamChatGenerics>['attachments'][number],
): keyof typeof CONTAINER_MAP | null => {
if (isScrapedContent(attachment) || isGiphyAttachment(attachment)) {
return 'card';
} else if (isMediaAttachment(attachment)) {
return 'media';
} else if (isAudioAttachment(attachment)) {
return 'audio';
} else if (isFileAttachment(attachment)) {
return 'file';
}

return null;
};
6 changes: 1 addition & 5 deletions src/components/Attachment/AttachmentActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ const UnMemoizedAttachmentActions = <
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
name?: string,
value?: string,
) => {
if (actionHandler) {
actionHandler(name, value, event);
}
};
) => actionHandler?.(name, value, event);

return (
<div className='str-chat__message-attachment-actions'>
Expand Down
Loading

0 comments on commit aeee078

Please sign in to comment.