@ngenux/ngage-whiteboarding 1.0.8 → 1.0.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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +106 -91
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +113 -90
- package/dist/index.js.map +1 -1
- package/dist/src/components/Whiteboard/index.d.ts.map +1 -1
- package/dist/src/context/WhiteboardContext.d.ts.map +1 -1
- package/dist/src/utils/socket-utility.d.ts +6 -1
- package/dist/src/utils/socket-utility.d.ts.map +1 -1
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/socket-utility.esm.js +168 -0
- package/dist/utils/socket-utility.esm.js.map +1 -0
- package/dist/utils/socket-utility.js +177 -0
- package/dist/utils/socket-utility.js.map +1 -0
- package/dist/utils/src/components/Shapes/Arrow.d.ts +11 -0
- package/dist/utils/src/components/Shapes/Arrow.d.ts.map +1 -0
- package/dist/utils/src/components/Shapes/Ellipse.d.ts +11 -0
- package/dist/utils/src/components/Shapes/Ellipse.d.ts.map +1 -0
- package/dist/utils/src/components/Shapes/ErasedShape.d.ts +11 -0
- package/dist/utils/src/components/Shapes/ErasedShape.d.ts.map +1 -0
- package/dist/utils/src/components/Shapes/FreehandDrawing.d.ts +11 -0
- package/dist/utils/src/components/Shapes/FreehandDrawing.d.ts.map +1 -0
- package/dist/utils/src/components/Shapes/Line.d.ts +11 -0
- package/dist/utils/src/components/Shapes/Line.d.ts.map +1 -0
- package/dist/utils/src/components/Shapes/Rectangle.d.ts +11 -0
- package/dist/utils/src/components/Shapes/Rectangle.d.ts.map +1 -0
- package/dist/utils/src/components/Whiteboard/Board.d.ts +15 -0
- package/dist/utils/src/components/Whiteboard/Board.d.ts.map +1 -0
- package/dist/utils/src/components/Whiteboard/Toolbar.d.ts +21 -0
- package/dist/utils/src/components/Whiteboard/Toolbar.d.ts.map +1 -0
- package/dist/utils/src/components/Whiteboard/index.d.ts +11 -0
- package/dist/utils/src/components/Whiteboard/index.d.ts.map +1 -0
- package/dist/utils/src/context/WhiteboardContext.d.ts +128 -0
- package/dist/utils/src/context/WhiteboardContext.d.ts.map +1 -0
- package/dist/utils/src/hooks/useCapture.d.ts +4 -0
- package/dist/utils/src/hooks/useCapture.d.ts.map +1 -0
- package/dist/utils/src/hooks/useCollaborativeWhiteboard.d.ts +27 -0
- package/dist/utils/src/hooks/useCollaborativeWhiteboard.d.ts.map +1 -0
- package/dist/utils/src/lib/utils.d.ts +3 -0
- package/dist/utils/src/lib/utils.d.ts.map +1 -0
- package/dist/utils/src/types/index.d.ts +123 -0
- package/dist/utils/src/types/index.d.ts.map +1 -0
- package/dist/utils/src/utils/compression.d.ts +14 -0
- package/dist/utils/src/utils/compression.d.ts.map +1 -0
- package/dist/utils/src/utils/socket-utility.d.ts +11 -0
- package/dist/utils/src/utils/socket-utility.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { WhiteboardProvider } from './src/context/WhiteboardContext';
|
|
|
4
4
|
import { useCapture } from './src/hooks/useCapture';
|
|
5
5
|
import { cn } from './src/lib/utils';
|
|
6
6
|
export { Whiteboard, WhiteboardProvider, useCapture as useWhiteboardStream, cn };
|
|
7
|
+
export { isSocketConnected, getSocket, onSocketStatusChange, onSend, onReceive, leaveRoom, disconnectSocket, waitForSocket } from './src/utils/socket-utility';
|
|
7
8
|
export type { WhiteboardProps } from './src/components/Whiteboard/index';
|
|
8
9
|
export type { ShapeProps, ToolType, StrokeStyle, WhiteboardState, DrawingAction, CompressedData } from './src/types';
|
|
9
10
|
export interface WhiteboardProviderProps {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.tsx"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,IAAI,mBAAmB,EAAE,EAAE,EAAE,CAAC;AAGjF,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,YAAY,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACX,eAAe,EACf,aAAa,EACb,cAAc,EACf,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.tsx"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,IAAI,mBAAmB,EAAE,EAAE,EAAE,CAAC;AAGjF,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,oBAAoB,EACpB,MAAM,EACN,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,YAAY,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACX,eAAe,EACf,aAAa,EACb,cAAc,EACf,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
package/dist/index.esm.js
CHANGED
|
@@ -35255,12 +35255,16 @@ const WhiteboardProvider = ({ children, webSocketUrl }) => {
|
|
|
35255
35255
|
};
|
|
35256
35256
|
const requestStateFromPeers = () => {
|
|
35257
35257
|
if (currentQueueAction) {
|
|
35258
|
-
|
|
35259
|
-
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35263
|
-
|
|
35258
|
+
setTimeout(() => {
|
|
35259
|
+
if (currentQueueAction) {
|
|
35260
|
+
currentQueueAction({
|
|
35261
|
+
type: 'request_state',
|
|
35262
|
+
payload: '',
|
|
35263
|
+
requesterId: state.userId,
|
|
35264
|
+
timestamp: Date.now(),
|
|
35265
|
+
});
|
|
35266
|
+
}
|
|
35267
|
+
}, 2000);
|
|
35264
35268
|
}
|
|
35265
35269
|
else {
|
|
35266
35270
|
console.warn('[STATE_SYNC] No queue action available for state request');
|
|
@@ -37069,32 +37073,6 @@ const Eraser = createLucideIcon("Eraser", [
|
|
|
37069
37073
|
*/
|
|
37070
37074
|
|
|
37071
37075
|
|
|
37072
|
-
const LockOpen = createLucideIcon("LockOpen", [
|
|
37073
|
-
["rect", { width: "18", height: "11", x: "3", y: "11", rx: "2", ry: "2", key: "1w4ew1" }],
|
|
37074
|
-
["path", { d: "M7 11V7a5 5 0 0 1 9.9-1", key: "1mm8w8" }]
|
|
37075
|
-
]);
|
|
37076
|
-
|
|
37077
|
-
/**
|
|
37078
|
-
* @license lucide-react v0.460.0 - ISC
|
|
37079
|
-
*
|
|
37080
|
-
* This source code is licensed under the ISC license.
|
|
37081
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
37082
|
-
*/
|
|
37083
|
-
|
|
37084
|
-
|
|
37085
|
-
const Lock = createLucideIcon("Lock", [
|
|
37086
|
-
["rect", { width: "18", height: "11", x: "3", y: "11", rx: "2", ry: "2", key: "1w4ew1" }],
|
|
37087
|
-
["path", { d: "M7 11V7a5 5 0 0 1 10 0v4", key: "fwvmzm" }]
|
|
37088
|
-
]);
|
|
37089
|
-
|
|
37090
|
-
/**
|
|
37091
|
-
* @license lucide-react v0.460.0 - ISC
|
|
37092
|
-
*
|
|
37093
|
-
* This source code is licensed under the ISC license.
|
|
37094
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
37095
|
-
*/
|
|
37096
|
-
|
|
37097
|
-
|
|
37098
37076
|
const Minus = createLucideIcon("Minus", [["path", { d: "M5 12h14", key: "1ays0h" }]]);
|
|
37099
37077
|
|
|
37100
37078
|
/**
|
|
@@ -37175,7 +37153,7 @@ const Undo2 = createLucideIcon("Undo2", [
|
|
|
37175
37153
|
]);
|
|
37176
37154
|
|
|
37177
37155
|
// Top Toolbar Component
|
|
37178
|
-
const TopToolbar = ({ queueAction, handleExportImage, handleClear, handleLockToggle, isAdmin = false, hasToolAccess = false, isGloballyUnlocked =
|
|
37156
|
+
const TopToolbar = ({ queueAction, handleExportImage, handleClear, handleLockToggle, isAdmin = false, hasToolAccess = false, isGloballyUnlocked = true, shouldBeOpenByDefault = true, hasVideoBackground = false }) => {
|
|
37179
37157
|
const { state, dispatch } = useWhiteboard();
|
|
37180
37158
|
const [isVisible, setIsVisible] = useState(shouldBeOpenByDefault);
|
|
37181
37159
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
@@ -37251,9 +37229,7 @@ const TopToolbar = ({ queueAction, handleExportImage, handleClear, handleLockTog
|
|
|
37251
37229
|
if (!isInitialized) {
|
|
37252
37230
|
return null;
|
|
37253
37231
|
}
|
|
37254
|
-
return (jsxs(Fragment, { children: [jsxs("div", { className: "absolute top-5 left-1/2 transform -translate-x-1/2 flex flex-col items-center z-10", children: [!isVisible && (jsx("button", { className: "w-10 h-10 flex items-center justify-center bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300", onClick: handleToggleVisibility, title: "Show Tools", children: jsx(ChevronDown, { size: 16, className: "text-current" }) })), isVisible && (jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg", children: jsxs("div", { className: "flex items-center gap-1 p-1", children: [
|
|
37255
|
-
? 'bg-green-100 dark:bg-green-900/50 text-green-600 dark:text-green-400 hover:bg-green-200 dark:hover:bg-green-900/70'
|
|
37256
|
-
: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}`, onClick: handleLockToggle, title: isGloballyUnlocked ? 'Whiteboard unlocked for all users - Click to lock' : 'Whiteboard locked - Click to unlock for all users', children: isGloballyUnlocked ? jsx(LockOpen, { size: 16, className: "text-current" }) : jsx(Lock, { size: 16, className: "text-current" }) })), jsx("button", { className: `w-10 h-10 flex items-center justify-center rounded ${hasToolAccess ? 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300' : 'opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-600'}`, onClick: handleUndo, disabled: !hasToolAccess, title: hasToolAccess ? 'Undo' : 'Access restricted', children: jsx(Undo2, { size: 16, className: "text-current" }) }), jsx("button", { className: `w-10 h-10 flex items-center justify-center rounded ${hasToolAccess ? 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300' : 'opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-600'}`, onClick: handleRedo, disabled: !hasToolAccess, title: hasToolAccess ? 'Redo' : 'Access restricted', children: jsx(Redo2, { size: 16, className: "text-current" }) }), jsx("div", { className: "w-px h-6 bg-gray-300 dark:bg-gray-600 mx-1" }), tools.map((tool) => (jsx("button", { className: `w-10 h-10 flex items-center justify-center rounded transition-colors ${!hasToolAccess
|
|
37232
|
+
return (jsxs(Fragment, { children: [jsxs("div", { className: "absolute top-5 left-1/2 transform -translate-x-1/2 flex flex-col items-center z-10", children: [!isVisible && (jsx("button", { className: "w-10 h-10 flex items-center justify-center bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300", onClick: handleToggleVisibility, title: "Show Tools", children: jsx(ChevronDown, { size: 16, className: "text-current" }) })), isVisible && (jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg", children: jsxs("div", { className: "flex items-center gap-1 p-1", children: [jsx("button", { className: `w-10 h-10 flex items-center justify-center rounded ${hasToolAccess ? 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300' : 'opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-600'}`, onClick: handleUndo, disabled: !hasToolAccess, title: hasToolAccess ? 'Undo' : 'Access restricted', children: jsx(Undo2, { size: 16, className: "text-current" }) }), jsx("button", { className: `w-10 h-10 flex items-center justify-center rounded ${hasToolAccess ? 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300' : 'opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-600'}`, onClick: handleRedo, disabled: !hasToolAccess, title: hasToolAccess ? 'Redo' : 'Access restricted', children: jsx(Redo2, { size: 16, className: "text-current" }) }), jsx("div", { className: "w-px h-6 bg-gray-300 dark:bg-gray-600 mx-1" }), tools.map((tool) => (jsx("button", { className: `w-10 h-10 flex items-center justify-center rounded transition-colors ${!hasToolAccess
|
|
37257
37233
|
? 'opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-600'
|
|
37258
37234
|
: state.tool === tool.type
|
|
37259
37235
|
? 'bg-purple-100 dark:bg-purple-900/50 text-purple-600 dark:text-purple-300'
|
|
@@ -42311,6 +42287,7 @@ Object.assign(lookup, {
|
|
|
42311
42287
|
let socket = null;
|
|
42312
42288
|
const joinedRooms = new Set();
|
|
42313
42289
|
const setupCallbacks = new Map();
|
|
42290
|
+
const statusChangeCallbacks = new Set();
|
|
42314
42291
|
let currentWebSocketUrl = undefined;
|
|
42315
42292
|
// Initialize socket connection
|
|
42316
42293
|
const initializeSocket = (webSocketUrl) => {
|
|
@@ -42326,7 +42303,6 @@ const initializeSocket = (webSocketUrl) => {
|
|
|
42326
42303
|
setupCallbacks.clear();
|
|
42327
42304
|
}
|
|
42328
42305
|
currentWebSocketUrl = webSocketUrl;
|
|
42329
|
-
console.log('[SOCKET] Using socket server URL:', socketServerUrl);
|
|
42330
42306
|
if (!socketServerUrl) {
|
|
42331
42307
|
console.error('[SOCKET] No socket server URL provided');
|
|
42332
42308
|
}
|
|
@@ -42337,29 +42313,21 @@ const initializeSocket = (webSocketUrl) => {
|
|
|
42337
42313
|
reconnectionDelay: 1000,
|
|
42338
42314
|
});
|
|
42339
42315
|
socket.on('connect', () => {
|
|
42340
|
-
|
|
42341
|
-
// Re-join all rooms after reconnection
|
|
42316
|
+
statusChangeCallbacks.forEach(callback => callback(true));
|
|
42342
42317
|
joinedRooms.forEach(roomId => {
|
|
42343
42318
|
socket.emit('join-room', roomId);
|
|
42344
|
-
console.log('[SOCKET] Re-joined room:', roomId);
|
|
42345
42319
|
});
|
|
42346
42320
|
});
|
|
42347
42321
|
socket.on('disconnect', () => {
|
|
42348
|
-
|
|
42322
|
+
statusChangeCallbacks.forEach(callback => callback(false));
|
|
42349
42323
|
});
|
|
42350
|
-
socket.on('connect_error', (
|
|
42351
|
-
|
|
42324
|
+
socket.on('connect_error', () => {
|
|
42325
|
+
// Connection error handled by Socket.IO reconnection logic
|
|
42352
42326
|
});
|
|
42353
42327
|
// Set up the global receive-message listener once
|
|
42354
42328
|
socket.on('receive-message', (message) => {
|
|
42355
42329
|
const callback = setupCallbacks.get(message.roomId);
|
|
42356
42330
|
if (callback) {
|
|
42357
|
-
console.log('[SOCKET] Received message from room:', message.roomId, {
|
|
42358
|
-
compression: message.data.compressionType,
|
|
42359
|
-
originalSize: message.data.originalSize,
|
|
42360
|
-
compressedSize: message.data.compressedSize,
|
|
42361
|
-
from: message.from
|
|
42362
|
-
});
|
|
42363
42331
|
callback(message.data);
|
|
42364
42332
|
}
|
|
42365
42333
|
});
|
|
@@ -42393,18 +42361,23 @@ const onSend = (roomId, data, webSocketUrl) => {
|
|
|
42393
42361
|
console.error('[SOCKET] Error sending message:', error);
|
|
42394
42362
|
}
|
|
42395
42363
|
};
|
|
42396
|
-
const onReceive = (roomId, callback, webSocketUrl) => {
|
|
42364
|
+
const onReceive = (roomId, callback, webSocketUrl, onRoomJoined) => {
|
|
42397
42365
|
const socketInstance = initializeSocket(webSocketUrl);
|
|
42398
42366
|
// Store the callback for this room
|
|
42399
42367
|
setupCallbacks.set(roomId, callback);
|
|
42400
|
-
// Only join the room if we haven't already
|
|
42401
42368
|
if (!joinedRooms.has(roomId)) {
|
|
42402
42369
|
socketInstance.emit('join-room', roomId);
|
|
42403
42370
|
joinedRooms.add(roomId);
|
|
42404
|
-
|
|
42371
|
+
if (onRoomJoined) {
|
|
42372
|
+
setTimeout(() => {
|
|
42373
|
+
onRoomJoined();
|
|
42374
|
+
}, 100);
|
|
42375
|
+
}
|
|
42405
42376
|
}
|
|
42406
42377
|
else {
|
|
42407
|
-
|
|
42378
|
+
if (onRoomJoined) {
|
|
42379
|
+
onRoomJoined();
|
|
42380
|
+
}
|
|
42408
42381
|
}
|
|
42409
42382
|
};
|
|
42410
42383
|
const leaveRoom = (roomId) => {
|
|
@@ -42421,20 +42394,66 @@ const disconnectSocket = () => {
|
|
|
42421
42394
|
socket = null;
|
|
42422
42395
|
joinedRooms.clear();
|
|
42423
42396
|
setupCallbacks.clear();
|
|
42424
|
-
console.log('[SOCKET] Socket disconnected and cleaned up');
|
|
42425
42397
|
}
|
|
42426
42398
|
};
|
|
42399
|
+
// Get current socket connection status
|
|
42400
|
+
const isSocketConnected = () => {
|
|
42401
|
+
return socket?.connected ?? false;
|
|
42402
|
+
};
|
|
42403
|
+
// Get current socket instance
|
|
42404
|
+
const getSocket = () => {
|
|
42405
|
+
return socket;
|
|
42406
|
+
};
|
|
42407
|
+
// Subscribe to connection status changes
|
|
42408
|
+
const onSocketStatusChange = (callback) => {
|
|
42409
|
+
const socketInstance = socket || initializeSocket();
|
|
42410
|
+
statusChangeCallbacks.add(callback);
|
|
42411
|
+
callback(socketInstance.connected);
|
|
42412
|
+
return () => {
|
|
42413
|
+
statusChangeCallbacks.delete(callback);
|
|
42414
|
+
};
|
|
42415
|
+
};
|
|
42416
|
+
const waitForSocket = (webSocketUrl, timeoutMs = 5000) => {
|
|
42417
|
+
return new Promise((resolve) => {
|
|
42418
|
+
const socketInstance = initializeSocket(webSocketUrl);
|
|
42419
|
+
if (socketInstance.connected) {
|
|
42420
|
+
resolve();
|
|
42421
|
+
return;
|
|
42422
|
+
}
|
|
42423
|
+
const handleConnect = () => {
|
|
42424
|
+
socketInstance.off('connect', handleConnect);
|
|
42425
|
+
clearTimeout(timeoutHandle);
|
|
42426
|
+
resolve();
|
|
42427
|
+
};
|
|
42428
|
+
socketInstance.on('connect', handleConnect);
|
|
42429
|
+
const timeoutHandle = setTimeout(() => {
|
|
42430
|
+
socketInstance.off('connect', handleConnect);
|
|
42431
|
+
resolve();
|
|
42432
|
+
}, timeoutMs);
|
|
42433
|
+
});
|
|
42434
|
+
};
|
|
42427
42435
|
|
|
42428
42436
|
const Whiteboard = ({ roomId, isAdmin = false, allowedUsers = [], userId, transparentBackground = false, videoStream }) => {
|
|
42429
42437
|
const { state, dispatch, setQueueAction, requestStateFromPeers, webSocketUrl } = useWhiteboard();
|
|
42430
42438
|
const [lastCollaborativeAction, setLastCollaborativeAction] = useState(null);
|
|
42431
42439
|
const [hasRequestedState, setHasRequestedState] = useState(false);
|
|
42432
42440
|
const [lastStateRequestTime, setLastStateRequestTime] = useState(0);
|
|
42433
|
-
const [
|
|
42441
|
+
const [isSocketConnected, setIsSocketConnected] = useState(false);
|
|
42442
|
+
const [isRoomJoined, setIsRoomJoined] = useState(false);
|
|
42443
|
+
const [isGloballyUnlocked, setIsGloballyUnlocked] = useState(true); // Global unlock status
|
|
42434
42444
|
const [syncedAllowedUsers, setSyncedAllowedUsers] = useState(allowedUsers); // Synced allowed users from collaboration
|
|
42435
42445
|
const isCollaborativeUpdateRef = useRef(false); // Track if update came from collaboration
|
|
42436
42446
|
const lastClearTimeRef = useRef(0); // Track when last clear happened
|
|
42437
42447
|
const boardRef = useRef(null);
|
|
42448
|
+
useEffect(() => {
|
|
42449
|
+
const unsubscribe = onSocketStatusChange((connected) => {
|
|
42450
|
+
setIsSocketConnected(connected);
|
|
42451
|
+
});
|
|
42452
|
+
return () => {
|
|
42453
|
+
console.log('[WHITEBOARD] Unsubscribing from socket status listener');
|
|
42454
|
+
unsubscribe();
|
|
42455
|
+
};
|
|
42456
|
+
}, []); // Empty dependencies - single listener for component lifecycle
|
|
42438
42457
|
// Set userId in context when component mounts or userId changes
|
|
42439
42458
|
useEffect(() => {
|
|
42440
42459
|
dispatch({ type: 'SET_USER_ID', payload: userId });
|
|
@@ -42515,7 +42534,9 @@ const Whiteboard = ({ roomId, isAdmin = false, allowedUsers = [], userId, transp
|
|
|
42515
42534
|
setHasRequestedState(false);
|
|
42516
42535
|
}
|
|
42517
42536
|
callback(data);
|
|
42518
|
-
}, webSocketUrl)
|
|
42537
|
+
}, webSocketUrl, () => {
|
|
42538
|
+
setIsRoomJoined(true);
|
|
42539
|
+
});
|
|
42519
42540
|
}, [roomId, webSocketUrl]);
|
|
42520
42541
|
// Initialize the collaborative whiteboard hook
|
|
42521
42542
|
const collaborativeConfig = useMemo(() => ({
|
|
@@ -42552,48 +42573,42 @@ const Whiteboard = ({ roomId, isAdmin = false, allowedUsers = [], userId, transp
|
|
|
42552
42573
|
});
|
|
42553
42574
|
}
|
|
42554
42575
|
}, [allowedUsers, queueAction, userId, syncedAllowedUsers]);
|
|
42555
|
-
// Request state from peers when
|
|
42576
|
+
// Request state from peers when room is successfully joined AND socket is connected
|
|
42577
|
+
// Need both conditions to ensure message can actually be sent
|
|
42556
42578
|
useEffect(() => {
|
|
42557
|
-
if (
|
|
42558
|
-
|
|
42559
|
-
const now = Date.now();
|
|
42560
|
-
if (now - lastClearTimeRef.current < 10000) {
|
|
42561
|
-
return;
|
|
42562
|
-
}
|
|
42563
|
-
if (now - lastStateRequestTime > 5000) {
|
|
42564
|
-
requestStateFromPeers();
|
|
42565
|
-
setHasRequestedState(true);
|
|
42566
|
-
setLastStateRequestTime(now);
|
|
42567
|
-
}
|
|
42568
|
-
}, 500);
|
|
42569
|
-
return () => clearTimeout(requestTimer);
|
|
42579
|
+
if (!isRoomJoined || !isSocketConnected || hasRequestedState || state.shapes.length > 0 || !queueAction) {
|
|
42580
|
+
return;
|
|
42570
42581
|
}
|
|
42571
|
-
|
|
42582
|
+
const now = Date.now();
|
|
42583
|
+
if (now - lastClearTimeRef.current < 10000) {
|
|
42584
|
+
return;
|
|
42585
|
+
}
|
|
42586
|
+
requestStateFromPeers();
|
|
42587
|
+
setHasRequestedState(true);
|
|
42588
|
+
setLastStateRequestTime(Date.now());
|
|
42589
|
+
}, [isRoomJoined, isSocketConnected, state.shapes.length, hasRequestedState, queueAction, requestStateFromPeers, userId, roomId]);
|
|
42572
42590
|
// Reset request flag when room changes
|
|
42573
42591
|
useEffect(() => {
|
|
42574
42592
|
setHasRequestedState(false);
|
|
42575
42593
|
setLastStateRequestTime(0);
|
|
42594
|
+
setIsRoomJoined(false);
|
|
42576
42595
|
}, [roomId]);
|
|
42577
|
-
//
|
|
42596
|
+
// Retry state request if canvas is still empty after 3 seconds
|
|
42578
42597
|
useEffect(() => {
|
|
42579
|
-
if (queueAction
|
|
42580
|
-
|
|
42581
|
-
|
|
42582
|
-
|
|
42583
|
-
|
|
42584
|
-
|
|
42585
|
-
|
|
42586
|
-
}
|
|
42587
|
-
if (now - lastStateRequestTime > 10000) {
|
|
42588
|
-
const reconnectTimer = setTimeout(() => {
|
|
42589
|
-
requestStateFromPeers();
|
|
42590
|
-
setHasRequestedState(true);
|
|
42591
|
-
setLastStateRequestTime(now);
|
|
42592
|
-
}, 2000);
|
|
42593
|
-
return () => clearTimeout(reconnectTimer);
|
|
42594
|
-
}
|
|
42598
|
+
if (!isRoomJoined || !isSocketConnected || hasRequestedState || state.shapes.length > 0 || !queueAction) {
|
|
42599
|
+
return;
|
|
42600
|
+
}
|
|
42601
|
+
const now = Date.now();
|
|
42602
|
+
// Don't retry if we recently cleared
|
|
42603
|
+
if (now - lastClearTimeRef.current < 10000) {
|
|
42604
|
+
return;
|
|
42595
42605
|
}
|
|
42596
|
-
|
|
42606
|
+
const retryTimer = setTimeout(() => {
|
|
42607
|
+
requestStateFromPeers();
|
|
42608
|
+
setLastStateRequestTime(Date.now());
|
|
42609
|
+
}, 3000);
|
|
42610
|
+
return () => clearTimeout(retryTimer);
|
|
42611
|
+
}, [isRoomJoined, isSocketConnected, state.shapes.length, queueAction, requestStateFromPeers, lastStateRequestTime, userId]);
|
|
42597
42612
|
// Cleanup stale active drawings periodically
|
|
42598
42613
|
useEffect(() => {
|
|
42599
42614
|
const cleanupInterval = setInterval(() => {
|
|
@@ -42795,7 +42810,7 @@ const Whiteboard = ({ roomId, isAdmin = false, allowedUsers = [], userId, transp
|
|
|
42795
42810
|
});
|
|
42796
42811
|
}
|
|
42797
42812
|
};
|
|
42798
|
-
}, [isAdmin,
|
|
42813
|
+
}, [isAdmin, userId, dispatch]);
|
|
42799
42814
|
// Global cleanup on app unmount
|
|
42800
42815
|
useEffect(() => {
|
|
42801
42816
|
const handleBeforeUnload = () => {
|
|
@@ -45838,5 +45853,5 @@ function cn(...inputs) {
|
|
|
45838
45853
|
return twMerge(clsx(inputs));
|
|
45839
45854
|
}
|
|
45840
45855
|
|
|
45841
|
-
export { Whiteboard, WhiteboardProvider, cn, useCapture as useWhiteboardStream };
|
|
45856
|
+
export { Whiteboard, WhiteboardProvider, cn, disconnectSocket, getSocket, isSocketConnected, leaveRoom, onReceive, onSend, onSocketStatusChange, useCapture as useWhiteboardStream, waitForSocket };
|
|
45842
45857
|
//# sourceMappingURL=index.esm.js.map
|