@yogiswara/honcho-editor-ui 3.6.7 → 3.6.9
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/HImageEditorSkeleton.js +15 -15
- package/dist/hooks/editor/useHonchoEditorBulk.d.ts +46 -27
- package/dist/hooks/editor/useHonchoEditorBulk.js +43 -1
- package/dist/hooks/editor/useHonchoEditorSingle.d.ts +18 -0
- package/dist/hooks/editor/useHonchoEditorSingle.js +35 -5
- package/dist/hooks/useAdjustmentHistory.d.ts +4 -0
- package/dist/hooks/useAdjustmentHistory.js +15 -3
- package/dist/hooks/useAdjustmentHistoryBatch.d.ts +6 -0
- package/dist/hooks/useAdjustmentHistoryBatch.js +13 -2
- package/dist/hooks/useGallerySwipe.d.ts +4 -0
- package/dist/hooks/useGallerySwipe.js +24 -17
- package/dist/hooks/usePaging.d.ts +2 -0
- package/dist/hooks/usePaging.js +2 -1
- package/dist/hooks/usePreset.d.ts +4 -1
- package/dist/hooks/usePreset.js +14 -8
- package/package.json +1 -1
|
@@ -23,7 +23,7 @@ export default function HImageEditorSkeleton(props) {
|
|
|
23
23
|
transform: 'scale(0.92)',
|
|
24
24
|
},
|
|
25
25
|
transition: 'transform 0.1s ease-in-out',
|
|
26
|
-
}, children: _jsx(CardMedia, { title: "back", src: "svg/Back.svg", component: "img" }) }) }), _jsx(Stack, { direction: "row", justifyContent: "flex-end", alignItems: "center", spacing: 0.1, sx: { pt: !isMobile ? "0px" : "2px", pr: isMobile ? "10px" : "
|
|
26
|
+
}, children: _jsx(CardMedia, { title: "back", src: "svg/Back.svg", component: "img" }) }) }), _jsx(Stack, { direction: "row", justifyContent: "flex-end", alignItems: "center", spacing: 0.1, sx: { pt: !isMobile ? "0px" : "2px", pr: isMobile ? "10px" : "6px" }, children: _jsx(Skeleton, { variant: "rounded", width: "110px", height: "18px",
|
|
27
27
|
// animation="wave"
|
|
28
28
|
sx: {
|
|
29
29
|
bgcolor: alpha(colors.background, 0.1),
|
|
@@ -38,35 +38,35 @@ export default function HImageEditorSkeleton(props) {
|
|
|
38
38
|
position: 'inherit',
|
|
39
39
|
right: '40px',
|
|
40
40
|
top: '55px',
|
|
41
|
-
width: '
|
|
41
|
+
width: '272px',
|
|
42
42
|
pt: '4px',
|
|
43
43
|
height: 'calc(100vh - 70px)',
|
|
44
44
|
zIndex: 1200,
|
|
45
45
|
backgroundColor: '#000000',
|
|
46
46
|
overflow: 'hidden',
|
|
47
|
-
}, children: _jsxs(Stack, { direction: "row", sx: { height: '100%', p: "0px" }, children: [_jsxs(Stack, { direction: "column", sx: { height: '95%' }, children: [_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', p: "0px", pt: "8px" }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', py: "6px", gap: "8px" }, children: [_jsx(Skeleton, { variant: "rounded", width: "
|
|
47
|
+
}, children: _jsxs(Stack, { direction: "row", sx: { height: '100%', p: "0px" }, children: [_jsxs(Stack, { direction: "column", sx: { height: '95%' }, children: [_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', p: "0px", pt: "8px" }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', py: "6px", gap: "8px" }, children: [_jsx(Skeleton, { variant: "rounded", width: "198px", height: "18px", sx: { bgcolor: alpha(colors.background, 0.1), } }), _jsx(Skeleton, { variant: "rounded", width: "198px", height: "18px", sx: { bgcolor: alpha(colors.background, 0.07), } })] }) }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', p: "0px", pt: "8px" }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', py: "6px", gap: "8px" }, children: [_jsx(Skeleton, { variant: "rounded", width: "198px", height: "18px", sx: { bgcolor: alpha(colors.background, 0.1), } }), _jsx(Skeleton, { variant: "rounded", width: "198px", height: "18px", sx: { bgcolor: alpha(colors.background, 0.07), } })] }) }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', p: "0px", pt: "8px" }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', py: "6px", gap: "8px" }, children: [_jsx(Skeleton, { variant: "rounded", width: "198px", height: "18px", sx: { bgcolor: alpha(colors.background, 0.1), } }), _jsx(Skeleton, { variant: "rounded", width: "198px", height: "18px", sx: { bgcolor: alpha(colors.background, 0.07), } })] }) })] }), _jsxs(Stack, { justifyContent: "flex-start",
|
|
48
48
|
// spacing={"15px"}
|
|
49
49
|
sx: {
|
|
50
|
-
width: '10px',
|
|
50
|
+
// width: '10px',
|
|
51
51
|
flexShrink: 0,
|
|
52
52
|
pt: "0px",
|
|
53
|
-
pl: "
|
|
53
|
+
pl: "8px",
|
|
54
54
|
}, children: [_jsx(Box, { sx: {
|
|
55
55
|
width: "50px",
|
|
56
|
-
height: "
|
|
56
|
+
height: "48px",
|
|
57
57
|
py: "17px",
|
|
58
58
|
px: "16px",
|
|
59
|
-
}, children: _jsx(Skeleton, { variant: "circular", width:
|
|
59
|
+
}, children: _jsx(Skeleton, { variant: "circular", width: 20, height: 20, sx: { bgcolor: alpha(colors.background, 0.1), } }) }), _jsx(Box, { sx: {
|
|
60
60
|
width: "50px",
|
|
61
|
-
height: "
|
|
61
|
+
height: "48px",
|
|
62
62
|
py: "17px",
|
|
63
63
|
px: "16px",
|
|
64
|
-
}, children: _jsx(Skeleton, { variant: "circular", width:
|
|
64
|
+
}, children: _jsx(Skeleton, { variant: "circular", width: 20, height: 20, sx: { bgcolor: alpha(colors.background, 0.1), } }) }), _jsx(Box, { sx: {
|
|
65
65
|
width: "50px",
|
|
66
|
-
height: "
|
|
66
|
+
height: "48px",
|
|
67
67
|
py: "17px",
|
|
68
68
|
px: "16px",
|
|
69
|
-
}, children: _jsx(Skeleton, { variant: "circular", width:
|
|
69
|
+
}, children: _jsx(Skeleton, { variant: "circular", width: 20, height: 20, sx: { bgcolor: alpha(colors.background, 0.1), } }) })] })] }) })), isMobile && (_jsx(Paper, { elevation: 0, sx: {
|
|
70
70
|
position: 'fixed',
|
|
71
71
|
bottom: 0,
|
|
72
72
|
left: 0,
|
|
@@ -78,10 +78,10 @@ export default function HImageEditorSkeleton(props) {
|
|
|
78
78
|
pt: "110px",
|
|
79
79
|
// px: "143px",
|
|
80
80
|
gap: "10px"
|
|
81
|
-
}, children: _jsx(Skeleton, { variant: "rounded", width: "
|
|
82
|
-
pt: "
|
|
83
|
-
pb: "
|
|
81
|
+
}, children: _jsx(Skeleton, { variant: "rounded", width: "260px", height: 18, sx: { bgcolor: alpha(colors.background, 0.1), } }) }), _jsx(Stack, { direction: "row", justifyContent: "center", alignItems: "center", sx: {
|
|
82
|
+
pt: "24px",
|
|
83
|
+
pb: "16px",
|
|
84
84
|
px: "16px",
|
|
85
85
|
gap: "24px"
|
|
86
|
-
}, children: _jsx(Skeleton, { variant: "rounded", width:
|
|
86
|
+
}, children: _jsx(Skeleton, { variant: "rounded", width: "88px", height: 18, sx: { bgcolor: alpha(colors.background, 0.1), } }) })] }) }))] })] }));
|
|
87
87
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
+
log.debug({ imageId: gallerySwipe.currentImageData.id }, 'Syncing adjustment history from backend for image ID:');
|
|
27
28
|
adjustmentHistory.actions.syncFromBackend()
|
|
28
|
-
.then(() => {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/hooks/usePaging.js
CHANGED
|
@@ -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
|
}
|
package/dist/hooks/usePreset.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|