mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(files): wire serverFileId for version tracking
Add serverFileId to FileEntry, pass fileId in tool processor formData when a file originates from the Files page, and set serverFileId on the loaded entry so subsequent tool runs create new file versions instead of duplicates.
This commit is contained in:
@@ -56,6 +56,13 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
|
|||||||
const file = new File([blob], details.originalName, { type: details.mimeType });
|
const file = new File([blob], details.originalName, { type: details.mimeType });
|
||||||
setFiles([file]);
|
setFiles([file]);
|
||||||
navigate("/");
|
navigate("/");
|
||||||
|
// Set serverFileId so tool processing creates a new version
|
||||||
|
setTimeout(() => {
|
||||||
|
const entries = useFileStore.getState().entries;
|
||||||
|
if (entries.length > 0) {
|
||||||
|
useFileStore.getState().updateEntry(0, { serverFileId: details.id });
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedFileId) {
|
if (!selectedFileId) {
|
||||||
@@ -63,7 +70,9 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-center justify-center text-muted-foreground",
|
"flex flex-col items-center justify-center text-muted-foreground",
|
||||||
mobile ? "flex-1" : "w-72 shrink-0 border-l border-border",
|
mobile
|
||||||
|
? "flex flex-col gap-4"
|
||||||
|
: "w-60 border-l border-border p-4 shrink-0 hidden lg:flex flex-col",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FileImage className="h-12 w-12 mb-3 opacity-30" />
|
<FileImage className="h-12 w-12 mb-3 opacity-30" />
|
||||||
@@ -77,7 +86,9 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-center",
|
"flex items-center justify-center",
|
||||||
mobile ? "flex-1" : "w-72 shrink-0 border-l border-border",
|
mobile
|
||||||
|
? "flex flex-col gap-4"
|
||||||
|
: "w-60 border-l border-border p-4 shrink-0 hidden lg:flex flex-col",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="h-6 w-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
<div className="h-6 w-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
@@ -90,12 +101,14 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col overflow-y-auto",
|
"overflow-y-auto",
|
||||||
mobile ? "flex-1" : "w-72 shrink-0 border-l border-border",
|
mobile
|
||||||
|
? "flex flex-col gap-4"
|
||||||
|
: "w-60 border-l border-border p-4 shrink-0 hidden lg:flex flex-col",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Thumbnail */}
|
{/* Thumbnail */}
|
||||||
<div className="p-4 border-b border-border">
|
<div className={cn("border-b border-border", mobile ? "" : "pb-4")}>
|
||||||
<img
|
<img
|
||||||
src={getFileThumbnailUrl(details.id)}
|
src={getFileThumbnailUrl(details.id)}
|
||||||
alt={details.originalName}
|
alt={details.originalName}
|
||||||
@@ -104,7 +117,7 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Details card */}
|
{/* Details card */}
|
||||||
<div className="flex-1 p-4">
|
<div className="flex-1">
|
||||||
<div className="rounded-lg border border-border overflow-hidden">
|
<div className="rounded-lg border border-border overflow-hidden">
|
||||||
<div className="bg-blue-500/10 border-b border-border px-3 py-2">
|
<div className="bg-blue-500/10 border-b border-border px-3 py-2">
|
||||||
<h4 className="text-sm font-semibold text-blue-600 dark:text-blue-400">File Details</h4>
|
<h4 className="text-sm font-semibold text-blue-600 dark:text-blue-400">File Details</h4>
|
||||||
@@ -135,7 +148,7 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Open File button */}
|
{/* Open File button */}
|
||||||
<div className="p-4 border-t border-border">
|
<div className={cn("border-t border-border", mobile ? "pt-3" : "pt-4")}>
|
||||||
<button
|
<button
|
||||||
onClick={handleOpenFile}
|
onClick={handleOpenFile}
|
||||||
className="w-full px-4 py-2 bg-primary text-primary-foreground text-sm font-medium rounded-lg hover:bg-primary/90 transition-colors"
|
className="w-full px-4 py-2 bg-primary text-primary-foreground text-sm font-medium rounded-lg hover:bg-primary/90 transition-colors"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ interface ProcessResult {
|
|||||||
downloadUrl: string;
|
downloadUrl: string;
|
||||||
originalSize: number;
|
originalSize: number;
|
||||||
processedSize: number;
|
processedSize: number;
|
||||||
|
savedFileId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolProgress {
|
export interface ToolProgress {
|
||||||
@@ -128,6 +129,12 @@ export function useToolProcessor(toolId: string) {
|
|||||||
formData.append("clientJobId", clientJobId);
|
formData.append("clientJobId", clientJobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this file came from the Files page, include its ID for version tracking
|
||||||
|
const currentEntry = useFileStore.getState().currentEntry;
|
||||||
|
if (currentEntry?.serverFileId) {
|
||||||
|
formData.append("fileId", currentEntry.serverFileId);
|
||||||
|
}
|
||||||
|
|
||||||
// Use XHR for upload progress tracking
|
// Use XHR for upload progress tracking
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhrRef.current = xhr;
|
xhrRef.current = xhr;
|
||||||
@@ -168,6 +175,13 @@ export function useToolProcessor(toolId: string) {
|
|||||||
setJobId(result.jobId);
|
setJobId(result.jobId);
|
||||||
setProcessedUrl(result.downloadUrl);
|
setProcessedUrl(result.downloadUrl);
|
||||||
setSizes(result.originalSize, result.processedSize);
|
setSizes(result.originalSize, result.processedSize);
|
||||||
|
// Update serverFileId if a new version was saved
|
||||||
|
if (result.savedFileId) {
|
||||||
|
const state = useFileStore.getState();
|
||||||
|
if (state.entries[state.selectedIndex]) {
|
||||||
|
state.updateEntry(state.selectedIndex, { serverFileId: result.savedFileId });
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setError("Invalid response from server");
|
setError("Invalid response from server");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface FileEntry {
|
|||||||
originalSize: number;
|
originalSize: number;
|
||||||
status: "pending" | "processing" | "completed" | "failed";
|
status: "pending" | "processing" | "completed" | "failed";
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
serverFileId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -23,6 +24,7 @@ function createEntry(file: File): FileEntry {
|
|||||||
originalSize: file.size,
|
originalSize: file.size,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
error: null,
|
error: null,
|
||||||
|
serverFileId: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user