mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-03-11 10:32:37 +01:00
🚀 feat: Enhance Message Editing with File Resubmission (#2347)
* chore: fix type issue with File Table fakeData * refactor: new lazy loading image strategy and load images/files as part of Message Container * feat: resubmit files when editing messages with attached files
This commit is contained in:
parent
caabab4489
commit
3411d7a543
10 changed files with 122 additions and 90 deletions
|
|
@ -1,18 +1,35 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import { LazyLoadImage } from 'react-lazy-load-image-component';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import DialogImage from './DialogImage';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const scaleImage = ({
|
||||
originalWidth,
|
||||
originalHeight,
|
||||
containerRef,
|
||||
}: {
|
||||
originalWidth: number;
|
||||
originalHeight: number;
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
}) => {
|
||||
const containerWidth = containerRef.current?.offsetWidth ?? 0;
|
||||
if (containerWidth === 0 || originalWidth === undefined || originalHeight === undefined) {
|
||||
return { width: 'auto', height: 'auto' };
|
||||
}
|
||||
const aspectRatio = originalWidth / originalHeight;
|
||||
const scaledWidth = Math.min(containerWidth, originalWidth);
|
||||
const scaledHeight = scaledWidth / aspectRatio;
|
||||
return { width: `${scaledWidth}px`, height: `${scaledHeight}px` };
|
||||
};
|
||||
|
||||
const Image = ({
|
||||
imagePath,
|
||||
altText,
|
||||
height,
|
||||
width,
|
||||
placeholderDimensions,
|
||||
}: // n,
|
||||
// i,
|
||||
{
|
||||
}: {
|
||||
imagePath: string;
|
||||
altText: string;
|
||||
height: number;
|
||||
|
|
@ -21,69 +38,49 @@ const Image = ({
|
|||
height: string;
|
||||
width: string;
|
||||
};
|
||||
// n: number;
|
||||
// i: number;
|
||||
}) => {
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleImageLoad = () => setIsLoaded(true);
|
||||
const [minDisplayTimeElapsed, setMinDisplayTimeElapsed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let timer: NodeJS.Timeout;
|
||||
if (isLoaded) {
|
||||
timer = setTimeout(() => setMinDisplayTimeElapsed(true), 150);
|
||||
}
|
||||
return () => clearTimeout(timer);
|
||||
}, [isLoaded]);
|
||||
// const makeSquare = n >= 3 && i < 2;
|
||||
|
||||
let placeholderHeight = '288px';
|
||||
if (placeholderDimensions?.height && placeholderDimensions?.width) {
|
||||
placeholderHeight = placeholderDimensions.height;
|
||||
} else if (height > width) {
|
||||
placeholderHeight = '900px';
|
||||
} else if (height === width) {
|
||||
placeholderHeight = width + 'px';
|
||||
}
|
||||
const { width: scaledWidth, height: scaledHeight } = useMemo(
|
||||
() =>
|
||||
scaleImage({
|
||||
originalWidth: Number(placeholderDimensions?.width?.split('px')[0]) ?? width,
|
||||
originalHeight: Number(placeholderDimensions?.height?.split('px')[0]) ?? height,
|
||||
containerRef,
|
||||
}),
|
||||
[placeholderDimensions, height, width],
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<div className="">
|
||||
<div ref={containerRef}>
|
||||
<div className="relative mt-1 flex h-auto w-full max-w-lg items-center justify-center overflow-hidden bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400">
|
||||
<Dialog.Trigger asChild>
|
||||
<button type="button" aria-haspopup="dialog" aria-expanded="false">
|
||||
<LazyLoadImage
|
||||
// loading="lazy"
|
||||
alt={altText}
|
||||
onLoad={handleImageLoad}
|
||||
visibleByDefault={true}
|
||||
className={cn(
|
||||
'max-h-[900px] max-w-full opacity-100 transition-opacity duration-300',
|
||||
// n >= 3 && i < 2 ? 'aspect-square object-cover' : '',
|
||||
isLoaded && minDisplayTimeElapsed ? 'opacity-100' : 'opacity-0',
|
||||
'opacity-100 transition-opacity duration-100',
|
||||
isLoaded ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
src={imagePath}
|
||||
style={{
|
||||
height: isLoaded && minDisplayTimeElapsed ? 'auto' : placeholderHeight,
|
||||
width: placeholderDimensions?.width ?? width,
|
||||
width: scaledWidth,
|
||||
height: 'auto',
|
||||
color: 'transparent',
|
||||
}}
|
||||
placeholder={
|
||||
<div
|
||||
style={{
|
||||
height: isLoaded && minDisplayTimeElapsed ? 'auto' : placeholderHeight,
|
||||
width: placeholderDimensions?.width ?? width,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
placeholder={<div style={{ width: scaledWidth, height: scaledHeight }} />}
|
||||
/>
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
</div>
|
||||
</div>
|
||||
{isLoaded && minDisplayTimeElapsed && (
|
||||
<DialogImage src={imagePath} height={height} width={width} />
|
||||
)}
|
||||
{isLoaded && <DialogImage src={imagePath} height={height} width={width} />}
|
||||
</Dialog.Root>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue