@@ -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 }
0 commit comments