@yogiswara/honcho-editor-ui 3.6.6 → 3.6.8

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.
@@ -75,6 +75,7 @@ export default function HImageEditorSkeleton(props) {
75
75
  backgroundColor: 'transparent',
76
76
  }, children: _jsxs(Paper, { sx: { backgroundColor: colors.onBackground, borderRadius: "0px" }, elevation: 3, children: [_jsx(Stack, { direction: "row", justifyContent: "center", alignItems: "center", sx: {
77
77
  py: "12px",
78
+ pt: "110px",
78
79
  // px: "143px",
79
80
  gap: "10px"
80
81
  }, children: _jsx(Skeleton, { variant: "rounded", width: "156px", height: 18, sx: { bgcolor: alpha(colors.background, 0.1), } }) }), _jsx(Stack, { direction: "row", justifyContent: "center", alignItems: "center", sx: {
@@ -32,43 +32,26 @@ export interface CopyCheckboxes {
32
32
  sharpness: boolean;
33
33
  };
34
34
  }
35
- /**
36
- * Helper function to create selective adjustments based on checkboxes
37
- */
38
- /**
39
- * Hook for bulk photo editing with multi-image backend synchronization.
40
- *
41
- * **Multi-Image Backend Sync:**
42
- * - Each selected image maintains its own history and backend state
43
- * - History operations (undo/redo) trigger separate controller.setHistoryIndex() calls for each selected image
44
- * - Preset application triggers separate controller.createEditorConfig() calls for each selected image
45
- * - Adjustment changes trigger separate controller.createEditorConfig() calls for each selected image
46
- *
47
- * **Key Features:**
48
- * - Individual history tracking per image with backend persistence
49
- * - Bulk operations that respect per-image differences
50
- * - Automatic backend synchronization for all state changes
51
- * - Comprehensive logging of multi-image operations
52
- */
53
- export declare function useHonchoEditorBulk(controller: Controller, eventID: string, firebaseUid: string): {
35
+ export interface UseHonchoEditorBulkReturn {
54
36
  imageData: PhotoData[];
55
37
  isLoading: boolean;
56
38
  isLoadingMore: boolean;
57
39
  error: string | null;
58
40
  selectedIds: string[];
59
41
  hasMore: boolean;
42
+ gallerySetupData: GallerySetup[];
60
43
  handleBackCallbackBulk: () => void;
44
+ handleToggleImageSelection: (imageId: string) => Promise<void>;
45
+ handleLoadMore: () => Promise<void>;
46
+ handleRefresh: () => Promise<void>;
47
+ clearSelection: () => void;
48
+ selectAllImages: () => void;
61
49
  presets: Preset[];
62
50
  selectedBulkPreset: string;
63
51
  activePreset: Preset | null;
64
52
  presetSummary: string;
65
53
  handleSelectBulkPreset: (presetId: string) => void;
66
- clearSelection: () => void;
67
- selectAllImages: () => void;
68
- presetActions: import("../usePreset").PresetActions;
69
- handleToggleImageSelection: (imageId: string) => Promise<void>;
70
- handleLoadMore: () => Promise<void>;
71
- handleRefresh: () => Promise<void>;
54
+ presetActions: any;
72
55
  handleBulkTempDecreaseMax: () => void;
73
56
  handleBulkTempDecrease: () => void;
74
57
  handleBulkTempIncrease: () => void;
@@ -135,5 +118,41 @@ export declare function useHonchoEditorBulk(controller: Controller, eventID: str
135
118
  adjustSelected: (delta: Partial<AdjustmentState>) => void;
136
119
  adjustSelectedWithPreset: (presetAdjustments: AdjustmentState, presetId: string) => void;
137
120
  adjustSelectedWithPaste: (adjustments: Partial<AdjustmentState>) => void;
138
- gallerySetupData: GallerySetup[];
139
- };
121
+ readyState: {
122
+ isReady: boolean;
123
+ isLoading: boolean;
124
+ operations: {
125
+ paging: {
126
+ isReady: boolean;
127
+ isLoading: boolean;
128
+ };
129
+ history: {
130
+ isReady: boolean;
131
+ isLoading: boolean;
132
+ };
133
+ presets: {
134
+ isReady: boolean;
135
+ isLoading: boolean;
136
+ };
137
+ };
138
+ };
139
+ }
140
+ /**
141
+ * Helper function to create selective adjustments based on checkboxes
142
+ */
143
+ /**
144
+ * Hook for bulk photo editing with multi-image backend synchronization.
145
+ *
146
+ * **Multi-Image Backend Sync:**
147
+ * - Each selected image maintains its own history and backend state
148
+ * - History operations (undo/redo) trigger separate controller.setHistoryIndex() calls for each selected image
149
+ * - Preset application triggers separate controller.createEditorConfig() calls for each selected image
150
+ * - Adjustment changes trigger separate controller.createEditorConfig() calls for each selected image
151
+ *
152
+ * **Key Features:**
153
+ * - Individual history tracking per image with backend persistence
154
+ * - Bulk operations that respect per-image differences
155
+ * - Automatic backend synchronization for all state changes
156
+ * - Comprehensive logging of multi-image operations
157
+ */
158
+ export declare function useHonchoEditorBulk(controller: Controller, eventID: string, firebaseUid: string): UseHonchoEditorBulkReturn;
@@ -150,7 +150,7 @@ export function useHonchoEditorBulk(controller, eventID, firebaseUid) {
150
150
  autoReset: false, // Prevent auto-reset to avoid loops
151
151
  });
152
152
  // Preset management
153
- const { presets, actions: presetActions } = usePreset(controller, firebaseUid, {
153
+ const { presets, info: presetInfo, actions: presetActions } = usePreset(controller, firebaseUid, {
154
154
  autoLoad: true,
155
155
  });
156
156
  // Track which images have been synced to prevent loops
@@ -176,6 +176,47 @@ export function useHonchoEditorBulk(controller, eventID, firebaseUid) {
176
176
  }
177
177
  return true;
178
178
  };
179
+ const readyState = useMemo(() => {
180
+ const paging = {
181
+ isReady: info.isReady ?? (info.isInitialized && !info.isLoading && info.error === null),
182
+ isLoading: info.isLoading,
183
+ };
184
+ log.debug("[useHonchoEditorBulk] 📊 Paging state");
185
+ const history = {
186
+ isReady: state.isReady ?? true, // History sync happens on-demand, not blocking
187
+ isLoading: state.isLoading ?? false
188
+ };
189
+ log.debug("[useHonchoEditorBulk] 📊 History state");
190
+ const presets = {
191
+ isReady: presetInfo.isReady ?? (presetInfo.isInitialized && !presetInfo.isLoading),
192
+ isLoading: presetInfo.isLoading
193
+ };
194
+ log.debug("[useHonchoEditorBulk] 📊 Preset state");
195
+ const allReady = paging.isReady && history.isReady && presets.isReady;
196
+ const anyLoading = paging.isLoading || history.isLoading || presets.isLoading;
197
+ log.debug({
198
+ paging: { isReady: paging.isReady, isLoading: paging.isLoading },
199
+ history: { isReady: history.isReady, isLoading: history.isLoading },
200
+ presets: { isReady: presets.isReady, isLoading: presets.isLoading },
201
+ allReady,
202
+ anyLoading
203
+ }, '[useHonchoEditorBulk] 📊 Ready state computed');
204
+ return {
205
+ isReady: allReady,
206
+ isLoading: anyLoading,
207
+ operations: { paging, history, presets }
208
+ };
209
+ }, [
210
+ info.isReady,
211
+ info.isInitialized,
212
+ info.isLoading,
213
+ info.error,
214
+ state.isReady,
215
+ state.isLoading,
216
+ presetInfo.isReady,
217
+ presetInfo.isInitialized,
218
+ presetInfo.isLoading
219
+ ]);
179
220
  // Calculate active preset based on preset_id from history (not value comparison)
180
221
  const { activePreset, presetSummary } = useMemo(() => {
181
222
  if (selectedIds.length === 0) {
@@ -591,5 +632,6 @@ export function useHonchoEditorBulk(controller, eventID, firebaseUid) {
591
632
  adjustSelectedWithPaste: batchActions.adjustSelectedWithPaste,
592
633
  // Gallery setup data for AlbumImageGalleryInfinite
593
634
  gallerySetupData,
635
+ readyState,
594
636
  };
595
637
  }
@@ -21,6 +21,24 @@ export interface UseHonchoEditorSingleState {
21
21
  activePreset: Preset | null;
22
22
  presetsLoading: boolean;
23
23
  presetsError: string | null;
24
+ readyState: {
25
+ isReady: boolean;
26
+ isLoading: boolean;
27
+ operations: {
28
+ gallery: {
29
+ isReady: boolean;
30
+ isLoading: boolean;
31
+ };
32
+ adjustments: {
33
+ isReady: boolean;
34
+ isLoading: boolean;
35
+ };
36
+ presets: {
37
+ isReady: boolean;
38
+ isLoading: boolean;
39
+ };
40
+ };
41
+ };
24
42
  }
25
43
  export interface UseHonchoEditorSingleActions {
26
44
  navigateNext: () => void;
@@ -3,6 +3,7 @@ import { useEffect, useCallback, useMemo } from 'react';
3
3
  import { useAdjustmentHistory } from '../useAdjustmentHistory';
4
4
  import { useGallerySwipe } from '../useGallerySwipe';
5
5
  import { usePreset } from '../usePreset';
6
+ import { log } from '../../utils/logger';
6
7
  // Default adjustment values
7
8
  const initialAdjustments = {
8
9
  tempScore: 0, tintScore: 0, vibranceScore: 0, saturationScore: 0,
@@ -23,9 +24,9 @@ export function useHonchoEditorSingle({ controller, initImageId, firebaseUid })
23
24
  // loop on every 5 seconds to refresh the backend()
24
25
  useEffect(() => {
25
26
  if (gallerySwipe.currentImageData?.id) {
26
- console.log('Syncing adjustment history from backend for image ID:', gallerySwipe.currentImageData.id);
27
+ log.debug({ imageId: gallerySwipe.currentImageData.id }, 'Syncing adjustment history from backend for image ID:');
27
28
  adjustmentHistory.actions.syncFromBackend()
28
- .then(() => { console.log('Adjustment history synced from backend'); })
29
+ .then(() => { log.debug('Adjustment history synced from backend'); })
29
30
  .catch(console.error);
30
31
  }
31
32
  }, [gallerySwipe.currentImageData?.id]);
@@ -80,7 +81,7 @@ export function useHonchoEditorSingle({ controller, initImageId, firebaseUid })
80
81
  // Reset means setting all adjustments to 0 and adding it as new history entry
81
82
  // This allows users to undo the reset operation
82
83
  // Reset acts like normal adjustment - each reset creates a new history entry
83
- console.log('Resetting adjustments to 0 - adding to history');
84
+ log.debug('Resetting adjustments to 0 - adding to history');
84
85
  // Always add reset to history (this automatically syncs to backend via batch mode)
85
86
  await adjustmentHistory.config.setBatchMode(true);
86
87
  adjustmentHistory.actions.pushState(initialAdjustments);
@@ -90,7 +91,7 @@ export function useHonchoEditorSingle({ controller, initImageId, firebaseUid })
90
91
  await presetHook.actions.load();
91
92
  }, [presetHook.actions.load]);
92
93
  const applyPreset = useCallback(async (preset) => {
93
- console.log('Applying preset:', preset.name);
94
+ log.debug({ presetName: preset.name }, 'Applying preset:');
94
95
  const adjustmentState = {
95
96
  tempScore: preset.temperature,
96
97
  tintScore: preset.tint,
@@ -105,7 +106,9 @@ export function useHonchoEditorSingle({ controller, initImageId, firebaseUid })
105
106
  clarityScore: preset.clarity,
106
107
  sharpnessScore: preset.sharpness,
107
108
  };
108
- console.log('Applying preset:', preset.name, 'with adjustments:', adjustmentState);
109
+ log.debug({ presetName: preset.name }, `Applying preset:`);
110
+ log.debug({ adjustmentState }, `with adjustments:`);
111
+ // 'with adjustments:', adjustmentState);
109
112
  // Always apply preset and add to history (this automatically syncs to backend via batch mode)
110
113
  await adjustmentHistory.config.setBatchMode(true);
111
114
  adjustmentHistory.actions.pushState(adjustmentState);
@@ -120,6 +123,32 @@ export function useHonchoEditorSingle({ controller, initImageId, firebaseUid })
120
123
  const deletePreset = useCallback(async (presetId) => {
121
124
  await presetHook.actions.delete(presetId);
122
125
  }, [presetHook.actions.delete]);
126
+ const readyState = useMemo(() => {
127
+ const gallery = {
128
+ isReady: !gallerySwipe.isLoading && gallerySwipe.currentImageData !== null,
129
+ isLoading: gallerySwipe.isLoading
130
+ };
131
+ const adjustments = {
132
+ isReady: adjustmentHistory.historyInfo.isReady,
133
+ isLoading: adjustmentHistory.historyInfo.isLoading
134
+ };
135
+ const presets = {
136
+ isReady: presetHook.info.isReady,
137
+ isLoading: presetHook.info.isLoading
138
+ };
139
+ return {
140
+ isReady: gallery.isReady && adjustments.isReady && presets.isReady,
141
+ isLoading: gallery.isLoading || adjustments.isLoading || presets.isLoading,
142
+ operations: { gallery, adjustments, presets }
143
+ };
144
+ }, [
145
+ gallerySwipe.isLoading,
146
+ gallerySwipe.currentImageData,
147
+ adjustmentHistory.historyInfo.isReady,
148
+ adjustmentHistory.historyInfo.isLoading,
149
+ presetHook.info.isReady,
150
+ presetHook.info.isLoading
151
+ ]);
123
152
  const getEditorAdjustments = useCallback(() => {
124
153
  const adjustments = adjustmentHistory.currentState;
125
154
  return {
@@ -183,6 +212,7 @@ export function useHonchoEditorSingle({ controller, initImageId, firebaseUid })
183
212
  activePreset: activePreset || null,
184
213
  presetsLoading: presetHook.info.isLoading,
185
214
  presetsError: presetHook.info.error,
215
+ readyState,
186
216
  };
187
217
  return {
188
218
  state,
@@ -27,6 +27,10 @@ export interface HistoryInfo {
27
27
  historySize: number;
28
28
  /** Whether batch mode is currently active */
29
29
  isBatchMode: boolean;
30
+ /** Whether the history has been synced from backend at least once */
31
+ isReady: boolean;
32
+ /** Whether the history is currently syncing from backend */
33
+ isLoading: boolean;
30
34
  }
31
35
  /**
32
36
  * Actions available for history management
@@ -1,4 +1,5 @@
1
1
  import { useState, useCallback, useMemo, useRef, useEffect } from 'react';
2
+ import { log } from '../utils/logger';
2
3
  /**
3
4
  * Convert AdjustmentState to ColorAdjustment format for backend
4
5
  */
@@ -122,6 +123,9 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
122
123
  lastUpdateTime: Date.now(),
123
124
  largeHistoryWarningShown: false
124
125
  });
126
+ // Syncing from backend state
127
+ const [isSyncingFromBackend, setIsSyncingFromBackend] = useState(false);
128
+ const [hasSyncedOnce, setHasSyncedOnce] = useState(false);
125
129
  // Sync currentState with history when not in batch mode
126
130
  // useEffect(() => {
127
131
  // if (!batchModeRef.current) {
@@ -494,6 +498,7 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
494
498
  console.warn('⚠️ syncFromBackend: Controller, firebaseUid, or currentImageId not provided - cannot sync from backend');
495
499
  return;
496
500
  }
501
+ setIsSyncingFromBackend(true);
497
502
  try {
498
503
  console.log('🔄 Syncing history from backend using getEditorHistory');
499
504
  const historyResponse = await internalOptions.controller.getEditorHistory(internalOptions.firebaseUid, internalOptions.currentImageId);
@@ -562,11 +567,16 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
562
567
  setCurrentState(backendHistoryEntries[Math.max(0, currentTaskIndex)].state);
563
568
  console.log(`✅ Successfully synced ${backendHistoryEntries.length} history entries from backend`);
564
569
  console.log(`📍 Set current index to: ${Math.max(0, currentTaskIndex)} (task_id: ${historyResponse.current_task_id})`);
570
+ setHasSyncedOnce(true); // NEW: Mark as synced
571
+ log.info('✅ Successfully synced history from backend');
565
572
  }
566
573
  catch (error) {
567
- console.error('❌ Failed to sync history from backend:', error);
574
+ log.error({ error }, '❌ Failed to sync history from backend');
568
575
  throw error;
569
576
  }
577
+ finally {
578
+ setIsSyncingFromBackend(false); // NEW: Track sync end
579
+ }
570
580
  }, [internalOptions]);
571
581
  // Update specific history entry from backend without changing current index
572
582
  const updateFromBackend = useCallback((imageId, adjustmentTaskId, adjustment) => {
@@ -625,8 +635,10 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
625
635
  currentIndex: currentIndex,
626
636
  totalStates: history.length,
627
637
  historySize: getMemoryUsage(),
628
- isBatchMode: batchModeRef.current
629
- }), [history.length, getMemoryUsage, currentIndex]);
638
+ isBatchMode: batchModeRef.current,
639
+ isReady: hasSyncedOnce && !isSyncingFromBackend,
640
+ isLoading: isSyncingFromBackend
641
+ }), [history.length, getMemoryUsage, currentIndex, hasSyncedOnce, isSyncingFromBackend]);
630
642
  // Actions object - stabilized with useMemo
631
643
  const actions = useMemo(() => ({
632
644
  pushState,
@@ -68,6 +68,12 @@ export interface BatchHistoryState {
68
68
  totalImages: number;
69
69
  /** Current size of history in memory */
70
70
  historySize: number;
71
+ /** Whether the hook is operating in batch mode */
72
+ isReady: boolean;
73
+ /** Whether the hook is currently syncing with backend */
74
+ isLoading: boolean;
75
+ /** Whether the hook is currently syncing adjustments with backend */
76
+ isSyncing: boolean;
71
77
  }
72
78
  /**
73
79
  * Actions available for batch history management
@@ -130,6 +130,8 @@ export function useAdjustmentHistoryBatch(controller, firebaseUid, eventId, opti
130
130
  // Configuration refs
131
131
  const maxSizeRef = useRef(internalOptions.maxSize);
132
132
  const devWarningsRef = useRef(internalOptions.devWarnings);
133
+ const [isSyncingHistory, setIsSyncingHistory] = useState(false);
134
+ const [hasInitialSync, setHasInitialSync] = useState(false);
133
135
  // Helper function to rebuild currentBatch from imageHistories
134
136
  const rebuildCurrentBatch = useCallback(() => {
135
137
  log.debug({
@@ -1489,6 +1491,7 @@ export function useAdjustmentHistoryBatch(controller, firebaseUid, eventId, opti
1489
1491
  }
1490
1492
  return;
1491
1493
  }
1494
+ setIsSyncingHistory(true);
1492
1495
  try {
1493
1496
  log.info({ imageId }, `[useAdjustmentHistoryBatch] 🔄 Syncing history for image`);
1494
1497
  const historyResponse = await internalOptions.controller.getEditorHistory(internalOptions.firebaseUid, imageId);
@@ -1528,10 +1531,15 @@ export function useAdjustmentHistoryBatch(controller, firebaseUid, eventId, opti
1528
1531
  presetIds: historyEntries.map(e => ({ entryId: e.id, preset_id: e.adjustment.preset_id })),
1529
1532
  currentEntryPresetId: historyEntries.find(e => e.id === historyResponse.current_task_id)?.adjustment.preset_id
1530
1533
  });
1534
+ setHasInitialSync(true); // NEW: Mark as synced at least once
1535
+ log.info({ imageId }, `[useAdjustmentHistoryBatch] ✅ History synced successfully`);
1531
1536
  }
1532
1537
  catch (error) {
1533
1538
  log.error({ imageId, error }, `[useAdjustmentHistoryBatch] ❌ Failed to sync history for image`);
1534
1539
  }
1540
+ finally {
1541
+ setIsSyncingHistory(false); // NEW: Track sync end
1542
+ }
1535
1543
  }, [internalOptions, mapColorAdjustmentToAdjustmentState]);
1536
1544
  // Sync history for all images that have updates since last timestamp
1537
1545
  const syncHistoryFromBackendAll = useCallback(async () => {
@@ -1590,8 +1598,11 @@ export function useAdjustmentHistoryBatch(controller, firebaseUid, eventId, opti
1590
1598
  totalStates: imageHistories.reduce((sum, h) => sum + h.history.length, 0),
1591
1599
  selectedCount: selectedIds.length,
1592
1600
  totalImages: allImageIds.length,
1593
- historySize: getMemoryUsage()
1594
- }), [currentBatch, selectedIds, allImageIds, canUndoSelected, canRedoSelected, imageHistories, getMemoryUsage]);
1601
+ historySize: getMemoryUsage(),
1602
+ isReady: hasInitialSync && !isSyncingHistory,
1603
+ isLoading: isSyncingHistory,
1604
+ isSyncing: isSyncingHistory
1605
+ }), [currentBatch, selectedIds, allImageIds, canUndoSelected, canRedoSelected, imageHistories, getMemoryUsage, hasInitialSync, isSyncingHistory]);
1595
1606
  // Update state from socket - handles both selected and non-selected images
1596
1607
  const updateFromSocket = useCallback(async (imageId, adjustmentTaskId, adjustment, preset_id) => {
1597
1608
  log.info({
@@ -19,6 +19,10 @@ interface UseGallerySwipeReturn {
19
19
  isLoading: boolean;
20
20
  /** Error message if any operation fails */
21
21
  error: string | null;
22
+ /** Overall readiness state of the hook */
23
+ isReady: boolean;
24
+ /** Indicates if there was an error during initialization */
25
+ hasError: boolean;
22
26
  }
23
27
  /**
24
28
  * Custom hook for handling image gallery navigation with automatic pagination
@@ -1,4 +1,5 @@
1
1
  import { useState, useEffect, useCallback, useRef } from "react";
2
+ import { log } from '../utils/logger';
2
3
  /**
3
4
  * Custom hook for handling image gallery navigation with automatic pagination
4
5
  *
@@ -76,7 +77,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
76
77
  }
77
78
  }
78
79
  catch (err) {
79
- console.error(`Error fetching page ${page}:`, err);
80
+ log.error({ page, err }, `Error fetching page`);
80
81
  break;
81
82
  }
82
83
  }
@@ -104,7 +105,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
104
105
  }
105
106
  }
106
107
  catch (err) {
107
- console.error('Error loading next page:', err);
108
+ log.error({ err }, 'Error loading next page');
108
109
  }
109
110
  return [];
110
111
  }, [hasNextPage, currentEventId, controller, firebaseUid, currentPage]);
@@ -147,7 +148,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
147
148
  // Step 3: Get complete image list by searching through pages
148
149
  // This ensures we have navigation context for the current image
149
150
  const allImages = await getImageListUntilFound(initImageId, gallery.event_id);
150
- console.log("Print all images Id: ", allImages.map(image => image.id).join(', '));
151
+ log.debug({ imageIds: allImages.map(image => image.id) }, "Print all images Id");
151
152
  setCurrentImageList(allImages);
152
153
  // Step 4: Update tracking refs to prevent unnecessary re-initialization
153
154
  prevFirebaseUid.current = firebaseUid;
@@ -157,7 +158,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
157
158
  catch (err) {
158
159
  const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
159
160
  setError(errorMessage);
160
- console.error('Error initializing gallery:', err);
161
+ log.error({ err }, 'Error initializing gallery');
161
162
  }
162
163
  finally {
163
164
  setIsLoading(false);
@@ -179,19 +180,20 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
179
180
  setError(null);
180
181
  try {
181
182
  // Debug logging
182
- console.log("=== SWIPE NEXT DEBUG ===");
183
- console.log("currentImageId:", currentImageId);
184
- console.log("currentImageList length:", currentImageList.length);
185
- console.log("currentImageList IDs:", currentImageList.map(img => img.id).join(", "));
183
+ log.debug({
184
+ currentImageId,
185
+ currentImageListLength: currentImageList.length,
186
+ currentImageListIds: currentImageList.map(img => img.id),
187
+ currentIndex: getCurrentImageIndex()
188
+ }, "=== SWIPE NEXT DEBUG ===");
186
189
  // Calculate current index
187
190
  const currentIndex = getCurrentImageIndex();
188
- console.log("Current index: ", currentIndex);
189
191
  if (currentIndex === -1) {
190
192
  throw new Error('Current image not found in list');
191
193
  }
192
194
  // Scenario 1: At the last image of current list
193
195
  if (currentIndex === currentImageList.length - 1) {
194
- console.log("[SCENARIO 1] if last image: " + currentImageList.length);
196
+ log.debug({ currentImageListLength: currentImageList.length }, "[SCENARIO 1] if last image");
195
197
  // Try to load next page for more images
196
198
  const newImages = await loadNextPage();
197
199
  if (newImages.length > 0) {
@@ -200,7 +202,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
200
202
  setCurrentImageList(updatedList);
201
203
  // Navigate to first image of the new page
202
204
  const nextImage = newImages[0];
203
- console.log("Setting currentImageId to:", nextImage.id);
205
+ log.debug({ nextImageId: nextImage.id }, "Setting currentImageId to");
204
206
  setCurrentImageId(nextImage.id);
205
207
  // Fetch complete data for the new current image
206
208
  const nextImageData = await controller.onGetImage(firebaseUid, nextImage.id);
@@ -216,8 +218,11 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
216
218
  else {
217
219
  // Scenario 2: Navigate to next image in current list
218
220
  const nextImage = currentImageList[currentIndex + 1];
219
- console.log("[SCENARIO 2] Navigating to next image:", nextImage.id);
220
- console.log("Setting currentImageId from", currentImageId, "to", nextImage.id);
221
+ log.debug({
222
+ nextImageId: nextImage.id,
223
+ fromImageId: currentImageId,
224
+ toImageId: nextImage.id
225
+ }, "[SCENARIO 2] Navigating to next image");
221
226
  setCurrentImageId(nextImage.id);
222
227
  // Fetch complete data for the next image
223
228
  const nextImageData = await controller.onGetImage(firebaseUid, nextImage.id);
@@ -229,7 +234,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
229
234
  catch (err) {
230
235
  const errorMessage = err instanceof Error ? err.message : 'Error moving to next image';
231
236
  setError(errorMessage);
232
- console.error('Error in onSwipeNext:', err);
237
+ log.error({ err }, 'Error in onSwipeNext');
233
238
  }
234
239
  finally {
235
240
  setIsLoading(false);
@@ -272,7 +277,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
272
277
  catch (err) {
273
278
  const errorMessage = err instanceof Error ? err.message : 'Error moving to previous image';
274
279
  setError(errorMessage);
275
- console.error('Error in onSwipePrev:', err);
280
+ log.error({ err }, 'Error in onSwipePrev');
276
281
  }
277
282
  finally {
278
283
  setIsLoading(false);
@@ -326,7 +331,7 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
326
331
  // Additional check: only update if this is truly an external change
327
332
  // If prevInitImageId is different from initImageId, it's an external change
328
333
  if (prevInitImageId.current !== initImageId) {
329
- console.log("External initImageId change detected, updating currentImageId");
334
+ log.debug({ oldImageId: prevInitImageId.current, newImageId: initImageId }, "External initImageId change detected, updating currentImageId");
330
335
  setCurrentImageId(initImageId);
331
336
  }
332
337
  }
@@ -339,6 +344,8 @@ export function useGallerySwipe(firebaseUid, initImageId, controller) {
339
344
  onSwipeNext,
340
345
  onSwipePrev,
341
346
  isLoading,
342
- error
347
+ error,
348
+ isReady: isInitialized && currentImageData !== null && !isLoading,
349
+ hasError: error !== null
343
350
  };
344
351
  }
@@ -29,6 +29,8 @@ export interface PagingInfo {
29
29
  totalImages: number;
30
30
  /** Whether the hook has been initialized */
31
31
  isInitialized: boolean;
32
+ /** Whether the hook is ready to use */
33
+ isReady: boolean;
32
34
  }
33
35
  /**
34
36
  * Actions available for paging management
@@ -195,7 +195,8 @@ export function usePaging(controller, firebaseUid, eventId, options = {}) {
195
195
  currentPage,
196
196
  hasMore,
197
197
  totalImages: images.length,
198
- isInitialized
198
+ isInitialized,
199
+ isReady: isInitialized && isLoading
199
200
  }), [isLoading, isLoadingMore, error, currentPage, hasMore, images.length, isInitialized]);
200
201
  // Actions object - memoized to prevent re-renders
201
202
  const actions = useMemo(() => ({
@@ -43,7 +43,10 @@ export interface UsePresetReturn {
43
43
  /** Current list of presets */
44
44
  presets: Preset[];
45
45
  /** Information about preset state */
46
- info: PresetInfo;
46
+ info: PresetInfo & {
47
+ /** Ready state - true when presets are loaded and not loading */
48
+ isReady: boolean;
49
+ };
47
50
  /** Available preset actions */
48
51
  actions: PresetActions;
49
52
  }
@@ -1,4 +1,5 @@
1
1
  import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
2
+ import { log } from '../utils/logger';
2
3
  /**
3
4
  * Hook for managing presets with backend communication through Controller.
4
5
  *
@@ -45,7 +46,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
45
46
  // Helper function to log debug messages - memoized to prevent re-renders
46
47
  const debugLog = useCallback((message, data) => {
47
48
  if (memoizedOptions.devWarnings) {
48
- console.log(`[usePreset] ${message}`, data || '');
49
+ log.debug({ data }, `[usePreset] ${message}`);
49
50
  }
50
51
  }, [memoizedOptions.devWarnings]);
51
52
  // Stable references for controller and firebaseUid to detect actual changes
@@ -69,7 +70,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
69
70
  }, [debugLog]);
70
71
  // Load presets from backend
71
72
  const load = useCallback(async () => {
72
- console.log("Load Presets Get Function Called");
73
+ log.info("Load Presets Get Function Called");
73
74
  if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
74
75
  debugLog('Load skipped: missing controller or firebaseUid');
75
76
  return;
@@ -118,7 +119,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
118
119
  }, [debugLog, isInitialized]);
119
120
  // Create a new preset
120
121
  const create = useCallback(async (name, settings) => {
121
- console.log("Create Preset Get Function Called");
122
+ log.info("Create Preset Get Function Called");
122
123
  if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
123
124
  debugLog('Create skipped: missing controller or firebaseUid');
124
125
  return null;
@@ -212,7 +213,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
212
213
  }, [presets, debugLog, handleError]);
213
214
  // Delete a preset
214
215
  const deletePreset = useCallback(async (presetId) => {
215
- console.log("Delete Presets Get Function Called");
216
+ log.info("Delete Presets Get Function Called");
216
217
  if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
217
218
  debugLog('Delete skipped: missing controller or firebaseUid');
218
219
  return false;
@@ -242,9 +243,13 @@ export function usePreset(controller, firebaseUid, options = {}) {
242
243
  }, [presets, debugLog, handleError]);
243
244
  // Find preset that matches the given adjustment state
244
245
  const findByAdjustments = useCallback((adjustments) => {
245
- // Helper function to normalize adjustment values (treat null/undefined as 0)
246
+ // Helper function to normalize adjustment values (treat null/undefined as 0, convert strings to numbers)
246
247
  const normalizeValue = (value) => {
247
- return value ?? 0;
248
+ if (value == null)
249
+ return 0;
250
+ if (typeof value === 'string')
251
+ return parseFloat(value) || 0;
252
+ return value;
248
253
  };
249
254
  // Helper function to convert Preset to AdjustmentState for comparison
250
255
  const presetToAdjustmentState = (preset) => {
@@ -325,7 +330,8 @@ export function usePreset(controller, firebaseUid, options = {}) {
325
330
  isLoading,
326
331
  error,
327
332
  count: Array.isArray(presets) ? presets.length : 0,
328
- isInitialized
333
+ isInitialized,
334
+ isReady: isInitialized && !isLoading && error === null
329
335
  }), [isLoading, error, presets, isInitialized]);
330
336
  // Actions object - memoized to prevent re-renders when functions don't change
331
337
  const actions = useMemo(() => ({
@@ -338,7 +344,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
338
344
  // Return object - memoized to prevent re-renders when values don't change
339
345
  return useMemo(() => ({
340
346
  presets,
341
- info,
347
+ info: { ...info, isReady: info.isInitialized && !info.isLoading && info.error === null },
342
348
  actions
343
349
  }), [presets, info, actions]);
344
350
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yogiswara/honcho-editor-ui",
3
- "version": "3.6.6",
3
+ "version": "3.6.8",
4
4
  "description": "A complete UI component library for the Honcho photo editor.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",