@@ -115,6 +115,7 @@ function Canvas({
115115 rotation : 0 ,
116116 opacity : 100 ,
117117 } ) ;
118+ const [ backendStamps , setBackendStamps ] = useState ( [ ] ) ;
118119 const [ activeFilter , setActiveFilter ] = useState ( null ) ;
119120 const [ filterParams , setFilterParams ] = useState ( { } ) ;
120121 const [ isFilterPreview , setIsFilterPreview ] = useState ( false ) ;
@@ -434,6 +435,28 @@ function Canvas({
434435
435436 setPendingDrawings ( ( prev ) => [ ...prev , drawing ] ) ;
436437
438+ // If this is a custom stamp, add it to the stamp panel
439+ if ( drawing . drawingType === "stamp" && drawing . stampData && drawing . stampData . image ) {
440+ setBackendStamps ( ( prevStamps ) => {
441+ const imageKey = drawing . stampData . image . substring ( 0 , 100 ) ;
442+ const alreadyExists = prevStamps . some ( s =>
443+ s . image && s . image . substring ( 0 , 100 ) === imageKey
444+ ) ;
445+
446+ if ( ! alreadyExists ) {
447+ console . log ( 'Adding new custom stamp from Socket.IO:' , drawing . stampData . name || 'Custom Stamp' ) ;
448+ return [ ...prevStamps , {
449+ id : `stamp-${ Date . now ( ) } -${ prevStamps . length } ` ,
450+ name : drawing . stampData . name || 'Custom Stamp' ,
451+ category : drawing . stampData . category || 'custom' ,
452+ image : drawing . stampData . image ,
453+ emoji : drawing . stampData . emoji
454+ } ] ;
455+ }
456+ return prevStamps ;
457+ } ) ;
458+ }
459+
437460 // Use requestAnimationFrame for smoother rendering
438461 requestAnimationFrame ( ( ) => {
439462 drawAllDrawings ( ) ;
@@ -1454,7 +1477,6 @@ function Canvas({
14541477 }
14551478 }
14561479
1457- // CRITICAL: Check for stamps FIRST before checking pathData as array
14581480 // Stamps have pathData as array but need special rendering
14591481 if ( drawing . drawingType === "stamp" && drawing . stampData && drawing . stampSettings && Array . isArray ( drawing . pathData ) && drawing . pathData . length > 0 ) {
14601482 // Collect stamp for batch rendering (handled after loop)
@@ -1609,7 +1631,7 @@ function Canvas({
16091631 }
16101632 }
16111633 if ( ! selectedUser ) {
1612- // Group users by 5-minute intervals (periodStart in epoch ms).
1634+ // Group users by 5-minute intervals
16131635 // Use both committed drawings and pending drawings so the UI's
16141636 // user/time-group list reflects the strokes the user currently sees.
16151637 const groupMap = { } ;
@@ -1664,7 +1686,6 @@ function Canvas({
16641686 setUserList ( groups ) ;
16651687 }
16661688
1667- // CRITICAL: Render all stamps synchronously to avoid async rendering issues
16681689 // Emoji stamps can be drawn immediately, image stamps need to be loaded first
16691690 console . log ( "[drawAllDrawings] Processing" , stampsToRender . length , "stamps" ) ;
16701691
@@ -1743,7 +1764,7 @@ function Canvas({
17431764 }
17441765 }
17451766
1746- // Copy offscreen canvas to visible canvas atomically (no flicker)
1767+ // Copy offscreen canvas to visible canvas atomically
17471768 console . log ( "[drawAllDrawings] Copying offscreen canvas to visible canvas. Total strokes rendered:" , sortedDrawings . length ) ;
17481769 context . imageSmoothingEnabled = false ;
17491770 context . clearRect ( 0 , 0 , canvasWidth , canvasHeight ) ;
@@ -2200,13 +2221,58 @@ function Canvas({
22002221 // Update pending drawings to only include those still not confirmed by backend
22012222 setPendingDrawings ( stillPending ) ;
22022223
2224+ // Extract custom stamps from all drawings and update stamp panel
2225+ extractCustomStamps ( ) ;
2226+
22032227 // Use requestAnimationFrame for smoother rendering
22042228 requestAnimationFrame ( ( ) => {
22052229 drawAllDrawings ( ) ;
22062230 setIsLoading ( false ) ;
22072231 } ) ;
22082232 } ;
22092233
2234+ // Extract custom stamps from backend drawings and update StampPanel
2235+ const extractCustomStamps = ( ) => {
2236+ try {
2237+ const customStamps = [ ] ;
2238+ const seenStamps = new Map ( ) ; // Deduplicate by image content or emoji
2239+
2240+ ( userData . drawings || [ ] ) . forEach ( ( drawing ) => {
2241+ if ( drawing . drawingType === "stamp" && drawing . stampData ) {
2242+ const stamp = drawing . stampData ;
2243+
2244+ // Skip default emoji stamps (they're already in StampPanel)
2245+ if ( stamp . emoji && ! stamp . image ) {
2246+ return ;
2247+ }
2248+
2249+ // For custom image stamps, create a unique key based on image content
2250+ if ( stamp . image ) {
2251+ const imageKey = stamp . image . substring ( 0 , 100 ) ; // Use first 100 chars as key
2252+
2253+ if ( ! seenStamps . has ( imageKey ) ) {
2254+ seenStamps . set ( imageKey , true ) ;
2255+ customStamps . push ( {
2256+ id : `stamp-${ Date . now ( ) } -${ customStamps . length } ` ,
2257+ name : stamp . name || 'Custom Stamp' ,
2258+ category : stamp . category || 'custom' ,
2259+ image : stamp . image ,
2260+ emoji : stamp . emoji
2261+ } ) ;
2262+ }
2263+ }
2264+ }
2265+ } ) ;
2266+
2267+ if ( customStamps . length > 0 ) {
2268+ console . log ( 'Extracted custom stamps from backend:' , customStamps . length ) ;
2269+ setBackendStamps ( customStamps ) ;
2270+ }
2271+ } catch ( error ) {
2272+ console . error ( 'Error extracting custom stamps:' , error ) ;
2273+ }
2274+ } ;
2275+
22102276 const startDrawingHandler = ( e ) => {
22112277 const canvas = canvasRef . current ;
22122278 const rect = canvas . getBoundingClientRect ( ) ;
@@ -3278,6 +3344,7 @@ function Canvas({
32783344 selectedStamp = { selectedStamp }
32793345 onStampSelect = { handleStampSelect }
32803346 onStampChange = { handleStampChange }
3347+ backendStamps = { backendStamps }
32813348 onFilterApply = { applyFilter }
32823349 onFilterPreview = { previewFilter }
32833350 onFilterUndo = { undoFilter }
0 commit comments