Enhance adjustment nodes with localized labels and improved state management
- Integrated localization for slider labels and prompts in color-adjust, curves, detail-adjust, and light-adjust nodes. - Updated preset handling to include user-friendly prompts for saving presets. - Improved state management for slider values and preset selections, enhancing user experience. - Added reset actions to adjustment nodes for better control over user adjustments.
This commit is contained in:
@@ -336,7 +336,11 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
const pendingResizeAfterCreateRef = useRef(
|
||||
new Map<string, { width: number; height: number }>(),
|
||||
);
|
||||
const pendingDataAfterCreateRef = useRef(new Map<string, unknown>());
|
||||
const resolvedRealIdByClientRequestRef = useRef(new Map<string, Id<"nodes">>());
|
||||
const pendingCreatePromiseByClientRequestRef = useRef(
|
||||
new Map<string, Promise<Id<"nodes">>>(),
|
||||
);
|
||||
const pendingEdgeSplitByClientRequestRef = useRef(
|
||||
new Map<string, PendingEdgeSplit>(),
|
||||
);
|
||||
@@ -580,6 +584,29 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
const isSyncOnline =
|
||||
isBrowserOnline === true && connectionState.isWebSocketConnected === true;
|
||||
|
||||
const trackPendingNodeCreate = useCallback(
|
||||
(
|
||||
clientRequestId: string,
|
||||
createPromise: Promise<Id<"nodes">>,
|
||||
): Promise<Id<"nodes">> => {
|
||||
const trackedPromise = createPromise
|
||||
.then((realId) => {
|
||||
resolvedRealIdByClientRequestRef.current.set(clientRequestId, realId);
|
||||
return realId;
|
||||
})
|
||||
.finally(() => {
|
||||
pendingCreatePromiseByClientRequestRef.current.delete(clientRequestId);
|
||||
});
|
||||
|
||||
pendingCreatePromiseByClientRequestRef.current.set(
|
||||
clientRequestId,
|
||||
trackedPromise,
|
||||
);
|
||||
return trackedPromise;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleOnline = () => setIsBrowserOnline(true);
|
||||
const handleOffline = () => setIsBrowserOnline(false);
|
||||
@@ -763,6 +790,8 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
|
||||
pendingMoveAfterCreateRef.current.delete(args.clientRequestId);
|
||||
pendingResizeAfterCreateRef.current.delete(args.clientRequestId);
|
||||
pendingDataAfterCreateRef.current.delete(args.clientRequestId);
|
||||
pendingCreatePromiseByClientRequestRef.current.delete(args.clientRequestId);
|
||||
pendingEdgeSplitByClientRequestRef.current.delete(args.clientRequestId);
|
||||
pendingConnectionCreatesRef.current.delete(args.clientRequestId);
|
||||
resolvedRealIdByClientRequestRef.current.delete(args.clientRequestId);
|
||||
@@ -848,24 +877,28 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
const payload = { ...args, clientRequestId };
|
||||
|
||||
if (isSyncOnline) {
|
||||
return await createNode(payload);
|
||||
return await trackPendingNodeCreate(clientRequestId, createNode(payload));
|
||||
}
|
||||
|
||||
const optimisticNodeId = addOptimisticNodeLocally(payload);
|
||||
await enqueueSyncMutationRef.current("createNode", payload);
|
||||
return optimisticNodeId;
|
||||
},
|
||||
[addOptimisticNodeLocally, createNode, isSyncOnline],
|
||||
[addOptimisticNodeLocally, createNode, isSyncOnline, trackPendingNodeCreate],
|
||||
);
|
||||
|
||||
const runCreateNodeWithEdgeFromSourceOnlineOnly = useCallback(
|
||||
async (args: Parameters<typeof createNodeWithEdgeFromSource>[0]) => {
|
||||
const clientRequestId = args.clientRequestId ?? crypto.randomUUID();
|
||||
const payload = { ...args, clientRequestId };
|
||||
const sourceNodeId = payload.sourceNodeId as string;
|
||||
|
||||
pendingConnectionCreatesRef.current.add(clientRequestId);
|
||||
if (isSyncOnline) {
|
||||
return await createNodeWithEdgeFromSource(payload);
|
||||
if (isSyncOnline && !isOptimisticNodeId(sourceNodeId)) {
|
||||
return await trackPendingNodeCreate(
|
||||
clientRequestId,
|
||||
createNodeWithEdgeFromSource(payload),
|
||||
);
|
||||
}
|
||||
|
||||
const optimisticNodeId = addOptimisticNodeLocally(payload);
|
||||
@@ -876,6 +909,24 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
sourceHandle: payload.sourceHandle,
|
||||
targetHandle: payload.targetHandle,
|
||||
});
|
||||
|
||||
if (isSyncOnline) {
|
||||
try {
|
||||
const realId = await trackPendingNodeCreate(clientRequestId, createNodeWithEdgeFromSourceRaw({
|
||||
...payload,
|
||||
}));
|
||||
await remapOptimisticNodeLocally(clientRequestId, realId);
|
||||
return realId;
|
||||
} catch (error) {
|
||||
removeOptimisticCreateLocally({
|
||||
clientRequestId,
|
||||
removeNode: true,
|
||||
removeEdge: true,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await enqueueSyncMutationRef.current(
|
||||
"createNodeWithEdgeFromSource",
|
||||
payload,
|
||||
@@ -886,7 +937,11 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
addOptimisticEdgeLocally,
|
||||
addOptimisticNodeLocally,
|
||||
createNodeWithEdgeFromSource,
|
||||
createNodeWithEdgeFromSourceRaw,
|
||||
isSyncOnline,
|
||||
remapOptimisticNodeLocally,
|
||||
removeOptimisticCreateLocally,
|
||||
trackPendingNodeCreate,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -894,10 +949,14 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
async (args: Parameters<typeof createNodeWithEdgeToTarget>[0]) => {
|
||||
const clientRequestId = args.clientRequestId ?? crypto.randomUUID();
|
||||
const payload = { ...args, clientRequestId };
|
||||
const targetNodeId = payload.targetNodeId as string;
|
||||
|
||||
pendingConnectionCreatesRef.current.add(clientRequestId);
|
||||
if (isSyncOnline) {
|
||||
return await createNodeWithEdgeToTarget(payload);
|
||||
if (isSyncOnline && !isOptimisticNodeId(targetNodeId)) {
|
||||
return await trackPendingNodeCreate(
|
||||
clientRequestId,
|
||||
createNodeWithEdgeToTarget(payload),
|
||||
);
|
||||
}
|
||||
|
||||
const optimisticNodeId = addOptimisticNodeLocally(payload);
|
||||
@@ -908,6 +967,24 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
sourceHandle: payload.sourceHandle,
|
||||
targetHandle: payload.targetHandle,
|
||||
});
|
||||
|
||||
if (isSyncOnline) {
|
||||
try {
|
||||
const realId = await trackPendingNodeCreate(clientRequestId, createNodeWithEdgeToTargetRaw({
|
||||
...payload,
|
||||
}));
|
||||
await remapOptimisticNodeLocally(clientRequestId, realId);
|
||||
return realId;
|
||||
} catch (error) {
|
||||
removeOptimisticCreateLocally({
|
||||
clientRequestId,
|
||||
removeNode: true,
|
||||
removeEdge: true,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await enqueueSyncMutationRef.current("createNodeWithEdgeToTarget", payload);
|
||||
return optimisticNodeId;
|
||||
},
|
||||
@@ -915,7 +992,11 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
addOptimisticEdgeLocally,
|
||||
addOptimisticNodeLocally,
|
||||
createNodeWithEdgeToTarget,
|
||||
createNodeWithEdgeToTargetRaw,
|
||||
isSyncOnline,
|
||||
remapOptimisticNodeLocally,
|
||||
removeOptimisticCreateLocally,
|
||||
trackPendingNodeCreate,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1287,6 +1368,19 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
[enqueueSyncMutation],
|
||||
);
|
||||
|
||||
const flushPendingDataForClientRequest = useCallback(
|
||||
async (clientRequestId: string, realId: Id<"nodes">): Promise<void> => {
|
||||
if (!pendingDataAfterCreateRef.current.has(clientRequestId)) return;
|
||||
const pendingData = pendingDataAfterCreateRef.current.get(clientRequestId);
|
||||
pendingDataAfterCreateRef.current.delete(clientRequestId);
|
||||
await enqueueSyncMutation("updateData", {
|
||||
nodeId: realId,
|
||||
data: pendingData,
|
||||
});
|
||||
},
|
||||
[enqueueSyncMutation],
|
||||
);
|
||||
|
||||
const runResizeNodeMutation = useCallback(
|
||||
async (args: { nodeId: Id<"nodes">; width: number; height: number }) => {
|
||||
const rawNodeId = args.nodeId as string;
|
||||
@@ -1336,9 +1430,44 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
|
||||
const runUpdateNodeDataMutation = useCallback(
|
||||
async (args: { nodeId: Id<"nodes">; data: unknown }) => {
|
||||
await enqueueSyncMutation("updateData", args);
|
||||
const rawNodeId = args.nodeId as string;
|
||||
if (!isOptimisticNodeId(rawNodeId)) {
|
||||
await enqueueSyncMutation("updateData", args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSyncOnline) {
|
||||
await enqueueSyncMutation("updateData", args);
|
||||
return;
|
||||
}
|
||||
|
||||
const clientRequestId = clientRequestIdFromOptimisticNodeId(rawNodeId);
|
||||
const resolvedRealId = clientRequestId
|
||||
? resolvedRealIdByClientRequestRef.current.get(clientRequestId)
|
||||
: undefined;
|
||||
|
||||
if (resolvedRealId) {
|
||||
await enqueueSyncMutation("updateData", {
|
||||
nodeId: resolvedRealId,
|
||||
data: args.data,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientRequestId) {
|
||||
pendingDataAfterCreateRef.current.set(clientRequestId, args.data);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.info("[Canvas sync debug] deferred updateData for optimistic node", {
|
||||
nodeId: rawNodeId,
|
||||
clientRequestId,
|
||||
resolvedRealId: resolvedRealId ?? null,
|
||||
hasData: args.data !== undefined,
|
||||
});
|
||||
}
|
||||
},
|
||||
[enqueueSyncMutation],
|
||||
[enqueueSyncMutation, isSyncOnline],
|
||||
);
|
||||
|
||||
const runBatchRemoveNodesMutation = useCallback(
|
||||
@@ -1590,6 +1719,7 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
);
|
||||
pendingMoveAfterCreateRef.current.delete(clientRequestId);
|
||||
pendingResizeAfterCreateRef.current.delete(clientRequestId);
|
||||
pendingDataAfterCreateRef.current.delete(clientRequestId);
|
||||
pendingEdgeSplitByClientRequestRef.current.delete(clientRequestId);
|
||||
pendingConnectionCreatesRef.current.delete(clientRequestId);
|
||||
resolvedRealIdByClientRequestRef.current.delete(clientRequestId);
|
||||
@@ -1642,6 +1772,7 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
});
|
||||
}
|
||||
await flushPendingResizeForClientRequest(clientRequestId, realId);
|
||||
await flushPendingDataForClientRequest(clientRequestId, realId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1663,11 +1794,13 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
positionY: pendingMove.positionY,
|
||||
});
|
||||
await flushPendingResizeForClientRequest(clientRequestId, realId);
|
||||
await flushPendingDataForClientRequest(clientRequestId, realId);
|
||||
return;
|
||||
}
|
||||
|
||||
resolvedRealIdByClientRequestRef.current.set(clientRequestId, realId);
|
||||
await flushPendingResizeForClientRequest(clientRequestId, realId);
|
||||
await flushPendingDataForClientRequest(clientRequestId, realId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1700,6 +1833,7 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
error: String(error),
|
||||
});
|
||||
}
|
||||
await flushPendingDataForClientRequest(clientRequestId, r);
|
||||
} else {
|
||||
pendingLocalPositionUntilConvexMatchesRef.current.set(r as string, {
|
||||
x: p.positionX,
|
||||
@@ -1710,11 +1844,13 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
||||
positionX: p.positionX,
|
||||
positionY: p.positionY,
|
||||
});
|
||||
await flushPendingDataForClientRequest(clientRequestId, r);
|
||||
}
|
||||
},
|
||||
[
|
||||
canvasId,
|
||||
runBatchRemoveNodesMutation,
|
||||
flushPendingDataForClientRequest,
|
||||
flushPendingResizeForClientRequest,
|
||||
runMoveNodeMutation,
|
||||
runSplitEdgeAtExistingNodeMutation,
|
||||
|
||||
Reference in New Issue
Block a user