forked from github/cinny
Show image viewer when clicking url preview thumbnail (#2309)
* Show large image overlay when clicking url preview thumbnail * Move image overlay into its own component * Move ImageOverlay props into extended type * Remove export for internal type
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { IPreviewUrlResponse } from 'matrix-js-sdk';
|
||||
import { Box, Icon, IconButton, Icons, Scroll, Spinner, Text, as, color, config } from 'folds';
|
||||
import { RenderViewerProps, ImageOverlay } from '../ImageOverlay';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { UrlPreview, UrlPreviewContent, UrlPreviewDescription, UrlPreviewImg } from './UrlPreview';
|
||||
@@ -15,72 +16,94 @@ import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||
|
||||
const linkStyles = { color: color.Success.Main };
|
||||
|
||||
export const UrlPreviewCard = as<'div', { url: string; ts: number }>(
|
||||
({ url, ts, ...props }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
const [previewStatus, loadPreview] = useAsyncCallback(
|
||||
useCallback(() => mx.getUrlPreview(url, ts), [url, ts, mx])
|
||||
export const UrlPreviewCard = as<
|
||||
'div',
|
||||
{ url: string; ts: number; renderViewer: (props: RenderViewerProps) => ReactNode }
|
||||
>(({ url, ts, renderViewer, ...props }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
const [viewer, setViewer] = useState(false);
|
||||
const [previewStatus, loadPreview] = useAsyncCallback(
|
||||
useCallback(() => mx.getUrlPreview(url, ts), [url, ts, mx])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadPreview();
|
||||
}, [loadPreview]);
|
||||
|
||||
if (previewStatus.status === AsyncStatus.Error) return null;
|
||||
|
||||
const renderContent = (prev: IPreviewUrlResponse) => {
|
||||
const thumbUrl = mxcUrlToHttp(
|
||||
mx,
|
||||
prev['og:image'] || '',
|
||||
useAuthentication,
|
||||
256,
|
||||
256,
|
||||
'scale',
|
||||
false
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadPreview();
|
||||
}, [loadPreview]);
|
||||
|
||||
if (previewStatus.status === AsyncStatus.Error) return null;
|
||||
|
||||
const renderContent = (prev: IPreviewUrlResponse) => {
|
||||
const imgUrl = mxcUrlToHttp(
|
||||
mx,
|
||||
prev['og:image'] || '',
|
||||
useAuthentication,
|
||||
256,
|
||||
256,
|
||||
'scale',
|
||||
false
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{imgUrl && <UrlPreviewImg src={imgUrl} alt={prev['og:title']} title={prev['og:title']} />}
|
||||
<UrlPreviewContent>
|
||||
<Text
|
||||
style={linkStyles}
|
||||
truncate
|
||||
as="a"
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
size="T200"
|
||||
priority="300"
|
||||
>
|
||||
{typeof prev['og:site_name'] === 'string' && `${prev['og:site_name']} | `}
|
||||
{tryDecodeURIComponent(url)}
|
||||
</Text>
|
||||
<Text truncate priority="400">
|
||||
<b>{prev['og:title']}</b>
|
||||
</Text>
|
||||
<Text size="T200" priority="300">
|
||||
<UrlPreviewDescription>{prev['og:description']}</UrlPreviewDescription>
|
||||
</Text>
|
||||
</UrlPreviewContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
const imgUrl = mxcUrlToHttp(mx, prev['og:image'] || '', useAuthentication);
|
||||
|
||||
return (
|
||||
<UrlPreview {...props} ref={ref}>
|
||||
{previewStatus.status === AsyncStatus.Success ? (
|
||||
renderContent(previewStatus.data)
|
||||
) : (
|
||||
<Box grow="Yes" alignItems="Center" justifyContent="Center">
|
||||
<Spinner variant="Secondary" size="400" />
|
||||
</Box>
|
||||
<>
|
||||
{thumbUrl && (
|
||||
<UrlPreviewImg
|
||||
src={thumbUrl}
|
||||
alt={prev['og:title']}
|
||||
title={prev['og:title']}
|
||||
onClick={() => setViewer(true)}
|
||||
/>
|
||||
)}
|
||||
</UrlPreview>
|
||||
{imgUrl && (
|
||||
<ImageOverlay
|
||||
src={imgUrl}
|
||||
alt={prev['og:title']}
|
||||
viewer={viewer}
|
||||
requestClose={() => {
|
||||
setViewer(false);
|
||||
}}
|
||||
renderViewer={renderViewer}
|
||||
/>
|
||||
)}
|
||||
<UrlPreviewContent>
|
||||
<Text
|
||||
style={linkStyles}
|
||||
truncate
|
||||
as="a"
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
size="T200"
|
||||
priority="300"
|
||||
>
|
||||
{typeof prev['og:site_name'] === 'string' && `${prev['og:site_name']} | `}
|
||||
{tryDecodeURIComponent(url)}
|
||||
</Text>
|
||||
<Text truncate priority="400">
|
||||
<b>{prev['og:title']}</b>
|
||||
</Text>
|
||||
<Text size="T200" priority="300">
|
||||
<UrlPreviewDescription>{prev['og:description']}</UrlPreviewDescription>
|
||||
</Text>
|
||||
</UrlPreviewContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<UrlPreview {...props} ref={ref}>
|
||||
{previewStatus.status === AsyncStatus.Success ? (
|
||||
renderContent(previewStatus.data)
|
||||
) : (
|
||||
<Box grow="Yes" alignItems="Center" justifyContent="Center">
|
||||
<Spinner variant="Secondary" size="400" />
|
||||
</Box>
|
||||
)}
|
||||
</UrlPreview>
|
||||
);
|
||||
});
|
||||
|
||||
export const UrlPreviewHolder = as<'div'>(({ children, ...props }, ref) => {
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
Reference in New Issue
Block a user