Skip to content

Commit 72f04b1

Browse files
committed
Fix issues with filters stacking up again and not showing up until refreshing canvas
1 parent 19a467f commit 72f04b1

3 files changed

Lines changed: 62 additions & 36 deletions

File tree

frontend/src/components/Canvas.js

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,7 @@ function Canvas({
106106
const [brushStyle] = useState("round");
107107
const [shapeStart, setShapeStart] = useState(null);
108108

109-
// Initialize brush engine
110109
const brushEngine = useBrushEngine();
111-
112-
// Advanced brush/stamp/filter state
113110
const [currentBrushType, setCurrentBrushType] = useState("normal");
114111
const [brushParams, setBrushParams] = useState({});
115112
const [selectedStamp, setSelectedStamp] = useState(null);
@@ -126,7 +123,7 @@ function Canvas({
126123
const [isFilterPreview, setIsFilterPreview] = useState(false);
127124
const filterCanvasRef = useRef(null);
128125
const originalCanvasDataRef = useRef(null); // For preview mode undo
129-
const preFilterCanvasStateRef = useRef(null); // Stores canvas state before ANY filters applied
126+
const preFilterCanvasStateRef = useRef(null);
130127

131128
const [showColorPicker, setShowColorPicker] = useState(false);
132129
const [isRefreshing, setIsRefreshing] = useState(false);
@@ -164,8 +161,8 @@ function Canvas({
164161
const confirmedStrokesRef = useRef(new Set());
165162
const lastDrawnStateRef = useRef(null); // Track last drawn state to avoid redundant redraws
166163
const isDrawingInProgressRef = useRef(false); // Prevent concurrent drawing operations
167-
const offscreenCanvasRef = useRef(null); // Offscreen canvas for flicker-free rendering
168-
const forceNextRedrawRef = useRef(false); // Force next redraw even if signature matches (for undo/redo)
164+
const offscreenCanvasRef = useRef(null); // Offscreen canvas for flicker free rendering
165+
const forceNextRedrawRef = useRef(false); // Force next redraw even if signature matches for undo redo
169166
const [historyMode, setHistoryMode] = useState(false);
170167
const [historyRange, setHistoryRange] = useState(null); // {start, end} in epoch ms
171168
const [historyDialogOpen, setHistoryDialogOpen] = useState(false);
@@ -277,7 +274,6 @@ function Canvas({
277274
useEffect(() => {
278275
if (!templateObjects || templateObjects.length === 0) return;
279276

280-
// Wait a tiny bit for canvas to be ready, then force redraw
281277
const timer = setTimeout(() => {
282278
if (drawAllDrawingsRef.current) {
283279
lastDrawnStateRef.current = null; // Force redraw by clearing cache
@@ -805,8 +801,14 @@ function Canvas({
805801

806802
// Helper function to update filter state
807803
const updateFilterState = () => {
808-
const filterExists = userData.drawings.some((d) => d.drawingType === "filter");
809-
setHasFilters(filterExists);
804+
// Use setUserData callback to read current state accurately
805+
setUserData((currentUserData) => {
806+
const filterExists = currentUserData.drawings.some((d) => d.drawingType === "filter");
807+
const filterCount = currentUserData.drawings.filter((d) => d.drawingType === "filter").length;
808+
console.log(`[updateFilterState] filterExists=${filterExists}, filterCount=${filterCount}`);
809+
setHasFilters(filterExists);
810+
return currentUserData;
811+
});
810812
};
811813

812814
// Advanced Brush/Stamp/Filter Functions
@@ -967,22 +969,26 @@ function Canvas({
967969
let isReplacement = existingFilterIndex !== -1;
968970

969971
if (isReplacement) {
970-
// DO NOT ALLOW STACKING - Update the existing filter with new parameters
971972
const existingFilter = userData.drawings[existingFilterIndex];
972973
existingFilter.filterParams = { ...params }; // Clone params
973974
existingFilter.timestamp = Date.now();
974975
filterDrawing = existingFilter;
975976

977+
// Update React state to reflect the filter parameter change
978+
const newUserData = new UserData(userData.userId, userData.username);
979+
newUserData.drawings = [...userData.drawings]; // Clone the array to trigger state update
980+
setUserData(newUserData);
981+
976982
// Force a complete redraw with the updated filter parameters
977983
// This will redraw all strokes first, then apply the filter
978984
lastDrawnStateRef.current = null;
979985
forceNextRedrawRef.current = true;
980986
await drawAllDrawings();
981987

982988
showLocalSnack(`Updated ${filterType} filter`);
983-
updateFilterState(); // Update filter state for UI
989+
updateFilterState();
984990

985-
// IMPORTANT: For filter updates, we need to submit the UPDATE to backend
991+
// For filter updates, we need to submit the UPDATE to backend
986992
// The backend should handle this as an update, not a new drawing
987993
try {
988994
await submitToDatabase(
@@ -1030,24 +1036,29 @@ function Canvas({
10301036
// Set filter properties directly on the drawing object
10311037
filterDrawing.drawingType = "filter";
10321038
filterDrawing.filterType = filterType;
1033-
filterDrawing.filterParams = { ...params }; // Clone params
1039+
filterDrawing.filterParams = { ...params };
10341040
filterDrawing.roomId = currentRoomId;
10351041

10361042
userData.addDrawing(filterDrawing);
1043+
1044+
// Update React state so components know about the new filter
1045+
const newUserData = new UserData(userData.userId, userData.username);
1046+
newUserData.drawings = [...userData.drawings]; // Clone array with new filter
1047+
setUserData(newUserData);
1048+
10371049
setPendingDrawings((prev) => [...prev, filterDrawing]);
10381050

10391051
setUndoStack((prev) => [...prev, filterDrawing]);
10401052
setRedoStack([]);
10411053

1042-
// Force complete redraw - this will render all strokes THEN apply filter
1054+
// Force complete redraw this will render all strokes THEN apply filter
10431055
lastDrawnStateRef.current = null;
10441056
forceNextRedrawRef.current = true;
10451057
await drawAllDrawings();
10461058

10471059
showLocalSnack(`Applied ${filterType} filter`);
1048-
updateFilterState(); // Update filter state for UI
1060+
updateFilterState();
10491061

1050-
// Submit NEW filter to backend
10511062
try {
10521063
await submitToDatabase(
10531064
filterDrawing,
@@ -1183,10 +1194,16 @@ function Canvas({
11831194
}
11841195

11851196
// Find all filter drawings in userData (not just undo stack)
1186-
const allDrawings = userData.drawings || [];
1187-
const filterDrawings = allDrawings.filter(
1188-
(drawing) => drawing.drawingType === "filter"
1189-
);
1197+
// Use setUserData callback to get the latest state
1198+
let filterDrawings = [];
1199+
setUserData((currentUserData) => {
1200+
const allDrawings = currentUserData.drawings || [];
1201+
filterDrawings = allDrawings.filter(
1202+
(drawing) => drawing.drawingType === "filter"
1203+
);
1204+
console.log(`[clearAllFilters] Found ${filterDrawings.length} filters to clear`, filterDrawings);
1205+
return currentUserData;
1206+
});
11901207

11911208
if (filterDrawings.length === 0) {
11921209
showLocalSnack("No filters to clear.");
@@ -1204,10 +1221,15 @@ function Canvas({
12041221
// Get filter IDs before removing from local state
12051222
const filterIds = filterDrawings.map(f => f.drawingId).filter(id => id);
12061223

1207-
// Remove all filter drawings from local state immediately
1208-
userData.drawings = userData.drawings.filter(
1209-
(d) => d.drawingType !== "filter"
1210-
);
1224+
// Remove all filter drawings from local state immediately using proper state update
1225+
setUserData((currentUserData) => {
1226+
const newUserData = new UserData(currentUserData.userId, currentUserData.username);
1227+
newUserData.drawings = currentUserData.drawings.filter(
1228+
(d) => d.drawingType !== "filter"
1229+
);
1230+
console.log(`[clearAllFilters] Removed ${filterDrawings.length} filters, ${newUserData.drawings.length} drawings remain`);
1231+
return newUserData;
1232+
});
12111233

12121234
// Remove from pendingDrawings
12131235
setPendingDrawings((prev) =>
@@ -1254,7 +1276,6 @@ function Canvas({
12541276
}
12551277
}
12561278

1257-
// Update undo/redo availability
12581279
await checkUndoRedoAvailability(
12591280
auth,
12601281
setUndoAvailable,
@@ -1263,7 +1284,6 @@ function Canvas({
12631284
);
12641285
} catch (e) {
12651286
console.error("Error syncing filter removal with backend:", e);
1266-
// Even if backend sync fails, local state is updated so filters are gone
12671287
}
12681288
}
12691289
} catch (error) {
@@ -2995,12 +3015,21 @@ function Canvas({
29953015
});
29963016

29973017
// Rebuild drawings array with deduplicated filters
2998-
userData.drawings = [
3018+
const deduplicatedDrawings = [
29993019
...nonFilterDrawings,
30003020
...Array.from(filtersByType.values())
30013021
];
30023022

3003-
console.log(`[mergedRefreshCanvas] Deduplicated filters. Filter count: ${filtersByType.size}`);
3023+
console.log(`[mergedRefreshCanvas] Deduplicated filters. Filter count: ${filtersByType.size}, Total drawings: ${deduplicatedDrawings.length}`);
3024+
3025+
// CRITICAL: Update both the mutable userData object AND React state
3026+
// Update userData in place so the closure reference works
3027+
userData.drawings = deduplicatedDrawings;
3028+
3029+
// Also update React state to trigger re-renders
3030+
const newUserData = new UserData(userData.userId, userData.username);
3031+
newUserData.drawings = deduplicatedDrawings;
3032+
setUserData(newUserData);
30043033

30053034
// Extract custom stamps from all drawings and update stamp panel
30063035
extractCustomStamps();
@@ -4214,7 +4243,11 @@ function Canvas({
42144243
}
42154244
canClearFilters={hasFilters}
42164245
appliedFilters={
4217-
userData.drawings.filter((drawing) => drawing.drawingType === "filter")
4246+
(() => {
4247+
const filters = userData.drawings.filter((drawing) => drawing.drawingType === "filter");
4248+
console.log(`[Canvas render] Passing ${filters.length} applied filters to Toolbar`, filters);
4249+
return filters;
4250+
})()
42184251
}
42194252
/* History Recall props (required so the toolbar can open/change/exit history mode) */
42204253
openHistoryDialog={openHistoryDialog}

frontend/src/components/Mixer/MixerPanel.jsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,27 +81,23 @@ export default function MixerPanel({ onApply, onPreview, onUndo, onClearAll, can
8181
const [isPreviewMode, setIsPreviewMode] = useState(false);
8282
const previewCanvasRef = useRef(null);
8383

84-
// Track which filters are already applied (max 1 per type to prevent stacking)
8584
const appliedFilterTypes = appliedFilters.reduce((acc, filter) => {
8685
if (filter.filterType) {
8786
acc[filter.filterType] = filter;
8887
}
8988
return acc;
9089
}, {});
9190

92-
// Check if the selected filter is already applied
9391
const isFilterAlreadyApplied = selectedFilter && appliedFilterTypes[selectedFilter];
9492

9593
useEffect(() => {
9694
if (selectedFilter) {
9795
const filter = filters.find(f => f.id === selectedFilter);
9896
if (filter) {
99-
// If filter is already applied, load its current parameters
10097
const existingFilter = appliedFilterTypes[selectedFilter];
10198
if (existingFilter && existingFilter.filterParams) {
10299
setFilterParams({ ...existingFilter.filterParams });
103100
} else {
104-
// Otherwise use defaults
105101
const defaultParams = {};
106102
Object.entries(filter.params).forEach(([key, config]) => {
107103
defaultParams[key] = config.default;
@@ -156,7 +152,6 @@ export default function MixerPanel({ onApply, onPreview, onUndo, onClearAll, can
156152
};
157153

158154
const handleClose = () => {
159-
// If in preview mode, cancel it before closing
160155
if (isPreviewMode && onUndo) {
161156
onUndo();
162157
}

frontend/src/components/Toolbar.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ const Toolbar = ({
6363
historyMode,
6464
controlsDisabled,
6565
onOpenSettings,
66-
// Advanced brush/stamp/filter props
6766
currentBrushType,
6867
onBrushSelect,
6968
onBrushParamsChange,
@@ -88,7 +87,6 @@ const Toolbar = ({
8887
};
8988

9089
const handleClose = () => {
91-
// Note: MixerPanel will handle its own cleanup via its onClose handler
9290
setAnchorEl(null);
9391
setTool(null);
9492
};

0 commit comments

Comments
 (0)