forked from github/cinny
🦉 img tag conversion
This commit is contained in:
@@ -25,6 +25,8 @@ import {
|
||||
VideoContent,
|
||||
} from './message';
|
||||
import { UrlPreviewCard, UrlPreviewHolder } from './url-preview';
|
||||
import { ExternalImageCard } from '../../owl/components/ExternalImageCard';
|
||||
import { isImageUrl } from '../../owl/utils/imageUrl';
|
||||
import { Image, MediaControl, Video } from './media';
|
||||
import { ImageViewer } from './image-viewer';
|
||||
import { PdfViewer } from './Pdf-viewer';
|
||||
@@ -61,12 +63,23 @@ export function RenderMessageContent({
|
||||
const renderUrlsPreview = (urls: string[]) => {
|
||||
const filteredUrls = urls.filter((url) => !testMatrixTo(url));
|
||||
if (filteredUrls.length === 0) return undefined;
|
||||
|
||||
const imageUrls = filteredUrls.filter(isImageUrl);
|
||||
const otherUrls = filteredUrls.filter((url) => !isImageUrl(url));
|
||||
|
||||
return (
|
||||
<>
|
||||
{imageUrls.map((url) => (
|
||||
<ExternalImageCard key={url} url={url} />
|
||||
))}
|
||||
{otherUrls.length > 0 && (
|
||||
<UrlPreviewHolder>
|
||||
{filteredUrls.map((url) => (
|
||||
{otherUrls.map((url) => (
|
||||
<UrlPreviewCard key={url} url={url} ts={ts} />
|
||||
))}
|
||||
</UrlPreviewHolder>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
const renderCaption = () => {
|
||||
|
||||
28
src/owl/components/ExternalImageCard.css.tsx
Normal file
28
src/owl/components/ExternalImageCard.css.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { DefaultReset, color, config, toRem } from 'folds';
|
||||
|
||||
export const ExternalImageCard = style([
|
||||
DefaultReset,
|
||||
{
|
||||
display: 'inline-block',
|
||||
maxWidth: toRem(400),
|
||||
borderRadius: config.radii.R300,
|
||||
overflow: 'hidden',
|
||||
border: `${config.borderWidth.B300} solid ${color.SurfaceVariant.ContainerLine}`,
|
||||
cursor: 'pointer',
|
||||
|
||||
':hover': {
|
||||
filter: 'brightness(0.95)',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const ExternalImage = style([
|
||||
DefaultReset,
|
||||
{
|
||||
display: 'block',
|
||||
maxWidth: '100%',
|
||||
maxHeight: toRem(400),
|
||||
objectFit: 'contain',
|
||||
},
|
||||
]);
|
||||
57
src/owl/components/ExternalImageCard.tsx
Normal file
57
src/owl/components/ExternalImageCard.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Text, as, color } from 'folds';
|
||||
import { ImageOverlay } from '../../app/components/ImageOverlay';
|
||||
import { ImageViewer } from '../../app/components/image-viewer';
|
||||
import { onEnterOrSpace } from '../../app/utils/keyboard';
|
||||
import * as css from './ExternalImageCard.css';
|
||||
import { tryDecodeURIComponent } from '../../app/utils/dom';
|
||||
|
||||
const linkStyles = { color: color.Success.Main };
|
||||
|
||||
export const ExternalImageCard = as<'div', { url: string }>(({ url, ...props }, ref) => {
|
||||
const [viewer, setViewer] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
if (error) return null;
|
||||
|
||||
const filename = url.split('/').pop()?.split('?')[0] || url;
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100" {...props} ref={ref}>
|
||||
<div
|
||||
className={css.ExternalImageCard}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(evt) => onEnterOrSpace(() => setViewer(true))(evt)}
|
||||
onClick={() => setViewer(true)}
|
||||
>
|
||||
<img
|
||||
className={css.ExternalImage}
|
||||
src={url}
|
||||
alt={filename}
|
||||
loading="lazy"
|
||||
onError={() => setError(true)}
|
||||
/>
|
||||
</div>
|
||||
<Text
|
||||
style={linkStyles}
|
||||
truncate
|
||||
as="a"
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
size="T200"
|
||||
priority="300"
|
||||
>
|
||||
{tryDecodeURIComponent(url)}
|
||||
</Text>
|
||||
<ImageOverlay
|
||||
src={url}
|
||||
alt={filename}
|
||||
viewer={viewer}
|
||||
requestClose={() => setViewer(false)}
|
||||
renderViewer={(p) => <ImageViewer {...p} />}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
10
src/owl/utils/imageUrl.ts
Normal file
10
src/owl/utils/imageUrl.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.avif', '.svg', '.bmp', '.ico'];
|
||||
|
||||
export function isImageUrl(url: string): boolean {
|
||||
try {
|
||||
const { pathname } = new URL(url);
|
||||
return IMAGE_EXTENSIONS.some((ext) => pathname.toLowerCase().endsWith(ext));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user