mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-03-12 06:00:01 +01:00
Show image preview in upload window (#2231)
* memoize metadata callback properly * add image preview on upload * show spoiler image button inside image preview
This commit is contained in:
parent
ccfe30cd68
commit
5c94471956
2 changed files with 71 additions and 38 deletions
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import { Chip, Icon, IconButton, Icons, Text, Tooltip, TooltipProvider, color } from 'folds';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box, Chip, Icon, IconButton, Icons, Text, color, config, toRem } from 'folds';
|
||||
import { UploadCard, UploadCardError, UploadCardProgress } from './UploadCard';
|
||||
import { UploadStatus, UploadSuccess, useBindUploadAtom } from '../../state/upload';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
|
@ -10,11 +10,60 @@ import {
|
|||
TUploadItem,
|
||||
TUploadMetadata,
|
||||
} from '../../state/room/roomInputDrafts';
|
||||
import { useObjectURL } from '../../hooks/useObjectURL';
|
||||
|
||||
type ImagePreviewProps = { fileItem: TUploadItem; onSpoiler: (marked: boolean) => void };
|
||||
function ImagePreview({ fileItem, onSpoiler }: ImagePreviewProps) {
|
||||
const { originalFile, metadata } = fileItem;
|
||||
const fileUrl = useObjectURL(originalFile);
|
||||
|
||||
return fileUrl ? (
|
||||
<Box
|
||||
style={{
|
||||
borderRadius: config.radii.R300,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'black',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
objectFit: 'contain',
|
||||
width: '100%',
|
||||
height: toRem(152),
|
||||
filter: fileItem.metadata.markedAsSpoiler ? 'blur(44px)' : undefined,
|
||||
}}
|
||||
src={fileUrl}
|
||||
alt={originalFile.name}
|
||||
/>
|
||||
<Box
|
||||
justifyContent="End"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: config.space.S100,
|
||||
left: config.space.S100,
|
||||
right: config.space.S100,
|
||||
}}
|
||||
>
|
||||
<Chip
|
||||
variant={metadata.markedAsSpoiler ? 'Warning' : 'Secondary'}
|
||||
fill="Soft"
|
||||
radii="Pill"
|
||||
aria-pressed={metadata.markedAsSpoiler}
|
||||
before={<Icon src={Icons.EyeBlind} size="50" />}
|
||||
onClick={() => onSpoiler(!metadata.markedAsSpoiler)}
|
||||
>
|
||||
<Text size="B300">Spoiler</Text>
|
||||
</Chip>
|
||||
</Box>
|
||||
</Box>
|
||||
) : null;
|
||||
}
|
||||
|
||||
type UploadCardRendererProps = {
|
||||
isEncrypted?: boolean;
|
||||
fileItem: TUploadItem;
|
||||
setMetadata: (metadata: TUploadMetadata) => void;
|
||||
setMetadata: (fileItem: TUploadItem, metadata: TUploadMetadata) => void;
|
||||
onRemove: (file: TUploadContent) => void;
|
||||
onComplete?: (upload: UploadSuccess) => void;
|
||||
};
|
||||
|
@ -33,9 +82,9 @@ export function UploadCardRenderer({
|
|||
|
||||
if (upload.status === UploadStatus.Idle) startUpload();
|
||||
|
||||
const toggleSpoiler = useCallback(() => {
|
||||
setMetadata({ ...metadata, markedAsSpoiler: !metadata.markedAsSpoiler });
|
||||
}, [setMetadata, metadata]);
|
||||
const handleSpoiler = (marked: boolean) => {
|
||||
setMetadata(fileItem, { ...metadata, markedAsSpoiler: marked });
|
||||
};
|
||||
|
||||
const removeUpload = () => {
|
||||
cancelUpload();
|
||||
|
@ -66,31 +115,6 @@ export function UploadCardRenderer({
|
|||
<Text size="B300">Retry</Text>
|
||||
</Chip>
|
||||
)}
|
||||
{file.type.startsWith('image') && (
|
||||
<TooltipProvider
|
||||
tooltip={
|
||||
<Tooltip variant="SurfaceVariant">
|
||||
<Text>Mark as Spoiler</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
position="Top"
|
||||
align="Center"
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
onClick={toggleSpoiler}
|
||||
aria-label="Mark as Spoiler"
|
||||
variant="SurfaceVariant"
|
||||
radii="Pill"
|
||||
size="300"
|
||||
aria-pressed={metadata.markedAsSpoiler}
|
||||
>
|
||||
<Icon src={Icons.EyeBlind} size="200" />
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
)}
|
||||
<IconButton
|
||||
onClick={removeUpload}
|
||||
aria-label="Cancel Upload"
|
||||
|
@ -104,6 +128,9 @@ export function UploadCardRenderer({
|
|||
}
|
||||
bottom={
|
||||
<>
|
||||
{fileItem.originalFile.type.startsWith('image') && (
|
||||
<ImagePreview fileItem={fileItem} onSpoiler={handleSpoiler} />
|
||||
)}
|
||||
{upload.status === UploadStatus.Idle && (
|
||||
<UploadCardProgress sentBytes={0} totalBytes={file.size} />
|
||||
)}
|
||||
|
|
|
@ -70,6 +70,7 @@ import { useFilePasteHandler } from '../../hooks/useFilePasteHandler';
|
|||
import { useFileDropZone } from '../../hooks/useFileDrop';
|
||||
import {
|
||||
TUploadItem,
|
||||
TUploadMetadata,
|
||||
roomIdToMsgDraftAtomFamily,
|
||||
roomIdToReplyDraftAtomFamily,
|
||||
roomIdToUploadItemsAtomFamily,
|
||||
|
@ -220,6 +221,17 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
|||
[roomId, editor, setMsgDraft]
|
||||
);
|
||||
|
||||
const handleFileMetadata = useCallback(
|
||||
(fileItem: TUploadItem, metadata: TUploadMetadata) => {
|
||||
setSelectedFiles({
|
||||
type: 'REPLACE',
|
||||
item: fileItem,
|
||||
replacement: { ...fileItem, metadata },
|
||||
});
|
||||
},
|
||||
[setSelectedFiles]
|
||||
);
|
||||
|
||||
const handleRemoveUpload = useCallback(
|
||||
(upload: TUploadContent | TUploadContent[]) => {
|
||||
const uploads = Array.isArray(upload) ? upload : [upload];
|
||||
|
@ -433,13 +445,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
|||
key={index}
|
||||
isEncrypted={!!fileItem.encInfo}
|
||||
fileItem={fileItem}
|
||||
setMetadata={(metadata) =>
|
||||
setSelectedFiles({
|
||||
type: 'REPLACE',
|
||||
item: fileItem,
|
||||
replacement: { ...fileItem, metadata },
|
||||
})
|
||||
}
|
||||
setMetadata={handleFileMetadata}
|
||||
onRemove={handleRemoveUpload}
|
||||
/>
|
||||
))}
|
||||
|
|
Loading…
Reference in a new issue