@yogiswara/honcho-editor-ui 2.6.6 â 2.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/editor/HAccordionPreset.js +30 -30
- package/dist/hooks/demo/HonchoEditorBulkDemo.js +47 -12
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +1 -1
- package/dist/hooks/editor/type.d.ts +1 -1
- package/dist/hooks/editor/useHonchoEditorBulk.d.ts +15 -1
- package/dist/hooks/editor/useHonchoEditorBulk.js +88 -15
- package/dist/hooks/editor/useHonchoEditorSingle.d.ts +2 -1
- package/dist/hooks/editor/useHonchoEditorSingle.js +7 -3
- package/dist/hooks/useAdjustmentHistoryBatch.d.ts +2 -0
- package/dist/hooks/useAdjustmentHistoryBatch.js +478 -206
- package/dist/hooks/usePreset.js +35 -23
- package/package.json +1 -1
|
@@ -120,22 +120,49 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
120
120
|
const [selectedIds, setSelectedIds] = useState([]);
|
|
121
121
|
const [imageHistories, setImageHistories] = useState([]);
|
|
122
122
|
const [currentBatch, setCurrentBatch] = useState(createEmptyBatchState());
|
|
123
|
+
const [lastUpdateTimestamp, setLastUpdateTimestamp] = useState(Date.now());
|
|
123
124
|
// Configuration refs
|
|
124
125
|
const maxSizeRef = useRef(internalOptions.maxSize);
|
|
125
126
|
const devWarningsRef = useRef(internalOptions.devWarnings);
|
|
126
127
|
// Helper function to rebuild currentBatch from imageHistories
|
|
127
128
|
const rebuildCurrentBatch = useCallback(() => {
|
|
129
|
+
console.log('[useAdjustmentHistoryBatch] đ§ rebuildCurrentBatch called with imageHistories:', imageHistories.map(h => ({
|
|
130
|
+
imageId: h.imageId,
|
|
131
|
+
currentHistoryEntryId: h.currentHistoryEntryId,
|
|
132
|
+
historyLength: h.history.length,
|
|
133
|
+
historyIds: h.history.map(entry => entry.id)
|
|
134
|
+
})));
|
|
128
135
|
const newBatch = createEmptyBatchState();
|
|
129
136
|
imageHistories.forEach(imageHistory => {
|
|
130
137
|
// Find current adjustment using currentHistoryEntryId
|
|
131
138
|
const currentEntry = imageHistory.history.find(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
139
|
+
console.log(`[useAdjustmentHistoryBatch] đ rebuildCurrentBatch for image ${imageHistory.imageId}:`, {
|
|
140
|
+
currentHistoryEntryId: imageHistory.currentHistoryEntryId,
|
|
141
|
+
foundCurrentEntry: !!currentEntry,
|
|
142
|
+
historyLength: imageHistory.history.length,
|
|
143
|
+
historyIds: imageHistory.history.map(entry => entry.id)
|
|
144
|
+
});
|
|
132
145
|
if (currentEntry) {
|
|
133
146
|
newBatch.allImages[imageHistory.imageId] = currentEntry.adjustment;
|
|
134
147
|
if (selectedIds.includes(imageHistory.imageId)) {
|
|
135
148
|
newBatch.currentSelection[imageHistory.imageId] = currentEntry.adjustment;
|
|
136
149
|
}
|
|
150
|
+
console.log(`[useAdjustmentHistoryBatch] â
Successfully rebuilt batch for image ${imageHistory.imageId}`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.error(`[useAdjustmentHistoryBatch] â CRITICAL: Current entry not found for image ${imageHistory.imageId}!`, {
|
|
154
|
+
searchingFor: imageHistory.currentHistoryEntryId,
|
|
155
|
+
availableIds: imageHistory.history.map(entry => entry.id),
|
|
156
|
+
historyLength: imageHistory.history.length
|
|
157
|
+
});
|
|
137
158
|
}
|
|
138
159
|
});
|
|
160
|
+
console.log('[useAdjustmentHistoryBatch] đ§ rebuildCurrentBatch result:', {
|
|
161
|
+
allImagesCount: Object.keys(newBatch.allImages).length,
|
|
162
|
+
currentSelectionCount: Object.keys(newBatch.currentSelection).length,
|
|
163
|
+
allImagesIds: Object.keys(newBatch.allImages),
|
|
164
|
+
currentSelectionIds: Object.keys(newBatch.currentSelection)
|
|
165
|
+
});
|
|
139
166
|
return newBatch;
|
|
140
167
|
}, [imageHistories, selectedIds]);
|
|
141
168
|
// Sync currentBatch with imageHistories
|
|
@@ -184,76 +211,105 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
184
211
|
}
|
|
185
212
|
return;
|
|
186
213
|
}
|
|
187
|
-
//
|
|
214
|
+
// Prepare backend operations BEFORE state update (outside setImageHistories)
|
|
188
215
|
const backendOperations = [];
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
216
|
+
// Process each image to prepare operations
|
|
217
|
+
const operationsToApply = [];
|
|
218
|
+
imageHistories.forEach(imageHistory => {
|
|
219
|
+
if (!selectedIds.includes(imageHistory.imageId)) {
|
|
220
|
+
return; // Skip images not being adjusted
|
|
221
|
+
}
|
|
222
|
+
// Get current adjustment from current entry
|
|
223
|
+
const currentEntry = imageHistory.history.find(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
224
|
+
const currentAdjustment = currentEntry?.adjustment || createDefaultAdjustmentState(internalOptions.defaultAdjustmentState);
|
|
225
|
+
// Apply deltas with clamping
|
|
226
|
+
const newAdjustment = { ...currentAdjustment };
|
|
227
|
+
Object.keys(delta).forEach(key => {
|
|
228
|
+
const deltaValue = delta[key];
|
|
229
|
+
if (typeof deltaValue === 'number') {
|
|
230
|
+
const currentValue = newAdjustment[key];
|
|
231
|
+
newAdjustment[key] = Math.max(-100, Math.min(100, currentValue + deltaValue));
|
|
193
232
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
233
|
+
});
|
|
234
|
+
// Check if user is in the middle of history (not at latest state)
|
|
235
|
+
const currentEntryIndex = imageHistory.history.findIndex(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
236
|
+
const isInMiddleOfHistory = currentEntryIndex < imageHistory.history.length - 1;
|
|
237
|
+
let replaceFromTaskId;
|
|
238
|
+
console.log(`[useAdjustmentHistoryBatch] đ¯ Delta replace logic for image ${imageHistory.imageId}:`, {
|
|
239
|
+
currentEntryIndex,
|
|
240
|
+
historyLength: imageHistory.history.length,
|
|
241
|
+
isInMiddleOfHistory,
|
|
242
|
+
currentEntryId: currentEntry?.id
|
|
243
|
+
});
|
|
244
|
+
if (isInMiddleOfHistory) {
|
|
245
|
+
// If user is in middle of history, get the task ID of current position
|
|
246
|
+
replaceFromTaskId = currentEntry?.id;
|
|
247
|
+
console.log(`[useAdjustmentHistoryBatch] đ DELTA will REPLACE current position. replaceFromTaskId=${replaceFromTaskId}`);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
console.log(`[useAdjustmentHistoryBatch] â DELTA will ADD new entry (at end of history)`);
|
|
251
|
+
}
|
|
252
|
+
// Generate new task ID for backend (same as entry ID)
|
|
253
|
+
const taskId = generateTaskId();
|
|
254
|
+
// Create new entry with task ID
|
|
255
|
+
const newEntryId = taskId; // Use the same ID for both entry and task
|
|
256
|
+
const newEntry = {
|
|
257
|
+
id: newEntryId,
|
|
258
|
+
adjustment: newAdjustment
|
|
259
|
+
};
|
|
260
|
+
// Build new history
|
|
261
|
+
let newHistory;
|
|
262
|
+
if (isInMiddleOfHistory) {
|
|
263
|
+
// If in middle of history, truncate from current position and add new entry
|
|
264
|
+
newHistory = [...imageHistory.history.slice(0, currentEntryIndex + 1), newEntry];
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// If at end of history, just add new entry
|
|
268
|
+
newHistory = [...imageHistory.history, newEntry];
|
|
269
|
+
}
|
|
270
|
+
// Trim if needed
|
|
271
|
+
const maxSize = maxSizeRef.current;
|
|
272
|
+
const trimmedHistory = typeof maxSize === 'number' && newHistory.length > maxSize
|
|
273
|
+
? newHistory.slice(-maxSize)
|
|
274
|
+
: newHistory;
|
|
275
|
+
// Store operation for state update
|
|
276
|
+
operationsToApply.push({
|
|
277
|
+
imageId: imageHistory.imageId,
|
|
278
|
+
newEntryId,
|
|
279
|
+
newHistory: trimmedHistory
|
|
280
|
+
});
|
|
281
|
+
// Prepare backend operation (OUTSIDE state setter)
|
|
282
|
+
if (internalOptions.controller && internalOptions.firebaseUid) {
|
|
283
|
+
backendOperations.push({
|
|
284
|
+
imageId: imageHistory.imageId,
|
|
285
|
+
taskId,
|
|
286
|
+
adjustment: newAdjustment,
|
|
287
|
+
replaceFromTaskId
|
|
205
288
|
});
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const taskId = generateTaskId();
|
|
216
|
-
// Create new entry with task ID
|
|
217
|
-
const newEntryId = taskId; // Use the same ID for both entry and task
|
|
218
|
-
const newEntry = {
|
|
219
|
-
id: newEntryId,
|
|
220
|
-
adjustment: newAdjustment
|
|
221
|
-
};
|
|
222
|
-
// Prepare backend operation
|
|
223
|
-
if (internalOptions.controller && internalOptions.firebaseUid) {
|
|
224
|
-
backendOperations.push({
|
|
225
|
-
imageId: imageHistory.imageId,
|
|
226
|
-
taskId,
|
|
227
|
-
adjustment: newAdjustment,
|
|
228
|
-
replaceFromTaskId
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
// Build new history
|
|
232
|
-
let newHistory;
|
|
233
|
-
if (isInMiddleOfHistory) {
|
|
234
|
-
// If in middle of history, truncate from current position and add new entry
|
|
235
|
-
newHistory = [...imageHistory.history.slice(0, currentEntryIndex + 1), newEntry];
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
// If at end of history, just add new entry
|
|
239
|
-
newHistory = [...imageHistory.history, newEntry];
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
// Now update state with prepared operations (no backend preparation inside)
|
|
292
|
+
setImageHistories(prevHistories => {
|
|
293
|
+
return prevHistories.map(imageHistory => {
|
|
294
|
+
// Find operation for this image
|
|
295
|
+
const operation = operationsToApply.find(op => op.imageId === imageHistory.imageId);
|
|
296
|
+
if (!operation) {
|
|
297
|
+
return imageHistory; // No change for images not being adjusted
|
|
240
298
|
}
|
|
241
|
-
|
|
242
|
-
const maxSize = maxSizeRef.current;
|
|
243
|
-
const trimmedHistory = typeof maxSize === 'number' && newHistory.length > maxSize
|
|
244
|
-
? newHistory.slice(-maxSize)
|
|
245
|
-
: newHistory;
|
|
299
|
+
console.log(`[useAdjustmentHistoryBatch] đ Applying state update for delta on image ${imageHistory.imageId}`);
|
|
246
300
|
return {
|
|
247
301
|
...imageHistory,
|
|
248
|
-
history:
|
|
249
|
-
currentHistoryEntryId: newEntryId // Update current pointer
|
|
302
|
+
history: operation.newHistory,
|
|
303
|
+
currentHistoryEntryId: operation.newEntryId // Update current pointer
|
|
250
304
|
};
|
|
251
305
|
});
|
|
252
306
|
});
|
|
253
|
-
// Perform backend operations asynchronously
|
|
307
|
+
// Perform backend operations asynchronously (already prepared)
|
|
254
308
|
if (backendOperations.length > 0 && internalOptions.controller && internalOptions.firebaseUid) {
|
|
255
309
|
try {
|
|
310
|
+
console.log(`[useAdjustmentHistoryBatch] đ¤ Syncing ${backendOperations.length} adjustments to backend (createEditorConfig for each image)`);
|
|
256
311
|
const promises = backendOperations.map(async (operation) => {
|
|
312
|
+
console.log(`[useAdjustmentHistoryBatch] đ Calling createEditorConfig for image ${operation.imageId} with taskId ${operation.taskId}`);
|
|
257
313
|
await internalOptions.controller.createEditorConfig(internalOptions.firebaseUid, {
|
|
258
314
|
gallery_id: operation.imageId,
|
|
259
315
|
task_id: operation.taskId,
|
|
@@ -263,89 +319,118 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
263
319
|
});
|
|
264
320
|
await Promise.all(promises);
|
|
265
321
|
if (devWarningsRef.current) {
|
|
266
|
-
console.log(`[useAdjustmentHistoryBatch]
|
|
322
|
+
console.log(`[useAdjustmentHistoryBatch] â
Successfully synced ${backendOperations.length} adjustments to backend`);
|
|
267
323
|
}
|
|
268
324
|
}
|
|
269
325
|
catch (error) {
|
|
270
|
-
console.error('[useAdjustmentHistoryBatch] Failed to sync adjustments to backend:', error);
|
|
326
|
+
console.error('[useAdjustmentHistoryBatch] â Failed to sync adjustments to backend:', error);
|
|
271
327
|
}
|
|
272
328
|
}
|
|
273
|
-
}, [selectedIds, internalOptions]);
|
|
274
|
-
//
|
|
275
|
-
const
|
|
276
|
-
|
|
329
|
+
}, [selectedIds, internalOptions, imageHistories]);
|
|
330
|
+
// Shared function for applying adjustments (used by both reset and preset)
|
|
331
|
+
const applyAdjustmentToSelected = useCallback(async (adjustment, operationType, targetImageIds) => {
|
|
332
|
+
const idsToProcess = targetImageIds || selectedIds;
|
|
333
|
+
if (idsToProcess.length === 0) {
|
|
277
334
|
if (devWarningsRef.current) {
|
|
278
|
-
console.warn(
|
|
335
|
+
console.warn(`[useAdjustmentHistoryBatch] â ${operationType} called with no images to process`);
|
|
279
336
|
}
|
|
280
337
|
return;
|
|
281
338
|
}
|
|
282
|
-
|
|
339
|
+
console.log(`[useAdjustmentHistoryBatch] đ ${operationType.toUpperCase()} called for images:`, idsToProcess);
|
|
340
|
+
// Prepare backend operations BEFORE state update (outside setImageHistories)
|
|
283
341
|
const backendOperations = [];
|
|
342
|
+
// Process each image to prepare operations
|
|
343
|
+
const operationsToApply = [];
|
|
344
|
+
imageHistories.forEach(imageHistory => {
|
|
345
|
+
if (!idsToProcess.includes(imageHistory.imageId)) {
|
|
346
|
+
return; // Skip images not being processed
|
|
347
|
+
}
|
|
348
|
+
console.log(`[useAdjustmentHistoryBatch] đ Processing ${operationType} for image ${imageHistory.imageId}`);
|
|
349
|
+
// Get current entry for replace_from logic
|
|
350
|
+
const currentEntry = imageHistory.history.find(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
351
|
+
const currentEntryIndex = imageHistory.history.findIndex(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
352
|
+
const isInMiddleOfHistory = currentEntryIndex < imageHistory.history.length - 1;
|
|
353
|
+
let replaceFromTaskId;
|
|
354
|
+
console.log(`[useAdjustmentHistoryBatch] đ¯ ${operationType.toUpperCase()} replace logic for image ${imageHistory.imageId}:`, {
|
|
355
|
+
currentEntryIndex,
|
|
356
|
+
historyLength: imageHistory.history.length,
|
|
357
|
+
isInMiddleOfHistory,
|
|
358
|
+
currentEntryId: currentEntry?.id
|
|
359
|
+
});
|
|
360
|
+
if (isInMiddleOfHistory && currentEntry?.id) {
|
|
361
|
+
// Only use replace_from when truly in middle of history (not at latest)
|
|
362
|
+
replaceFromTaskId = currentEntry.id;
|
|
363
|
+
console.log(`[useAdjustmentHistoryBatch] đ ${operationType.toUpperCase()} will REPLACE current position. replaceFromTaskId=${replaceFromTaskId}`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
console.log(`[useAdjustmentHistoryBatch] â ${operationType.toUpperCase()} will ADD new entry (at end of history)`);
|
|
367
|
+
}
|
|
368
|
+
// Create new entry with adjustment
|
|
369
|
+
const newEntryId = generateEntryId();
|
|
370
|
+
const newEntry = {
|
|
371
|
+
id: newEntryId,
|
|
372
|
+
adjustment
|
|
373
|
+
};
|
|
374
|
+
// Build new history
|
|
375
|
+
let newHistory;
|
|
376
|
+
if (isInMiddleOfHistory) {
|
|
377
|
+
// If in middle of history, truncate from current position and add new entry
|
|
378
|
+
newHistory = [...imageHistory.history.slice(0, currentEntryIndex + 1), newEntry];
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// If at end of history, just add new entry
|
|
382
|
+
newHistory = [...imageHistory.history, newEntry];
|
|
383
|
+
}
|
|
384
|
+
// Trim if needed
|
|
385
|
+
const maxSize = maxSizeRef.current;
|
|
386
|
+
const trimmedHistory = typeof maxSize === 'number' && newHistory.length > maxSize
|
|
387
|
+
? newHistory.slice(-maxSize)
|
|
388
|
+
: newHistory;
|
|
389
|
+
// Store operation for state update
|
|
390
|
+
operationsToApply.push({
|
|
391
|
+
imageId: imageHistory.imageId,
|
|
392
|
+
newEntryId,
|
|
393
|
+
newHistory: trimmedHistory
|
|
394
|
+
});
|
|
395
|
+
// Prepare backend operation (OUTSIDE state setter)
|
|
396
|
+
if (internalOptions.controller && internalOptions.firebaseUid) {
|
|
397
|
+
console.log(`[useAdjustmentHistoryBatch] â
Adding ${operationType.toUpperCase()} backend operation for image ${imageHistory.imageId} with taskId ${newEntryId}`, {
|
|
398
|
+
replaceFromTaskId,
|
|
399
|
+
willReplace: !!replaceFromTaskId
|
|
400
|
+
});
|
|
401
|
+
backendOperations.push({
|
|
402
|
+
imageId: imageHistory.imageId,
|
|
403
|
+
taskId: newEntryId,
|
|
404
|
+
adjustment,
|
|
405
|
+
replaceFromTaskId
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
// Now update state with prepared operations (no backend preparation inside)
|
|
284
410
|
setImageHistories(prevHistories => {
|
|
285
411
|
return prevHistories.map(imageHistory => {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const currentEntry = imageHistory.history.find(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
291
|
-
const currentAdjustment = currentEntry?.adjustment || createDefaultAdjustmentState(internalOptions.defaultAdjustmentState);
|
|
292
|
-
// Apply preset values with clamping (same as adjustSelected logic)
|
|
293
|
-
const newAdjustment = { ...presetAdjustments };
|
|
294
|
-
Object.keys(newAdjustment).forEach(key => {
|
|
295
|
-
const presetValue = newAdjustment[key];
|
|
296
|
-
newAdjustment[key] = Math.max(-100, Math.min(100, presetValue));
|
|
297
|
-
});
|
|
298
|
-
// Check if user is in the middle of history (not at latest state)
|
|
299
|
-
const currentEntryIndex = imageHistory.history.findIndex(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
300
|
-
const isInMiddleOfHistory = currentEntryIndex < imageHistory.history.length - 1;
|
|
301
|
-
let replaceFromTaskId;
|
|
302
|
-
if (isInMiddleOfHistory) {
|
|
303
|
-
// If user is in middle of history, get the task ID of current position
|
|
304
|
-
replaceFromTaskId = currentEntry?.id;
|
|
305
|
-
}
|
|
306
|
-
// Generate new task ID for backend (same as entry ID)
|
|
307
|
-
const taskId = generateTaskId();
|
|
308
|
-
// Create new entry with task ID
|
|
309
|
-
const newEntryId = taskId; // Use the same ID for both entry and task
|
|
310
|
-
const newEntry = {
|
|
311
|
-
id: newEntryId,
|
|
312
|
-
adjustment: newAdjustment
|
|
313
|
-
};
|
|
314
|
-
// Prepare backend operation
|
|
315
|
-
if (internalOptions.controller && internalOptions.firebaseUid) {
|
|
316
|
-
backendOperations.push({
|
|
317
|
-
imageId: imageHistory.imageId,
|
|
318
|
-
taskId,
|
|
319
|
-
adjustment: newAdjustment,
|
|
320
|
-
replaceFromTaskId
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
// Build new history
|
|
324
|
-
let newHistory;
|
|
325
|
-
if (isInMiddleOfHistory) {
|
|
326
|
-
// If in middle of history, truncate from current position and add new entry
|
|
327
|
-
newHistory = [...imageHistory.history.slice(0, currentEntryIndex + 1), newEntry];
|
|
412
|
+
// Find operation for this image
|
|
413
|
+
const operation = operationsToApply.find(op => op.imageId === imageHistory.imageId);
|
|
414
|
+
if (!operation) {
|
|
415
|
+
return imageHistory; // No change for images not being processed
|
|
328
416
|
}
|
|
329
|
-
|
|
330
|
-
// If at end of history, just add new entry
|
|
331
|
-
newHistory = [...imageHistory.history, newEntry];
|
|
332
|
-
}
|
|
333
|
-
// Trim if needed
|
|
334
|
-
const maxSize = maxSizeRef.current;
|
|
335
|
-
const trimmedHistory = typeof maxSize === 'number' && newHistory.length > maxSize
|
|
336
|
-
? newHistory.slice(-maxSize)
|
|
337
|
-
: newHistory;
|
|
417
|
+
console.log(`[useAdjustmentHistoryBatch] đ Applying state update for ${operationType} on image ${imageHistory.imageId}`);
|
|
338
418
|
return {
|
|
339
419
|
...imageHistory,
|
|
340
|
-
history:
|
|
341
|
-
currentHistoryEntryId: newEntryId // Update current pointer
|
|
420
|
+
history: operation.newHistory,
|
|
421
|
+
currentHistoryEntryId: operation.newEntryId // Update current pointer
|
|
342
422
|
};
|
|
343
423
|
});
|
|
344
424
|
});
|
|
345
|
-
// Perform backend operations asynchronously
|
|
425
|
+
// Perform backend operations asynchronously (already prepared)
|
|
346
426
|
if (backendOperations.length > 0 && internalOptions.controller && internalOptions.firebaseUid) {
|
|
347
427
|
try {
|
|
428
|
+
console.log(`[useAdjustmentHistoryBatch] đ¤ Syncing ${backendOperations.length} ${operationType} operations to backend (createEditorConfig for each image)`);
|
|
348
429
|
const promises = backendOperations.map(async (operation) => {
|
|
430
|
+
console.log(`[useAdjustmentHistoryBatch] đ Calling createEditorConfig for ${operationType} on image ${operation.imageId} with taskId ${operation.taskId}`, {
|
|
431
|
+
replaceFrom: operation.replaceFromTaskId,
|
|
432
|
+
hasReplaceFrom: !!operation.replaceFromTaskId
|
|
433
|
+
});
|
|
349
434
|
await internalOptions.controller.createEditorConfig(internalOptions.firebaseUid, {
|
|
350
435
|
gallery_id: operation.imageId,
|
|
351
436
|
task_id: operation.taskId,
|
|
@@ -355,14 +440,48 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
355
440
|
});
|
|
356
441
|
await Promise.all(promises);
|
|
357
442
|
if (devWarningsRef.current) {
|
|
358
|
-
console.log(`[useAdjustmentHistoryBatch]
|
|
443
|
+
console.log(`[useAdjustmentHistoryBatch] â
Successfully synced ${backendOperations.length} ${operationType} operations to backend`);
|
|
359
444
|
}
|
|
360
445
|
}
|
|
361
446
|
catch (error) {
|
|
362
|
-
console.error(
|
|
447
|
+
console.error(`[useAdjustmentHistoryBatch] â Failed to sync ${operationType} to backend:`, error);
|
|
363
448
|
}
|
|
364
449
|
}
|
|
365
|
-
|
|
450
|
+
console.log(`[useAdjustmentHistoryBatch] đ ${operationType.toUpperCase()} backend operations prepared: ${backendOperations.length}`);
|
|
451
|
+
}, [selectedIds, internalOptions, imageHistories]);
|
|
452
|
+
// Apply preset values directly to selected images - now uses shared logic
|
|
453
|
+
const adjustSelectedWithPreset = useCallback(async (presetAdjustments) => {
|
|
454
|
+
console.log('[useAdjustmentHistoryBatch] đ¨ adjustSelectedWithPreset called with:', {
|
|
455
|
+
selectedIds,
|
|
456
|
+
selectedCount: selectedIds.length,
|
|
457
|
+
presetAdjustments,
|
|
458
|
+
hasController: !!internalOptions.controller,
|
|
459
|
+
hasFirebaseUid: !!internalOptions.firebaseUid,
|
|
460
|
+
imageHistoriesCount: imageHistories.length,
|
|
461
|
+
imageHistoriesIds: imageHistories.map(h => h.imageId)
|
|
462
|
+
});
|
|
463
|
+
console.log('[useAdjustmentHistoryBatch] đ PRESET: Current imageHistories state at start:', imageHistories.map(h => ({
|
|
464
|
+
imageId: h.imageId,
|
|
465
|
+
currentHistoryEntryId: h.currentHistoryEntryId,
|
|
466
|
+
historyLength: h.history.length,
|
|
467
|
+
historyIds: h.history.map(entry => entry.id)
|
|
468
|
+
})));
|
|
469
|
+
if (selectedIds.length === 0) {
|
|
470
|
+
if (devWarningsRef.current) {
|
|
471
|
+
console.warn('[useAdjustmentHistoryBatch] â adjustSelectedWithPreset called with no selection');
|
|
472
|
+
}
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
// Apply preset values with clamping
|
|
476
|
+
const clampedPreset = { ...presetAdjustments };
|
|
477
|
+
Object.keys(clampedPreset).forEach(key => {
|
|
478
|
+
const presetValue = clampedPreset[key];
|
|
479
|
+
clampedPreset[key] = Math.max(-100, Math.min(100, presetValue));
|
|
480
|
+
});
|
|
481
|
+
console.log('[useAdjustmentHistoryBatch] đ¨ Using SHARED logic for preset (same as reset)');
|
|
482
|
+
// Use the same logic as reset, just with preset values instead of default values
|
|
483
|
+
await applyAdjustmentToSelected(clampedPreset, 'preset');
|
|
484
|
+
}, [selectedIds, internalOptions, imageHistories, applyAdjustmentToSelected]);
|
|
366
485
|
// Set specific adjustment states for specified images (removed since not needed)
|
|
367
486
|
// Undo last changes to selected images - entry-based history version with backend sync
|
|
368
487
|
const undo = useCallback(async () => {
|
|
@@ -372,53 +491,102 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
372
491
|
}
|
|
373
492
|
return;
|
|
374
493
|
}
|
|
494
|
+
console.log('[useAdjustmentHistoryBatch] đ UNDO: Before undo, current imageHistories state:', imageHistories.map(h => ({
|
|
495
|
+
imageId: h.imageId,
|
|
496
|
+
currentHistoryEntryId: h.currentHistoryEntryId,
|
|
497
|
+
historyLength: h.history.length,
|
|
498
|
+
historyIds: h.history.map(entry => entry.id)
|
|
499
|
+
})));
|
|
500
|
+
// Prepare backend operations BEFORE state update (outside setImageHistories)
|
|
375
501
|
let anyChanges = false;
|
|
376
502
|
const backendOperations = [];
|
|
503
|
+
// Process each image to prepare operations
|
|
504
|
+
const operationsToApply = [];
|
|
505
|
+
imageHistories.forEach(imageHistory => {
|
|
506
|
+
if (!selectedIds.includes(imageHistory.imageId)) {
|
|
507
|
+
return; // Skip images not selected
|
|
508
|
+
}
|
|
509
|
+
console.log(`[useAdjustmentHistoryBatch] đ UNDO: Processing image ${imageHistory.imageId} with history:`, {
|
|
510
|
+
currentHistoryEntryId: imageHistory.currentHistoryEntryId,
|
|
511
|
+
historyLength: imageHistory.history.length,
|
|
512
|
+
historyIds: imageHistory.history.map(entry => entry.id)
|
|
513
|
+
});
|
|
514
|
+
// Find current entry index
|
|
515
|
+
const currentEntryIndex = imageHistory.history.findIndex(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
516
|
+
if (currentEntryIndex <= 0) {
|
|
517
|
+
console.log(`[useAdjustmentHistoryBatch] đ UNDO: Cannot undo image ${imageHistory.imageId} - at first entry or not found`);
|
|
518
|
+
return; // Cannot undo if at first entry or entry not found
|
|
519
|
+
}
|
|
520
|
+
// Move to previous entry
|
|
521
|
+
const previousEntry = imageHistory.history[currentEntryIndex - 1];
|
|
522
|
+
anyChanges = true;
|
|
523
|
+
console.log(`[useAdjustmentHistoryBatch] đ UNDO: Moving image ${imageHistory.imageId} from index ${currentEntryIndex} to ${currentEntryIndex - 1}`, {
|
|
524
|
+
fromId: imageHistory.currentHistoryEntryId,
|
|
525
|
+
toId: previousEntry.id,
|
|
526
|
+
preservedHistoryLength: imageHistory.history.length
|
|
527
|
+
});
|
|
528
|
+
// Store operation for state update
|
|
529
|
+
operationsToApply.push({
|
|
530
|
+
imageId: imageHistory.imageId,
|
|
531
|
+
newCurrentHistoryEntryId: previousEntry.id
|
|
532
|
+
});
|
|
533
|
+
// Prepare backend sync operation (OUTSIDE state setter)
|
|
534
|
+
if (previousEntry.id && internalOptions.controller && internalOptions.firebaseUid) {
|
|
535
|
+
backendOperations.push({
|
|
536
|
+
imageId: imageHistory.imageId,
|
|
537
|
+
taskId: previousEntry.id
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
// Now update state with prepared operations (no backend preparation inside)
|
|
377
542
|
setImageHistories(prevHistories => {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const currentEntryIndex = imageHistory.history.findIndex(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
384
|
-
if (currentEntryIndex <= 0) {
|
|
385
|
-
return imageHistory; // Cannot undo if at first entry or entry not found
|
|
386
|
-
}
|
|
387
|
-
// Move to previous entry
|
|
388
|
-
const previousEntry = imageHistory.history[currentEntryIndex - 1];
|
|
389
|
-
anyChanges = true;
|
|
390
|
-
// Prepare backend sync operation
|
|
391
|
-
if (previousEntry.id && internalOptions.controller && internalOptions.firebaseUid) {
|
|
392
|
-
backendOperations.push({
|
|
393
|
-
imageId: imageHistory.imageId,
|
|
394
|
-
taskId: previousEntry.id
|
|
395
|
-
});
|
|
543
|
+
const result = prevHistories.map(imageHistory => {
|
|
544
|
+
// Find operation for this image
|
|
545
|
+
const operation = operationsToApply.find(op => op.imageId === imageHistory.imageId);
|
|
546
|
+
if (!operation) {
|
|
547
|
+
return imageHistory; // No change for images not selected
|
|
396
548
|
}
|
|
397
|
-
|
|
549
|
+
console.log(`[useAdjustmentHistoryBatch] đ Applying state update for undo on image ${imageHistory.imageId}`);
|
|
550
|
+
const updatedHistory = {
|
|
398
551
|
...imageHistory,
|
|
399
|
-
currentHistoryEntryId:
|
|
552
|
+
currentHistoryEntryId: operation.newCurrentHistoryEntryId
|
|
400
553
|
};
|
|
554
|
+
console.log(`[useAdjustmentHistoryBatch] đ UNDO: Updated image ${imageHistory.imageId} history:`, {
|
|
555
|
+
currentHistoryEntryId: updatedHistory.currentHistoryEntryId,
|
|
556
|
+
historyLength: updatedHistory.history.length,
|
|
557
|
+
historyIds: updatedHistory.history.map(entry => entry.id)
|
|
558
|
+
});
|
|
559
|
+
return updatedHistory;
|
|
401
560
|
});
|
|
561
|
+
console.log('[useAdjustmentHistoryBatch] đ UNDO: After undo, final imageHistories state:', result.map(h => ({
|
|
562
|
+
imageId: h.imageId,
|
|
563
|
+
currentHistoryEntryId: h.currentHistoryEntryId,
|
|
564
|
+
historyLength: h.history.length,
|
|
565
|
+
historyIds: h.history.map(entry => entry.id)
|
|
566
|
+
})));
|
|
567
|
+
return result;
|
|
402
568
|
});
|
|
403
|
-
// Sync with backend
|
|
569
|
+
// Sync with backend (already prepared)
|
|
404
570
|
if (backendOperations.length > 0 && internalOptions.controller && internalOptions.firebaseUid) {
|
|
405
571
|
try {
|
|
572
|
+
console.log(`[useAdjustmentHistoryBatch] âĒ Syncing ${backendOperations.length} undo operations to backend (setHistoryIndex for each image)`);
|
|
406
573
|
const promises = backendOperations.map(async (operation) => {
|
|
574
|
+
console.log(`[useAdjustmentHistoryBatch] đ Calling setHistoryIndex for undo on image ${operation.imageId} to taskId ${operation.taskId}`);
|
|
407
575
|
await internalOptions.controller.setHistoryIndex(internalOptions.firebaseUid, operation.imageId, operation.taskId);
|
|
408
576
|
});
|
|
409
577
|
await Promise.all(promises);
|
|
410
578
|
if (devWarningsRef.current) {
|
|
411
|
-
console.log(`[useAdjustmentHistoryBatch]
|
|
579
|
+
console.log(`[useAdjustmentHistoryBatch] â
Successfully synced ${backendOperations.length} undo operations to backend`);
|
|
412
580
|
}
|
|
413
581
|
}
|
|
414
582
|
catch (error) {
|
|
415
|
-
console.error('[useAdjustmentHistoryBatch] Failed to sync undo to backend:', error);
|
|
583
|
+
console.error('[useAdjustmentHistoryBatch] â Failed to sync undo to backend:', error);
|
|
416
584
|
}
|
|
417
585
|
}
|
|
418
586
|
if (!anyChanges && devWarningsRef.current) {
|
|
419
587
|
console.warn('[useAdjustmentHistoryBatch] Undo skipped - no changes to undo for selected images');
|
|
420
588
|
}
|
|
421
|
-
}, [selectedIds, internalOptions]);
|
|
589
|
+
}, [selectedIds, internalOptions, imageHistories]);
|
|
422
590
|
// Redo next changes to selected images - entry-based history version with backend sync
|
|
423
591
|
const redo = useCallback(async () => {
|
|
424
592
|
if (selectedIds.length === 0) {
|
|
@@ -427,53 +595,70 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
427
595
|
}
|
|
428
596
|
return;
|
|
429
597
|
}
|
|
598
|
+
// Step 1: Prepare redo operations from current state
|
|
430
599
|
let anyChanges = false;
|
|
600
|
+
const operationsToApply = [];
|
|
431
601
|
const backendOperations = [];
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
backendOperations.push({
|
|
448
|
-
imageId: imageHistory.imageId,
|
|
449
|
-
taskId: nextEntry.id
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
return {
|
|
453
|
-
...imageHistory,
|
|
454
|
-
currentHistoryEntryId: nextEntry.id
|
|
455
|
-
};
|
|
602
|
+
imageHistories.forEach(imageHistory => {
|
|
603
|
+
if (!selectedIds.includes(imageHistory.imageId)) {
|
|
604
|
+
return; // Skip unselected images
|
|
605
|
+
}
|
|
606
|
+
// Find current entry index
|
|
607
|
+
const currentEntryIndex = imageHistory.history.findIndex(entry => entry.id === imageHistory.currentHistoryEntryId);
|
|
608
|
+
if (currentEntryIndex >= imageHistory.history.length - 1 || currentEntryIndex === -1) {
|
|
609
|
+
return; // Cannot redo if at last entry or entry not found
|
|
610
|
+
}
|
|
611
|
+
// Move to next entry
|
|
612
|
+
const nextEntry = imageHistory.history[currentEntryIndex + 1];
|
|
613
|
+
anyChanges = true;
|
|
614
|
+
operationsToApply.push({
|
|
615
|
+
imageId: imageHistory.imageId,
|
|
616
|
+
nextEntryId: nextEntry.id
|
|
456
617
|
});
|
|
618
|
+
// Prepare backend sync operation
|
|
619
|
+
if (nextEntry.id && internalOptions.controller && internalOptions.firebaseUid) {
|
|
620
|
+
backendOperations.push({
|
|
621
|
+
imageId: imageHistory.imageId,
|
|
622
|
+
taskId: nextEntry.id
|
|
623
|
+
});
|
|
624
|
+
}
|
|
457
625
|
});
|
|
458
|
-
//
|
|
626
|
+
// Step 2: Apply state updates cleanly
|
|
627
|
+
if (operationsToApply.length > 0) {
|
|
628
|
+
setImageHistories(prevHistories => {
|
|
629
|
+
return prevHistories.map(imageHistory => {
|
|
630
|
+
const operation = operationsToApply.find(op => op.imageId === imageHistory.imageId);
|
|
631
|
+
if (!operation) {
|
|
632
|
+
return imageHistory; // No change for this image
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
...imageHistory,
|
|
636
|
+
currentHistoryEntryId: operation.nextEntryId
|
|
637
|
+
};
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
// Step 3: Sync with backend
|
|
459
642
|
if (backendOperations.length > 0 && internalOptions.controller && internalOptions.firebaseUid) {
|
|
460
643
|
try {
|
|
644
|
+
console.log(`[useAdjustmentHistoryBatch] ⊠Syncing ${backendOperations.length} redo operations to backend (setHistoryIndex for each image)`);
|
|
461
645
|
const promises = backendOperations.map(async (operation) => {
|
|
646
|
+
console.log(`[useAdjustmentHistoryBatch] đ Calling setHistoryIndex for redo on image ${operation.imageId} to taskId ${operation.taskId}`);
|
|
462
647
|
await internalOptions.controller.setHistoryIndex(internalOptions.firebaseUid, operation.imageId, operation.taskId);
|
|
463
648
|
});
|
|
464
649
|
await Promise.all(promises);
|
|
465
650
|
if (devWarningsRef.current) {
|
|
466
|
-
console.log(`[useAdjustmentHistoryBatch]
|
|
651
|
+
console.log(`[useAdjustmentHistoryBatch] â
Successfully synced ${backendOperations.length} redo operations to backend`);
|
|
467
652
|
}
|
|
468
653
|
}
|
|
469
654
|
catch (error) {
|
|
470
|
-
console.error('[useAdjustmentHistoryBatch] Failed to sync redo to backend:', error);
|
|
655
|
+
console.error('[useAdjustmentHistoryBatch] â Failed to sync redo to backend:', error);
|
|
471
656
|
}
|
|
472
657
|
}
|
|
473
658
|
if (!anyChanges && devWarningsRef.current) {
|
|
474
659
|
console.warn('[useAdjustmentHistoryBatch] Redo skipped - no changes to redo for selected images');
|
|
475
660
|
}
|
|
476
|
-
}, [selectedIds, internalOptions]);
|
|
661
|
+
}, [selectedIds, internalOptions, imageHistories]);
|
|
477
662
|
// Check if any selected image can be undone
|
|
478
663
|
const canUndoSelected = useCallback(() => {
|
|
479
664
|
return selectedIds.some(imageId => {
|
|
@@ -494,38 +679,16 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
494
679
|
return currentEntryIndex >= 0 && currentEntryIndex < imageHistory.history.length - 1;
|
|
495
680
|
});
|
|
496
681
|
}, [selectedIds, imageHistories]);
|
|
497
|
-
// Reset selected images to default state -
|
|
498
|
-
const reset = useCallback((imageIds) => {
|
|
682
|
+
// Reset selected images to default state - now uses shared logic
|
|
683
|
+
const reset = useCallback(async (imageIds) => {
|
|
499
684
|
const idsToReset = imageIds || selectedIds;
|
|
500
|
-
|
|
501
|
-
|
|
685
|
+
console.log('[useAdjustmentHistoryBatch] đ reset called for images:', idsToReset);
|
|
686
|
+
// Create default state
|
|
502
687
|
const defaultState = createDefaultAdjustmentState(internalOptions.defaultAdjustmentState);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
// Create new entry with default state
|
|
509
|
-
const newEntryId = generateEntryId();
|
|
510
|
-
const newEntry = {
|
|
511
|
-
id: newEntryId,
|
|
512
|
-
adjustment: defaultState
|
|
513
|
-
};
|
|
514
|
-
// Add to this image's history
|
|
515
|
-
const newHistory = [...imageHistory.history, newEntry];
|
|
516
|
-
// Trim if needed
|
|
517
|
-
const maxSize = maxSizeRef.current;
|
|
518
|
-
const trimmedHistory = typeof maxSize === 'number' && newHistory.length > maxSize
|
|
519
|
-
? newHistory.slice(-maxSize)
|
|
520
|
-
: newHistory;
|
|
521
|
-
return {
|
|
522
|
-
...imageHistory,
|
|
523
|
-
history: trimmedHistory,
|
|
524
|
-
currentHistoryEntryId: newEntryId
|
|
525
|
-
};
|
|
526
|
-
});
|
|
527
|
-
});
|
|
528
|
-
}, [selectedIds, internalOptions.defaultAdjustmentState]);
|
|
688
|
+
console.log('[useAdjustmentHistoryBatch] đ Using SHARED logic for reset (same as preset)');
|
|
689
|
+
// Use the same logic as preset, just with default values instead of preset values
|
|
690
|
+
await applyAdjustmentToSelected(defaultState, 'reset', idsToReset);
|
|
691
|
+
}, [selectedIds, internalOptions.defaultAdjustmentState, applyAdjustmentToSelected]);
|
|
529
692
|
// Selection management with initial adjustments - single state update
|
|
530
693
|
const setSelection = useCallback((configs) => {
|
|
531
694
|
const imageIds = configs.map(config => config.imageId);
|
|
@@ -859,6 +1022,114 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
859
1022
|
});
|
|
860
1023
|
}
|
|
861
1024
|
}, []);
|
|
1025
|
+
// Sync gallery updates - check for updated galleries and refresh their history
|
|
1026
|
+
const syncGalleryUpdates = useCallback(async () => {
|
|
1027
|
+
if (!internalOptions.controller || !internalOptions.firebaseUid || !internalOptions.eventId) {
|
|
1028
|
+
if (devWarningsRef.current) {
|
|
1029
|
+
console.warn('[useAdjustmentHistoryBatch] syncGalleryUpdates: Missing required options (controller, firebaseUid, or eventId)');
|
|
1030
|
+
}
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
try {
|
|
1034
|
+
// Step 1: Check for gallery updates since last timestamp
|
|
1035
|
+
console.log(`[useAdjustmentHistoryBatch] đ Checking for gallery updates since timestamp: ${lastUpdateTimestamp}`);
|
|
1036
|
+
const updateResponse = await internalOptions.controller.getGalleryUpdateTimestamp(internalOptions.firebaseUid, internalOptions.eventId, lastUpdateTimestamp);
|
|
1037
|
+
if (!updateResponse.gallery || updateResponse.gallery.length === 0) {
|
|
1038
|
+
if (devWarningsRef.current) {
|
|
1039
|
+
console.log('[useAdjustmentHistoryBatch] â
No gallery updates found');
|
|
1040
|
+
}
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
console.log(`[useAdjustmentHistoryBatch] đĨ Found ${updateResponse.gallery.length} updated galleries:`, updateResponse.gallery);
|
|
1044
|
+
// Step 2: Fetch history for each updated gallery
|
|
1045
|
+
const historyPromises = updateResponse.gallery.map(async (galleryId) => {
|
|
1046
|
+
try {
|
|
1047
|
+
console.log(`[useAdjustmentHistoryBatch] đ Fetching history for gallery: ${galleryId}`);
|
|
1048
|
+
const historyResponse = await internalOptions.controller.getEditorHistory(internalOptions.firebaseUid, galleryId);
|
|
1049
|
+
return {
|
|
1050
|
+
imageId: galleryId,
|
|
1051
|
+
currentTaskId: historyResponse.current_task_id,
|
|
1052
|
+
backendHistory: historyResponse.history || [],
|
|
1053
|
+
success: true
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
console.error(`[useAdjustmentHistoryBatch] â Failed to fetch history for gallery ${galleryId}:`, error);
|
|
1058
|
+
return {
|
|
1059
|
+
imageId: galleryId,
|
|
1060
|
+
currentTaskId: '',
|
|
1061
|
+
backendHistory: [],
|
|
1062
|
+
success: false
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
const historyResults = await Promise.all(historyPromises);
|
|
1067
|
+
const successfulUpdates = historyResults.filter(result => result.success && result.backendHistory.length > 0);
|
|
1068
|
+
if (successfulUpdates.length === 0) {
|
|
1069
|
+
if (devWarningsRef.current) {
|
|
1070
|
+
console.log('[useAdjustmentHistoryBatch] âšī¸ No valid history updates to apply');
|
|
1071
|
+
}
|
|
1072
|
+
// Update timestamp even if no valid updates
|
|
1073
|
+
setLastUpdateTimestamp(Date.now());
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
// Step 3: Prepare history updates from backend data
|
|
1077
|
+
const historyUpdates = [];
|
|
1078
|
+
successfulUpdates.forEach(result => {
|
|
1079
|
+
const { imageId, currentTaskId, backendHistory } = result;
|
|
1080
|
+
if (backendHistory.length > 0) {
|
|
1081
|
+
// Convert backend history to local history entries
|
|
1082
|
+
const historyEntries = backendHistory.map((entry) => ({
|
|
1083
|
+
id: entry.task_id, // Use backend task_id as our entry id
|
|
1084
|
+
adjustment: mapColorAdjustmentToAdjustmentState ?
|
|
1085
|
+
mapColorAdjustmentToAdjustmentState(entry.editor_config.color_adjustment) :
|
|
1086
|
+
createDefaultAdjustmentState(internalOptions.defaultAdjustmentState)
|
|
1087
|
+
}));
|
|
1088
|
+
historyUpdates.push({
|
|
1089
|
+
imageId,
|
|
1090
|
+
newHistory: historyEntries,
|
|
1091
|
+
newCurrentEntryId: currentTaskId // Use current_task_id as current history entry
|
|
1092
|
+
});
|
|
1093
|
+
console.log(`[useAdjustmentHistoryBatch] đ Prepared history update for gallery ${imageId}:`, {
|
|
1094
|
+
historyEntries: historyEntries.length,
|
|
1095
|
+
currentEntryId: currentTaskId
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
// Step 4: Apply history updates cleanly to state
|
|
1100
|
+
if (historyUpdates.length > 0) {
|
|
1101
|
+
setImageHistories(prevHistories => {
|
|
1102
|
+
return prevHistories.map(imageHistory => {
|
|
1103
|
+
const update = historyUpdates.find(u => u.imageId === imageHistory.imageId);
|
|
1104
|
+
if (!update) {
|
|
1105
|
+
return imageHistory; // No update for this image
|
|
1106
|
+
}
|
|
1107
|
+
// Replace entire history with updated backend data
|
|
1108
|
+
return {
|
|
1109
|
+
...imageHistory,
|
|
1110
|
+
history: update.newHistory,
|
|
1111
|
+
currentHistoryEntryId: update.newCurrentEntryId
|
|
1112
|
+
};
|
|
1113
|
+
});
|
|
1114
|
+
});
|
|
1115
|
+
console.log(`[useAdjustmentHistoryBatch] â
Successfully updated history for ${historyUpdates.length} galleries`);
|
|
1116
|
+
}
|
|
1117
|
+
// Step 5: Update timestamp to mark successful sync
|
|
1118
|
+
setLastUpdateTimestamp(Date.now());
|
|
1119
|
+
if (devWarningsRef.current) {
|
|
1120
|
+
console.log('[useAdjustmentHistoryBatch] đ Gallery sync completed', {
|
|
1121
|
+
totalChecked: updateResponse.gallery.length,
|
|
1122
|
+
successfulUpdates: successfulUpdates.length,
|
|
1123
|
+
appliedUpdates: historyUpdates.length,
|
|
1124
|
+
newTimestamp: Date.now()
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
catch (error) {
|
|
1129
|
+
console.error('[useAdjustmentHistoryBatch] â Failed to sync gallery updates:', error);
|
|
1130
|
+
// Don't update timestamp on error, so we can retry
|
|
1131
|
+
}
|
|
1132
|
+
}, [internalOptions, lastUpdateTimestamp, mapColorAdjustmentToAdjustmentState]);
|
|
862
1133
|
// Configuration actions
|
|
863
1134
|
const setMaxSize = useCallback((size) => {
|
|
864
1135
|
maxSizeRef.current = size;
|
|
@@ -885,6 +1156,7 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
885
1156
|
reset,
|
|
886
1157
|
setSelection,
|
|
887
1158
|
syncAdjustment,
|
|
1159
|
+
syncGalleryUpdates,
|
|
888
1160
|
toggleSelection,
|
|
889
1161
|
selectAll,
|
|
890
1162
|
clearSelection,
|
|
@@ -894,7 +1166,7 @@ export function useAdjustmentHistoryBatch(options = {}) {
|
|
|
894
1166
|
syncBatch
|
|
895
1167
|
}), [
|
|
896
1168
|
adjustSelected, adjustSelectedWithPreset, undo, redo, reset,
|
|
897
|
-
setSelection, syncAdjustment, toggleSelection, selectAll, clearSelection,
|
|
1169
|
+
setSelection, syncAdjustment, syncGalleryUpdates, toggleSelection, selectAll, clearSelection,
|
|
898
1170
|
jumpToIndex, clearHistory, getCurrentBatch, syncBatch
|
|
899
1171
|
]);
|
|
900
1172
|
// Config object - stabilized with useMemo
|