fix(canvas): resolve hook rule violations in delete and image nodes
This commit is contained in:
@@ -99,7 +99,7 @@ export function useCanvasDeleteHandlers({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[],
|
[t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onNodesDelete = useCallback(
|
const onNodesDelete = useCallback(
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export default function ImageNode({
|
|||||||
const generateUploadUrl = useMutation(api.storage.generateUploadUrl);
|
const generateUploadUrl = useMutation(api.storage.generateUploadUrl);
|
||||||
const { queueNodeDataUpdate, queueNodeResize, status } = useCanvasSync();
|
const { queueNodeDataUpdate, queueNodeResize, status } = useCanvasSync();
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [uploadPhase, setUploadPhase] = useState<"idle" | "uploading" | "syncing">("idle");
|
||||||
const [uploadProgress, setUploadProgress] = useState(0);
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
const [pendingUploadStorageId, setPendingUploadStorageId] = useState<string | null>(
|
const [pendingUploadStorageId, setPendingUploadStorageId] = useState<string | null>(
|
||||||
null,
|
null,
|
||||||
@@ -93,19 +93,13 @@ export default function ImageNode({
|
|||||||
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
|
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
|
||||||
const hasAutoSizedRef = useRef(false);
|
const hasAutoSizedRef = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const isPendingUploadSynced =
|
||||||
if (!isUploading || !pendingUploadStorageId) return;
|
pendingUploadStorageId !== null &&
|
||||||
|
|
||||||
if (
|
|
||||||
data.storageId === pendingUploadStorageId &&
|
data.storageId === pendingUploadStorageId &&
|
||||||
typeof data.url === "string" &&
|
typeof data.url === "string" &&
|
||||||
data.url.length > 0
|
data.url.length > 0;
|
||||||
) {
|
const isWaitingForCanvasSync = pendingUploadStorageId !== null && !isPendingUploadSynced;
|
||||||
setIsUploading(false);
|
const isUploading = uploadPhase !== "idle" || isWaitingForCanvasSync;
|
||||||
setPendingUploadStorageId(null);
|
|
||||||
setUploadProgress(0);
|
|
||||||
}
|
|
||||||
}, [data.storageId, data.url, isUploading, pendingUploadStorageId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof id === "string" && id.startsWith(OPTIMISTIC_NODE_PREFIX)) {
|
if (typeof id === "string" && id.startsWith(OPTIMISTIC_NODE_PREFIX)) {
|
||||||
@@ -170,7 +164,7 @@ export default function ImageNode({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsUploading(true);
|
setUploadPhase("uploading");
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
setPendingUploadStorageId(null);
|
setPendingUploadStorageId(null);
|
||||||
|
|
||||||
@@ -216,6 +210,7 @@ export default function ImageNode({
|
|||||||
|
|
||||||
setUploadProgress(100);
|
setUploadProgress(100);
|
||||||
setPendingUploadStorageId(storageId);
|
setPendingUploadStorageId(storageId);
|
||||||
|
setUploadPhase("syncing");
|
||||||
|
|
||||||
await queueNodeDataUpdate({
|
await queueNodeDataUpdate({
|
||||||
nodeId: id as Id<"nodes">,
|
nodeId: id as Id<"nodes">,
|
||||||
@@ -241,6 +236,7 @@ export default function ImageNode({
|
|||||||
}
|
}
|
||||||
|
|
||||||
toast.success(t('canvas.imageUploaded'));
|
toast.success(t('canvas.imageUploaded'));
|
||||||
|
setUploadPhase("idle");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Upload failed:", err);
|
console.error("Upload failed:", err);
|
||||||
setPendingUploadStorageId(null);
|
setPendingUploadStorageId(null);
|
||||||
@@ -249,7 +245,7 @@ export default function ImageNode({
|
|||||||
err instanceof Error ? err.message : undefined,
|
err instanceof Error ? err.message : undefined,
|
||||||
);
|
);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
setIsUploading(false);
|
setUploadPhase("idle");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -259,6 +255,7 @@ export default function ImageNode({
|
|||||||
queueNodeDataUpdate,
|
queueNodeDataUpdate,
|
||||||
queueNodeResize,
|
queueNodeResize,
|
||||||
status.isOffline,
|
status.isOffline,
|
||||||
|
t,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -312,8 +309,9 @@ export default function ImageNode({
|
|||||||
}, [isUploading]);
|
}, [isUploading]);
|
||||||
|
|
||||||
const showFilename = Boolean(data.filename && data.url);
|
const showFilename = Boolean(data.filename && data.url);
|
||||||
|
const effectiveUploadProgress = isWaitingForCanvasSync ? 100 : uploadProgress;
|
||||||
const uploadingLabel =
|
const uploadingLabel =
|
||||||
uploadProgress === 100 && pendingUploadStorageId
|
isWaitingForCanvasSync
|
||||||
? "100% — wird synchronisiert…"
|
? "100% — wird synchronisiert…"
|
||||||
: "Wird hochgeladen…";
|
: "Wird hochgeladen…";
|
||||||
|
|
||||||
@@ -365,10 +363,10 @@ export default function ImageNode({
|
|||||||
<div className="flex flex-col items-center gap-2">
|
<div className="flex flex-col items-center gap-2">
|
||||||
<span className="text-xs text-muted-foreground">{uploadingLabel}</span>
|
<span className="text-xs text-muted-foreground">{uploadingLabel}</span>
|
||||||
<div className="w-40">
|
<div className="w-40">
|
||||||
<Progress value={uploadProgress} className="h-1.5" />
|
<Progress value={effectiveUploadProgress} className="h-1.5" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[11px] text-muted-foreground">
|
<span className="text-[11px] text-muted-foreground">
|
||||||
{uploadProgress}%
|
{effectiveUploadProgress}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user