Skip to content
Merged
1 change: 1 addition & 0 deletions apps/meteor/app/ui/client/views/app/photoswipeContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const initGallery = async (items: Slide[], options: PhotoSwipe.Options): Promise

const defaultGalleryOptions = {
bgOpacity: 0.7,
counterEl: false,
index: 0,
wheelToZoom: true,
padding: { top: 20, bottom: 40, left: 100, right: 100 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import { useCollapse } from '../../../../../components/message/Attachments/hooks
import OEmbedPreviewContent from './OEmbedPreviewContent';
import type { PreviewMetadata } from './PreviewList';

type OEmbedCollapseableProps = { thumb?: ReactElement; children?: ReactNode } & PreviewMetadata;
type OEmbedCollapsibleProps = { children?: ReactNode } & PreviewMetadata;

const OEmbedCollapseable = ({ children, ...props }: OEmbedCollapseableProps): ReactElement => {
const OEmbedCollapsible = ({ children, ...props }: OEmbedCollapsibleProps): ReactElement => {
const t = useTranslation();
const [collapsed, collapse] = useCollapse(false);

return (
<>
<Box display='flex' flexDirection='row' color='hint' fontScale='c1'>
<Box display='flex' flexDirection='row' color='hint' fontScale='c1' alignItems='center'>
{t('Link_Preview')} {collapse}
</Box>
<MessageGenericPreview>
Expand All @@ -25,4 +25,4 @@ const OEmbedCollapseable = ({ children, ...props }: OEmbedCollapseableProps): Re
);
};

export default OEmbedCollapseable;
export default OEmbedCollapsible;
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Box } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';

import OEmbedCollapseable from './OEmbedCollapseable';
import OEmbedCollapsible from './OEmbedCollapsible';
import type { PreviewMetadata } from './PreviewList';

const OEmbedHtmlPreview = ({ html, ...props }: PreviewMetadata): ReactElement => (
<OEmbedCollapseable {...props}>{html && <Box withRichContent dangerouslySetInnerHTML={{ __html: html }} />}</OEmbedCollapseable>
<OEmbedCollapsible {...props}>{html && <Box withRichContent dangerouslySetInnerHTML={{ __html: html }} />}</OEmbedCollapsible>
);

export default OEmbedHtmlPreview;
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { MessageGenericPreviewImage } from '@rocket.chat/fuselage';
import { MessageGenericPreviewCoverImage } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';

import OEmbedCollapseable from './OEmbedCollapseable';
import OEmbedCollapsible from './OEmbedCollapsible';
import type { PreviewMetadata } from './PreviewList';

const OEmbedImagePreview = ({ image, ...props }: PreviewMetadata): ReactElement => (
<OEmbedCollapseable {...props}>
<MessageGenericPreviewImage height={192} width={368} url={image?.url || ''} />
</OEmbedCollapseable>
<OEmbedCollapsible {...props}>
{image?.url && <MessageGenericPreviewCoverImage height={192} width={368} url={image?.url} />}
</OEmbedCollapsible>
);

export default OEmbedImagePreview;
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import { MessageGenericPreviewThumb, MessageGenericPreviewImage, MessageGenericPreview } from '@rocket.chat/fuselage';
import { MessageGenericPreviewCoverImage } from '@rocket.chat/fuselage';
import { ExternalLink } from '@rocket.chat/ui-client';
import React, { ReactElement } from 'react';

import OEmbedPreviewContent from './OEmbedPreviewContent';
import OEmbedCollapsible from './OEmbedCollapsible';
import type { PreviewMetadata } from './PreviewList';

const OEmbedGenericPreview = ({ image, ...props }: PreviewMetadata): ReactElement => (
<MessageGenericPreview>
<OEmbedPreviewContent
{...props}
thumb={
image ? (
<MessageGenericPreviewThumb>
<MessageGenericPreviewImage height={192} width={368} url={image.url} />
</MessageGenericPreviewThumb>
) : undefined
}
/>
</MessageGenericPreview>
const OEmbedLinkPreview = ({ image, url, ...props }: PreviewMetadata): ReactElement => (
<OEmbedCollapsible url={url} {...props}>
{image?.url && url && (
<ExternalLink to={url}>
<MessageGenericPreviewCoverImage height={192} width={368} url={image?.url} />
</ExternalLink>
)}
</OEmbedCollapsible>
);

export default OEmbedGenericPreview;
export default OEmbedLinkPreview;
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ const OEmbedPreviewContent = ({

return (
<MessageGenericPreviewContent thumb={thumb}>
{title && <MessageGenericPreviewTitle externalUrl={url}>{title}</MessageGenericPreviewTitle>}
{title && (
<MessageGenericPreviewTitle externalUrl={url} title={title}>
{title}
</MessageGenericPreviewTitle>
)}
{description && <MessageGenericPreviewDescription>{description}</MessageGenericPreviewDescription>}
{(showSiteName || showAuthorName) && (
<MessageGenericPreviewFooter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MessageBlock } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';

import { useMessageOembedMaxWidth } from '../../../contexts/MessageContext';
import { isValidLink } from '../../lib/isValidLink';
import OEmbedResolver from './OEmbedResolver';
import UrlPreview from './UrlPreview';

Expand Down Expand Up @@ -49,6 +50,18 @@ type PreviewData = {
data: PreviewMetadata | UrlPreview;
};

export const buildImageURL = (url: string, imageUrl: string): string => {
if (isValidLink(imageUrl)) {
return JSON.stringify(imageUrl);
}

const { origin } = new URL(url);
const imgURL = `${origin}/${imageUrl}`;
const normalizedUrl = imgURL.replace(/(?<!:)\/+/gm, '/');

return JSON.stringify(normalizedUrl);
};

const normalizeMeta = ({ url, meta }: OembedUrlLegacy): PreviewMetadata => {
const image = meta.ogImage || meta.twitterImage || meta.msapplicationTileImage || meta.oembedThumbnailUrl || meta.oembedThumbnailUrl;

Expand All @@ -65,7 +78,7 @@ const normalizeMeta = ({ url, meta }: OembedUrlLegacy): PreviewMetadata => {
authorUrl: meta.oembedAuthorUrl,
...(image && {
image: {
url: image,
url: buildImageURL(url, image),
dimensions: {
...(imageHeight && { height: imageHeight }),
...(imageWidth && { width: imageWidth }),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { MessageGenericPreviewImage } from '@rocket.chat/fuselage';
import { Box, MessageGenericPreviewImage } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';

import { useMessageOembedMaxHeight, useMessageOembedMaxWidth } from '../../../contexts/MessageContext';
import type { UrlPreview } from './PreviewList';

const UrlImagePreview = ({ url }: Pick<UrlPreview, 'url'>): ReactElement => (
<MessageGenericPreviewImage height={192} width={368} url={url || ''} />
);
const UrlImagePreview = ({ url }: Pick<UrlPreview, 'url'>): ReactElement => {
const oembedMaxWidth = useMessageOembedMaxWidth();
const oembedMaxHeight = useMessageOembedMaxHeight();

return (
<Box maxHeight={oembedMaxHeight} maxWidth={oembedMaxWidth}>
<MessageGenericPreviewImage className='gallery-item' url={url || ''} />
</Box>
);
};

export default UrlImagePreview;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const UrlPreview = (props: UrlPreviewType): ReactElement => {

return (
<>
<Box display='flex' flexDirection='row' color='hint' fontScale='c1'>
<Box display='flex' flexDirection='row' color='hint' fontScale='c1' alignItems='center'>
{t('Link_Preview')} {collapse}
</Box>
{!collapsed && <UrlPreviewResolver {...props} />}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const isValidLink = (link: string): boolean => {
try {
return Boolean(new URL(link));
} catch (error) {
return false;
}
};
9 changes: 7 additions & 2 deletions apps/meteor/client/views/room/contexts/MessageContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const runActionLink = () => () => (): void => {
export type MessageContextValue = {
broadcast: boolean;
oembedMaxWidth: `${number}px` | '100%';
oembedMaxHeight: `${number}px`;
oembedEnabled: boolean;
actions: {
openUserCard: (username: string) => (e: UIEvent) => void;
Expand All @@ -34,10 +35,9 @@ export type MessageContextValue = {
};

export const MessageContext = createContext<MessageContextValue>({
// buttons: [],
// menuButtons: [],
oembedEnabled: false,
oembedMaxWidth: '368px',
oembedMaxHeight: '368px',
broadcast: false,
actions: {
openUserCard,
Expand Down Expand Up @@ -74,3 +74,8 @@ export const useMessageOembedMaxWidth = (): string => {
const context = useMessageActions();
return context.oembedMaxWidth;
};

export const useMessageOembedMaxHeight = (): string => {
const context = useMessageActions();
return context.oembedMaxHeight;
};
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const MessageProvider = memo(function MessageProvider({
return {
oembedEnabled,
oembedMaxWidth: isMobile ? ('100%' as const) : ('368px' as `${number}px`),
oembedMaxHeight: '368px' as `${number}px`,
broadcast: Boolean(broadcast),
actions: {
runActionLink,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect } from 'chai';

import { buildImageURL } from '../../../../../../../client/views/room/MessageList/components/UrlPreview/PreviewList';

describe('buildImageURL', () => {
const testCases = [
[
'https://g1.globo.com/mundo/video/misseis-atingem-ponte-de-vidro-em-kiev-11012523.ghtml',
'https://s2.glbimg.com/fXQKM_UZjF6I_3APIbPJzJTOUvw=/1200x/smart/filters:cover():strip_icc()/s04.video.glbimg.com/x720/11012523.jpg',
'https://s2.glbimg.com/fXQKM_UZjF6I_3APIbPJzJTOUvw=/1200x/smart/filters:cover():strip_icc()/s04.video.glbimg.com/x720/11012523.jpg',
],
['https://open.rocket.chat/channel/general', 'assets/favicon_512.png', 'https://open.rocket.chat/assets/favicon_512.png'],
['https://open.rocket.chat/channel/general', '/assets/favicon_512.png', 'https://open.rocket.chat/assets/favicon_512.png'],
['https://open.rocket.chat/channel/general/', '/assets/favicon_512.png', 'https://open.rocket.chat/assets/favicon_512.png'],
] as const;

testCases.forEach(([linkUrl, metaImgUrl, expectedResult]) => {
it(`should return ${expectedResult} for ${metaImgUrl}`, () => {
const result = buildImageURL(linkUrl, metaImgUrl);

expect(result).to.equal(JSON.stringify(expectedResult));
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { expect } from 'chai';

import { isValidLink } from '../../../../../../../client/views/room/MessageList/lib/isValidLink';

describe('isValidLink', () => {
const testCases = [
['/', false],
['test', false],
['test/test', false],
['.', false],
['./test', false],
['https://rocket.chat', true],
['rocket.chat', false],
['data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAAsBAAEAAAICTAEAOw==', true],
] as const;

testCases.forEach(([parameter, expectedResult]) => {
it(`should return ${JSON.stringify(expectedResult)} for ${JSON.stringify(parameter)}`, () => {
const result = isValidLink(parameter);
expect(result).to.be.equal(expectedResult);
});
});
});
6 changes: 3 additions & 3 deletions packages/fuselage-ui-kit/src/blocks/PreviewBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
MessageGenericPreview,
MessageGenericPreviewContent,
MessageGenericPreviewDescription,
MessageGenericPreviewImage,
MessageGenericPreviewCoverImage,
MessageGenericPreviewTitle,
MessageGenericPreviewFooter,
MessageGenericPreviewThumb,
Expand All @@ -28,7 +28,7 @@ const PreviewBlock = ({
<Box>
<MessageGenericPreview>
{isPreviewBlockWithPreview(block) && block.preview?.dimensions && (
<MessageGenericPreviewImage
<MessageGenericPreviewCoverImage
width={block.preview.dimensions.width}
height={block.preview.dimensions.height}
url={block.preview.url}
Expand All @@ -38,7 +38,7 @@ const PreviewBlock = ({
thumb={
isPreviewBlockWithThumb(block) ? (
<MessageGenericPreviewThumb>
<MessageGenericPreviewImage
<MessageGenericPreviewCoverImage
height={192}
width={368}
url={block.thumb.url}
Expand Down
Loading