🛠️ refactor: Handle .webp, Improve File Life Cycle 📁 (#1213)

* fix: handle webp images correctly

* refactor: use the userPath from the start of the filecycle to avoid handling the blob, whose loading may fail upon user request

* refactor: delete temp files on reload and new chat
This commit is contained in:
Danny Avila 2023-11-24 16:45:06 -05:00 committed by GitHub
parent 650759306d
commit cc39074e0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 160 additions and 66 deletions

View file

@ -2,6 +2,7 @@ import debounce from 'lodash/debounce';
import { useState, useEffect, useCallback } from 'react';
import type { BatchFile } from 'librechat-data-provider';
import { useDeleteFilesMutation } from '~/data-provider';
import { useSetFilesToDelete } from '~/hooks';
import { ExtendedFile } from '~/common';
import Image from './Image';
@ -14,6 +15,7 @@ export default function Images({
setFiles: React.Dispatch<React.SetStateAction<Map<string, ExtendedFile>>>;
setFilesLoading: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const setFilesToDelete = useSetFilesToDelete();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_batch, setFileDeleteBatch] = useState<BatchFile[]>([]);
const files = Array.from(_files.values());
@ -37,7 +39,7 @@ export default function Images({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [files]);
const deleteFiles = useDeleteFilesMutation({
const { mutateAsync } = useDeleteFilesMutation({
onSuccess: () => {
console.log('Files deleted');
},
@ -49,10 +51,10 @@ export default function Images({
const executeBatchDelete = useCallback(
(filesToDelete: BatchFile[]) => {
console.log('Deleting files:', filesToDelete);
deleteFiles.mutate({ files: filesToDelete });
mutateAsync({ files: filesToDelete });
setFileDeleteBatch([]);
},
[deleteFiles],
[mutateAsync],
);
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -81,6 +83,8 @@ export default function Images({
const updatedFiles = new Map(currentFiles);
updatedFiles.delete(file_id);
updatedFiles.delete(temp_file_id);
const files = Object.fromEntries(updatedFiles);
setFilesToDelete(files);
return updatedFiles;
});

View file

@ -2,8 +2,9 @@ import { useState } from 'react';
import { Settings } from 'lucide-react';
import { EModelEndpoint } from 'librechat-data-provider';
import type { FC } from 'react';
import { useLocalize, useUserKey, useNewConvo, useOriginNavigate } from '~/hooks';
import { useLocalize, useUserKey, useOriginNavigate } from '~/hooks';
import { SetKeyDialog } from '~/components/Input/SetKeyDialog';
import { useChatContext } from '~/Providers';
import { icons } from './Icons';
import { cn } from '~/utils';
@ -27,8 +28,8 @@ const MenuItem: FC<MenuItemProps> = ({
}) => {
const Icon = icons[endpoint] ?? icons.unknown;
const [isDialogOpen, setDialogOpen] = useState(false);
const { newConversation } = useChatContext();
const { getExpiry } = useUserKey(endpoint);
const { newConversation } = useNewConvo();
const navigate = useOriginNavigate();
const localize = useLocalize();
const expiryTime = getExpiry();

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, memo } from 'react';
import React, { useState, useEffect } from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import * as Dialog from '@radix-ui/react-dialog';
import DialogImage from './DialogImage';
@ -19,7 +19,6 @@ const Image = ({
// n: number;
// i: number;
}) => {
const prevImagePathRef = useRef<string | null>(null);
const [isLoaded, setIsLoaded] = useState(false);
const handleImageLoad = () => setIsLoaded(true);
const [minDisplayTimeElapsed, setMinDisplayTimeElapsed] = useState(false);
@ -31,17 +30,14 @@ const Image = ({
}
return () => clearTimeout(timer);
}, [isLoaded]);
useEffect(() => {
const prevImagePath = prevImagePathRef.current;
if (prevImagePath && prevImagePath?.startsWith('blob:') && prevImagePath !== imagePath) {
URL.revokeObjectURL(prevImagePath);
}
prevImagePathRef.current = imagePath;
}, [imagePath]);
// const makeSquare = n >= 3 && i < 2;
const placeholderHeight = height > width ? '900px' : '288px';
let placeholderHeight = '288px';
if (height > width) {
placeholderHeight = '900px';
} else if (height === width) {
placeholderHeight = width + 'px';
}
return (
<Dialog.Root>
@ -82,4 +78,4 @@ const Image = ({
);
};
export default memo(Image);
export default Image;

View file

@ -1,8 +1,38 @@
import { useEffect } from 'react';
import type { ExtendedFile } from '~/common';
import { useDragHelpers, useSetFilesToDelete } from '~/hooks';
import DragDropOverlay from './Input/Files/DragDropOverlay';
import { useDragHelpers } from '~/hooks';
import { useDeleteFilesMutation } from '~/data-provider';
export default function Presentation({ children }: { children: React.ReactNode }) {
const { isOver, canDrop, drop } = useDragHelpers();
const setFilesToDelete = useSetFilesToDelete();
const { mutateAsync } = useDeleteFilesMutation({
onSuccess: () => {
console.log('Temporary Files deleted');
setFilesToDelete({});
},
onError: (error) => {
console.log('Error deleting temporary files:', error);
},
});
useEffect(() => {
const filesToDelete = localStorage.getItem('filesToDelete');
const map = JSON.parse(filesToDelete ?? '{}') as Record<string, ExtendedFile>;
const files = Object.values(map)
.filter((file) => file.filepath)
.map((file) => ({
file_id: file.file_id,
filepath: file.filepath as string,
}));
if (files.length === 0) {
return;
}
mutateAsync({ files });
}, [mutateAsync]);
const isActive = canDrop && isOver;
return (
<div ref={drop} className="relative flex w-full grow overflow-hidden bg-white dark:bg-gray-800">