Skip to content

Commit

Permalink
feat: add Card component for theming v2
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinCupela committed Jul 8, 2022
1 parent 40342fe commit 5f5341f
Show file tree
Hide file tree
Showing 3 changed files with 2,071 additions and 192 deletions.
172 changes: 145 additions & 27 deletions src/components/Attachment/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
import React, { PropsWithChildren } from 'react';
import React from 'react';
import clsx from 'clsx';
import ReactPlayer from 'react-player';

import { CardAudio } from './Audio';
import { SafeAnchor } from '../SafeAnchor';

import { useTranslationContext } from '../../context/TranslationContext';
import { useChatContext } from '../../context/ChatContext';
import { useChannelStateContext } from '../../context/ChannelStateContext';
import { useTranslationContext } from '../../context/TranslationContext';

import type { Attachment } from 'stream-chat';
import type { RenderAttachmentProps } from './utils';

const trimUrl = (url?: string | null) => {
if (url !== undefined && url !== null) {
const [trimmedUrl] = url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '').split('/');

return trimmedUrl;
}
return null;
};

const UnableToRenderCard = ({ type }: { type?: CardProps['type'] }) => {
const { t } = useTranslationContext('Card');

return (
<div
className={clsx(
'str-chat__message-attachment-card',
type && `str-chat__message-attachment-card--${type}`,
)}
>
<div className='str-chat__message-attachment-card--content'>
<div className='str-chat__message-attachment-card--text'>
{t<string>('this content could not be displayed')}
</div>
</div>
</div>
);
};

type Dimensions = { height?: string; width?: string };

export type CardProps = {
interface CardV1Props {
giphy?: Attachment['giphy'];
/** The url of the full sized image */
image_url?: string;
Expand All @@ -22,15 +58,14 @@ export type CardProps = {
title_link?: string;
/** The card type used in the className attribute */
type?: string;
};
}

const UnMemoizedCard = (props: PropsWithChildren<CardProps>) => {
const CardV1 = (props: CardV1Props) => {
const { giphy, image_url, og_scrape_url, text, thumb_url, title, title_link, type } = props;
const { t } = useTranslationContext('Card');
const { giphyVersion: giphyVersionName } = useChannelStateContext('Card');

let image = thumb_url || image_url;
const dimensions: { height?: string; width?: string } = {};
const dimensions: Dimensions = {};

if (type === 'giphy' && typeof giphy !== 'undefined') {
const giphyVersion = giphy[giphyVersionName as keyof NonNullable<Attachment['giphy']>];
Expand All @@ -39,27 +74,8 @@ const UnMemoizedCard = (props: PropsWithChildren<CardProps>) => {
dimensions.width = giphyVersion.width;
}

const trimUrl = (url?: string | null) => {
if (url !== undefined && url !== null) {
const [trimmedUrl] = url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '').split('/');

return trimmedUrl;
}
return null;
};

if (!title && !title_link && !image) {
return (
<div
className={`str-chat__message-attachment-card str-chat__message-attachment-card--${type}`}
>
<div className='str-chat__message-attachment-card--content'>
<div className='str-chat__message-attachment-card--text'>
{t<string>('this content could not be displayed')}
</div>
</div>
</div>
);
return <UnableToRenderCard type={type} />;
}

if (!title_link && !og_scrape_url) {
Expand Down Expand Up @@ -93,6 +109,108 @@ const UnMemoizedCard = (props: PropsWithChildren<CardProps>) => {
);
};

const Caption = ({ author_name, url }: Pick<CardProps, 'author_name'> & { url: string }) => (
<div className='str-chat__message-attachment-card--caption' data-testid='card-caption'>
<SafeAnchor
className='str-chat__message-attachment-card--url'
href={url}
rel='noopener noreferrer'
target='_blank'
>
{author_name || trimUrl(url)}
</SafeAnchor>
</div>
);

type CardHeaderProps = Pick<
CardProps,
'asset_url' | 'author_name' | 'og_scrape_url' | 'title' | 'title_link' | 'type'
> & { dimensions: Dimensions; image?: string };

const CardHeader = (props: CardHeaderProps) => {
const {
asset_url,
author_name,
dimensions,
image,
og_scrape_url,
title,
title_link,
type,
} = props;

let visual = null;
if (asset_url && type === 'video') {
visual = (
<ReactPlayer className='react-player' controls height='100%' url={asset_url} width='100%' />
);
} else if (image) {
visual = <img alt={title || image} src={image} {...dimensions} />;
}

const url = og_scrape_url || title_link;

return visual ? (
<div className='str-chat__message-attachment-card--header' data-testid={'card-header'}>
{visual}
{url && <Caption author_name={author_name} url={url} />}
</div>
) : null;
};

type CardContentProps = RenderAttachmentProps['attachment'];

const CardContent = (props: CardContentProps) => {
const { text, title, type } = props;

return (
<div className='str-chat__message-attachment-card--content'>
{type === 'audio' ? (
<CardAudio og={props} />
) : (
<div className='str-chat__message-attachment-card--flex'>
{title && <div className='str-chat__message-attachment-card--title'>{title}</div>}
{text && <div className='str-chat__message-attachment-card--text'>{text}</div>}
</div>
)}
</div>
);
};

const CardV2 = (props: CardProps) => {
const { asset_url, giphy, image_url, thumb_url, title, title_link, type } = props;
const { giphyVersion: giphyVersionName } = useChannelStateContext('CardHeader');

let image = thumb_url || image_url;
const dimensions: { height?: string; width?: string } = {};

if (type === 'giphy' && typeof giphy !== 'undefined') {
const giphyVersion = giphy[giphyVersionName as keyof NonNullable<Attachment['giphy']>];
image = giphyVersion.url;
dimensions.height = giphyVersion.height;
dimensions.width = giphyVersion.width;
}

if (!title && !title_link && !asset_url && !image) {
return <UnableToRenderCard />;
}

return (
<div className={`str-chat__message-attachment-card str-chat__message-attachment-card--${type}`}>
<CardHeader {...props} dimensions={dimensions} image={image} />
<CardContent {...props} />
</div>
);
};

export type CardProps = RenderAttachmentProps['attachment'];

const UnMemoizedCard = (props: CardProps) => {
const { themeVersion } = useChatContext('Card');

return themeVersion === '2' ? <CardV2 {...props} /> : <CardV1 {...props} />;
};

/**
* Simple Card Layout for displaying links
*/
Expand Down
Loading

0 comments on commit 5f5341f

Please sign in to comment.