@100mslive/roomkit-react 0.1.20-alpha.1 → 0.2.1-alpha.0

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.
Files changed (84) hide show
  1. package/README.md +27 -2
  2. package/dist/{HLSView-PJLISGG4.js → HLSView-GKCGIZ5F.js} +2 -2
  3. package/dist/Input/Input.d.ts +3 -3
  4. package/dist/Prebuilt/components/Chat/ChatActions.d.ts +2 -1
  5. package/dist/Prebuilt/components/Polls/CreateQuestions/DeleteQuestionModal.d.ts +6 -0
  6. package/dist/Prebuilt/components/Polls/CreateQuestions/QuestionForm.d.ts +22 -0
  7. package/dist/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.d.ts +11 -0
  8. package/dist/Prebuilt/components/Polls/Voting/StandardVoting.d.ts +5 -0
  9. package/dist/Prebuilt/components/Polls/Voting/TimedVoting.d.ts +5 -0
  10. package/dist/Prebuilt/components/Polls/Voting/Voting.d.ts +5 -0
  11. package/dist/Prebuilt/components/Polls/common/Line.d.ts +2 -0
  12. package/dist/Prebuilt/components/Polls/common/OptionInputWithDelete.d.ts +8 -0
  13. package/dist/Prebuilt/components/Polls/common/StatusIndicator.d.ts +4 -0
  14. package/dist/Prebuilt/components/Polls/common/VoteCount.d.ts +4 -0
  15. package/dist/Prebuilt/components/Polls/common/VoteProgress.d.ts +6 -0
  16. package/dist/Prebuilt/components/Polls/common/VoterList.d.ts +4 -0
  17. package/dist/Prebuilt/components/TileMenu/utils.d.ts +5 -0
  18. package/dist/Prebuilt/components/hooks/usePinnedBy.d.ts +1 -0
  19. package/dist/Prebuilt/components/hooks/{useSetPinnedMessages.d.ts → usePinnedMessages.d.ts} +6 -1
  20. package/dist/TextArea/TextArea.d.ts +441 -0
  21. package/dist/TextArea/index.d.ts +1 -0
  22. package/dist/Toast/Toast.d.ts +1 -1
  23. package/dist/{chunk-QENB2CO7.js → chunk-FTOP3RHP.js} +2778 -2714
  24. package/dist/chunk-FTOP3RHP.js.map +7 -0
  25. package/dist/index.cjs.js +3011 -2940
  26. package/dist/index.cjs.js.map +4 -4
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +3 -1
  29. package/dist/meta.cjs.json +477 -390
  30. package/dist/meta.esbuild.json +487 -398
  31. package/package.json +7 -8
  32. package/src/Button/Button.tsx +4 -4
  33. package/src/Input/Input.tsx +1 -1
  34. package/src/Prebuilt/App.tsx +2 -0
  35. package/src/Prebuilt/components/Chat/ChatActions.tsx +25 -8
  36. package/src/Prebuilt/components/Chat/ChatBody.tsx +64 -21
  37. package/src/Prebuilt/components/Chat/ChatFooter.tsx +1 -0
  38. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +2 -2
  39. package/src/Prebuilt/components/Header/AdditionalRoomState.jsx +1 -38
  40. package/src/Prebuilt/components/Header/StreamActions.tsx +1 -1
  41. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +11 -1
  42. package/src/Prebuilt/components/Polls/CreateQuestions/{DeleteQuestionModal.jsx → DeleteQuestionModal.tsx} +9 -1
  43. package/src/Prebuilt/components/Polls/CreateQuestions/{QuestionForm.jsx → QuestionForm.tsx} +71 -30
  44. package/src/Prebuilt/components/Polls/CreateQuestions/{SavedQuestion.jsx → SavedQuestion.tsx} +24 -15
  45. package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +1 -1
  46. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +61 -80
  47. package/src/Prebuilt/components/Polls/Voting/{StandardVoting.jsx → StandardVoting.tsx} +3 -7
  48. package/src/Prebuilt/components/Polls/Voting/{TimedVoting.jsx → TimedVoting.tsx} +4 -7
  49. package/src/Prebuilt/components/Polls/Voting/{Voting.jsx → Voting.tsx} +4 -3
  50. package/src/Prebuilt/components/Polls/common/Line.tsx +4 -0
  51. package/src/Prebuilt/components/Polls/common/{OptionInputWithDelete.jsx → OptionInputWithDelete.tsx} +14 -2
  52. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +1 -1
  53. package/src/Prebuilt/components/Polls/common/{StatusIndicator.jsx → StatusIndicator.tsx} +1 -2
  54. package/src/Prebuilt/components/Polls/common/{VoteCount.jsx → VoteCount.tsx} +1 -2
  55. package/src/Prebuilt/components/Polls/common/{VoteProgress.jsx → VoteProgress.tsx} +3 -2
  56. package/src/Prebuilt/components/Polls/common/{VoterList.jsx → VoterList.tsx} +1 -1
  57. package/src/Prebuilt/components/TileMenu/TileMenu.jsx +3 -1
  58. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +15 -3
  59. package/src/Prebuilt/components/TileMenu/utils.ts +7 -0
  60. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +7 -3
  61. package/src/Prebuilt/components/VideoTile.jsx +2 -4
  62. package/src/Prebuilt/components/hooks/usePinnedBy.tsx +22 -0
  63. package/src/Prebuilt/components/hooks/{useSetPinnedMessages.ts → usePinnedMessages.ts} +2 -2
  64. package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +1 -4
  65. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +0 -1
  66. package/src/TextArea/TextArea.tsx +30 -0
  67. package/src/TextArea/index.tsx +1 -0
  68. package/src/TileMenu/StyledMenuTile.tsx +1 -0
  69. package/src/index.ts +1 -0
  70. package/src/store/StorybookSDK.ts +3 -1
  71. package/dist/Prebuilt/plugins/whiteboard/ToggleWhiteboard.d.ts +0 -5
  72. package/dist/chunk-QENB2CO7.js.map +0 -7
  73. package/src/Prebuilt/components/Polls/common/Votes.jsx +0 -72
  74. package/src/Prebuilt/layouts/WhiteboardView.jsx +0 -40
  75. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +0 -110
  76. package/src/Prebuilt/plugins/whiteboard/README.md +0 -29
  77. package/src/Prebuilt/plugins/whiteboard/ToggleWhiteboard.tsx +0 -37
  78. package/src/Prebuilt/plugins/whiteboard/Whiteboard.css +0 -12
  79. package/src/Prebuilt/plugins/whiteboard/Whiteboard.jsx +0 -11
  80. package/src/Prebuilt/plugins/whiteboard/WhiteboardEvents.js +0 -8
  81. package/src/Prebuilt/plugins/whiteboard/index.js +0 -3
  82. package/src/Prebuilt/plugins/whiteboard/useMultiplayerState.js +0 -212
  83. package/src/Prebuilt/plugins/whiteboard/useWhiteboardMetadata.js +0 -47
  84. /package/dist/{HLSView-PJLISGG4.js.map → HLSView-GKCGIZ5F.js.map} +0 -0
@@ -1,72 +0,0 @@
1
- import React from 'react';
2
- import { Avatar, Box, Flex, Text, Tooltip } from '../../../../';
3
- import { VoterList } from './VoterList';
4
-
5
- // Shows number of votes, avatars of voters and list of voters on hover
6
- export const Votes = ({ voters }) => {
7
- const hiddenVotersCount = voters.length > 2 ? voters.length - 2 : 0;
8
-
9
- return (
10
- <Flex align="center" css={{ gap: '$4' }}>
11
- <Text variant="sm" css={{ color: '$on_surface_medium' }}>
12
- {voters.length}&nbsp;
13
- {voters.length && voters.length !== 1 ? 'votes' : 'votes'}
14
- </Text>
15
- <Tooltip
16
- side="bottom"
17
- align="start"
18
- disabled={hiddenVotersCount === 0}
19
- boxCss={{
20
- backgroundColor: '$surface_brighter',
21
- borderRadius: '$1',
22
- p: '$4 $6',
23
- top: '$2',
24
- zIndex: '20',
25
- minWidth: '$44',
26
- }}
27
- title={<VoterList voters={voters} />}
28
- >
29
- <Flex align="center">
30
- {voters.length
31
- ? voters.slice(0, 2).map((voter, index) => (
32
- <Avatar
33
- name={voter}
34
- css={{
35
- position: 'relative',
36
- transform: 'unset',
37
- left: `${index * -1}px`,
38
- fontSize: '$tiny',
39
- size: '$9',
40
- p: '$4',
41
- zIndex: '5',
42
- }}
43
- />
44
- ))
45
- : null}
46
- {hiddenVotersCount ? (
47
- <Box
48
- css={{
49
- backgroundColor: '$secondary_default',
50
- borderRadius: '$round',
51
- position: 'relative',
52
- left: '-$2',
53
- p: '$2 $3',
54
- }}
55
- >
56
- <Text
57
- variant="caption"
58
- css={{
59
- fontWeight: '$semiBold',
60
- color: '$on_surface_high',
61
- fontSize: '$tiny',
62
- }}
63
- >
64
- +{hiddenVotersCount}
65
- </Text>
66
- </Box>
67
- ) : null}
68
- </Flex>
69
- </Tooltip>
70
- </Flex>
71
- );
72
- };
@@ -1,40 +0,0 @@
1
- import React from 'react';
2
- import { selectPeers, selectRoomID, useHMSStore } from '@100mslive/react-sdk';
3
- import { SecondaryTiles } from '../components/SecondaryTiles';
4
- import { ProminenceLayout } from '../components/VideoLayouts/ProminenceLayout';
5
- import { Box } from '../../Layout';
6
- // import { Whiteboard } from '../plugins/whiteboard';
7
-
8
- const Editor = React.memo(() => {
9
- return (
10
- <Box
11
- css={{
12
- mx: '$4',
13
- flex: '3 1 0',
14
- '@lg': {
15
- flex: '2 1 0',
16
- '& video': {
17
- objectFit: 'contain',
18
- },
19
- },
20
- }}
21
- >
22
- <Box css={{ position: 'relative', width: '100%', height: '100%' }}>{/* <Whiteboard roomId={roomId} /> */}</Box>
23
- </Box>
24
- );
25
- });
26
-
27
- const WhiteboardView = () => {
28
- const peers = useHMSStore(selectPeers);
29
- const roomId = useHMSStore(selectRoomID);
30
- return (
31
- <ProminenceLayout.Root>
32
- <ProminenceLayout.ProminentSection>
33
- <Editor roomId={roomId} />
34
- </ProminenceLayout.ProminentSection>
35
- <SecondaryTiles peers={peers} />
36
- </ProminenceLayout.Root>
37
- );
38
- };
39
-
40
- export default WhiteboardView;
@@ -1,110 +0,0 @@
1
- // @ts-check
2
- import Pusher from 'pusher-js';
3
-
4
- const stringifyWithNull = obj => JSON.stringify(obj, (k, v) => (v === undefined ? null : v));
5
-
6
- /**
7
- * On whiteboard close, owner sends current state to remote peers.
8
- * Remote peers tear down too quickly(unsubscribing listeners) and are unable to store the last state.
9
- *
10
- * Hack: To overcome this, attach 2 listeners:
11
- * one for storing the message(won't be unsubscribed),
12
- * one for calling the actual whiteboard callback(will be unsubscribed on whiteboard close)
13
- *
14
- * This way the last state is always received and stored
15
- */
16
-
17
- /**
18
- * Base class which can be extended to use various realtime communication services.
19
- * Methods to broadcast and subscribe to events.
20
- *
21
- * Stores the last message received/broadcasted to resend when required(when board is ready)
22
- */
23
-
24
- class PusherCommunicationProvider {
25
- constructor() {
26
- /** @private */
27
- this.initialized = false;
28
- /** @private */
29
- this.lastMessage = {};
30
- }
31
-
32
- init = (roomId = '') => {
33
- if (this.initialized) {
34
- return;
35
- }
36
-
37
- /** @private */
38
- this.pusher = new Pusher(process.env.REACT_APP_PUSHER_APP_KEY, {
39
- cluster: process.env.REACT_APP_PUSHER_CLUSTER || 'ap2',
40
- authEndpoint: process.env.REACT_APP_PUSHER_AUTHENDPOINT,
41
- });
42
-
43
- // Pusher.default.logToConsole = true;
44
-
45
- /** @private */
46
- this.channel = this.pusher.subscribe(`private-${roomId}`);
47
-
48
- /**
49
- * When events(peer-join) are sent too early before subscribing to a channel,
50
- * resend last event after subscription has succeeded.
51
- */
52
- this.channel.bind('pusher:subscription_succeeded', this.resendLastEvents);
53
-
54
- console.log('Whiteboard initialized communication through Pusher');
55
- this.initialized = true;
56
- };
57
-
58
- /**
59
- * @param {string} eventName
60
- * @param {any} message
61
- */
62
- storeEvent = (eventName, message) => {
63
- this.lastMessage[eventName] = { eventName, ...message };
64
- };
65
-
66
- /**
67
- * @param {string} eventName
68
- * @returns {any}
69
- */
70
- getStoredEvent = eventName => {
71
- return this.lastMessage[eventName];
72
- };
73
-
74
- /**
75
- * @param {string} eventName
76
- * @param {Object} arg
77
- */
78
- broadcastEvent = (eventName, arg = {}) => {
79
- this.storeEvent(eventName, arg);
80
-
81
- this.channel?.trigger(`client-${eventName}`, stringifyWithNull({ eventName, ...arg }));
82
- };
83
-
84
- /**
85
- *
86
- * @param {string} eventName
87
- * @param {Function} cb
88
- */
89
- subscribe = (eventName, cb) => {
90
- this.channel?.bind(`client-${eventName}`, message => this.storeEvent(eventName, message));
91
- this.channel?.bind(`client-${eventName}`, cb);
92
- return () => {
93
- this.channel?.unbind(`client-${eventName}`, cb);
94
- };
95
- };
96
-
97
- /** @private */
98
- resendLastEvents = () => {
99
- for (const eventName in this.lastMessage) {
100
- if (this.lastMessage[eventName]) {
101
- this.channel?.trigger(`client-${eventName}`, this.lastMessage[eventName]);
102
- }
103
- }
104
- };
105
- }
106
-
107
- /**
108
- * @type {PusherCommunicationProvider}
109
- */
110
- export const provider = new PusherCommunicationProvider();
@@ -1,29 +0,0 @@
1
- # Collaborative Whiteboard setup
2
-
3
- ## Pusher Account
4
-
5
- - Create an Pusher account at https://pusher.com/.
6
- - Create a new channels app.
7
- - Go to the “App Keys” page for that app(apps list view at https://dashboard.pusher.com/apps), and make a note of your app_id, key, secret and cluster.
8
- - Go to the "App Settings" page and enable client events.
9
-
10
- ## Whiteboard Server
11
-
12
- - Fork the whiteboard pusher server from https://github.com/100mslive/whiteboard-server and deploy it using your preferred hosting provider.
13
- - Add the pusher keys noted earlier to environment variables.
14
-
15
- ```
16
- APP_CLUSTER="cluster"
17
- APP_SECRET="secret"
18
- APP_KEY="key"
19
- APP_ID="app_id"
20
- ```
21
-
22
- - The API path is `api/pusher/auth`, say your deployment URL is `whiteboard-server.vercel.app`, the Pusher Auth Endpoint is `https://whiteboard-server.vercel.app/api/pusher/auth`
23
-
24
- ## Whiteboard Client
25
-
26
- - Copy the whole folder at `/src/plugins/whiteboard` into your live video conferencing apps using 100ms' SDKs.
27
- - Add the pusher app key, auth endpoint and cluster to `REACT_APP_PUSHER_APP_KEY`, `REACT_APP_PUSHER_AUTHENDPOINT`, `REACT_APP_PUSHER_CLUSTER` environment variables.
28
- - The `useWhiteboardMetadata` hook returns state such as the whiteboard owner(`whiteboardOwner`) and action to toggle the whiteboard(`toggleWhiteboard`). Refer usage in `ToggleWhiteboard.jsx` - an icon button to toggle the whiteboard based on the active state.
29
- - When the whiteboard is active(`whiteboardOwner` from `useWhiteboardMetadata` is not null), render the `Whiteboard` component on your UI to let your users draw on the whiteboard. Refer `mainView.jsx` and `WhiteboardView.jsx`.
@@ -1,37 +0,0 @@
1
- import React from 'react';
2
- import { ConferencingScreen } from '@100mslive/types-prebuilt';
3
- import { PencilDrawIcon } from '@100mslive/react-icons';
4
- import { Tooltip } from '../../../Tooltip';
5
- // @ts-ignore: No implicit any
6
- import IconButton from '../../IconButton';
7
- // @ts-ignore: No implicit any
8
- import { useWhiteboardMetadata } from './useWhiteboardMetadata';
9
-
10
- export const ToggleWhiteboard = ({ screenType }: { screenType: keyof ConferencingScreen }) => {
11
- const {
12
- whiteboardEnabled,
13
- whiteboardOwner: whiteboardActive,
14
- amIWhiteboardOwner,
15
- toggleWhiteboard,
16
- } = useWhiteboardMetadata();
17
-
18
- if (!whiteboardEnabled || screenType === 'hls_live_streaming') {
19
- return null;
20
- }
21
-
22
- return (
23
- <Tooltip
24
- title={whiteboardActive ? (amIWhiteboardOwner ? `Stop whiteboard` : `Can't stop whiteboard`) : 'Start whiteboard'}
25
- key="whiteboard"
26
- >
27
- <IconButton
28
- onClick={toggleWhiteboard}
29
- active={!whiteboardActive}
30
- disabled={whiteboardActive && !amIWhiteboardOwner}
31
- data-testid="white_board_btn"
32
- >
33
- <PencilDrawIcon />
34
- </IconButton>
35
- </Tooltip>
36
- );
37
- };
@@ -1,12 +0,0 @@
1
- /* Hide undo and redo buttons */
2
- .c-bUEyht-jvfJsl-side-right > button:nth-child(1) {
3
- display: none;
4
- }
5
-
6
- .c-bUEyht-jvfJsl-side-right > button:nth-child(2) {
7
- display: none;
8
- }
9
-
10
- #TD-PrimaryTools-Image {
11
- display: none;
12
- }
@@ -1,11 +0,0 @@
1
- import React from 'react';
2
- import { Tldraw } from '@tldraw/tldraw';
3
- import { useMultiplayerState } from './useMultiplayerState';
4
- import './Whiteboard.css';
5
-
6
- export const Whiteboard = React.memo(({ roomId }) => {
7
- const events = useMultiplayerState(roomId);
8
- return (
9
- <Tldraw autofocus disableAssets={true} showSponsorLink={false} showPages={false} showMenu={false} {...events} />
10
- );
11
- });
@@ -1,8 +0,0 @@
1
- export const WhiteboardEvents = {
2
- // To broadcast new changes made in whiteboard
3
- STATE_CHANGE: 'state-change',
4
- // To broadcast the current whole state of the board by the owner
5
- CURRENT_STATE: 'current-state',
6
- // For newly joined peers to request current state from owner
7
- REQUEST_STATE: 'request-state',
8
- };
@@ -1,3 +0,0 @@
1
- // export { Whiteboard } from './Whiteboard';
2
- export { ToggleWhiteboard } from './ToggleWhiteboard';
3
- export { useWhiteboardMetadata } from './useWhiteboardMetadata';
@@ -1,212 +0,0 @@
1
- // @ts-check
2
- import { useCallback, useEffect, useRef, useState } from 'react';
3
- import { selectDidIJoinWithin, useHMSStore } from '@100mslive/react-sdk';
4
- import { provider as room } from './PusherCommunicationProvider';
5
- import { WhiteboardEvents as Events } from './WhiteboardEvents';
6
- import { useWhiteboardMetadata } from './useWhiteboardMetadata';
7
-
8
- const useWhiteboardState = () => {
9
- const { amIWhiteboardOwner } = useWhiteboardMetadata();
10
- /*
11
- * LIVE-1470 state need to have some delay after join.
12
- * It will initialize pusher room and send request state.
13
- */
14
- const shouldRequestState = useHMSStore(selectDidIJoinWithin(2000));
15
-
16
- return { shouldRequestState, amIWhiteboardOwner };
17
- };
18
-
19
- /**
20
- * Ref: https://github.com/tldraw/tldraw/blob/main/apps/www/hooks/useMultiplayerState.ts
21
- */
22
- export function useMultiplayerState(roomId) {
23
- const [app, setApp] = useState(null);
24
- const [isReady, setIsReady] = useState(false);
25
- const { amIWhiteboardOwner, shouldRequestState } = useWhiteboardState();
26
-
27
- /**
28
- * Stores current state(shapes, bindings, [assets]) of the whiteboard
29
- */
30
- const rLiveShapes = useRef(new Map());
31
- const rLiveBindings = useRef(new Map());
32
-
33
- const getCurrentState = useCallback(() => {
34
- return {
35
- shapes: rLiveShapes.current ? Object.fromEntries(rLiveShapes.current) : {},
36
- bindings: rLiveBindings.current ? Object.fromEntries(rLiveBindings.current) : {},
37
- };
38
- }, []);
39
-
40
- const sendCurrentState = useCallback(() => {
41
- if (amIWhiteboardOwner && isReady) {
42
- room.broadcastEvent(Events.CURRENT_STATE, getCurrentState());
43
- }
44
- }, [amIWhiteboardOwner, isReady, getCurrentState]);
45
-
46
- const updateLocalState = useCallback(({ shapes, bindings, merge = true }) => {
47
- if (!(shapes && bindings)) {
48
- return;
49
- }
50
-
51
- if (merge) {
52
- const lShapes = rLiveShapes.current;
53
- const lBindings = rLiveBindings.current;
54
-
55
- if (!(lShapes && lBindings)) {
56
- return;
57
- }
58
- Object.entries(shapes).forEach(([id, shape]) => {
59
- if (!shape) {
60
- lShapes.delete(id);
61
- } else {
62
- lShapes.set(shape.id, shape);
63
- }
64
- });
65
-
66
- Object.entries(bindings).forEach(([id, binding]) => {
67
- if (!binding) {
68
- lBindings.delete(id);
69
- } else {
70
- lBindings.set(binding.id, binding);
71
- }
72
- });
73
- } else {
74
- rLiveShapes.current = new Map(Object.entries(shapes));
75
- rLiveBindings.current = new Map(Object.entries(bindings));
76
- }
77
- }, []);
78
-
79
- const applyStateToBoard = useCallback(
80
- state => {
81
- app === null || app === void 0
82
- ? void 0
83
- : // eslint-disable-next-line @typescript-eslint/ban-ts-comment
84
- // @ts-ignore
85
- app.replacePageContent(
86
- state.shapes,
87
- state.bindings,
88
- {}, // Object.fromEntries(lAssets.entries())
89
- );
90
- },
91
- [app],
92
- );
93
-
94
- const handleChanges = useCallback(
95
- state => {
96
- if (!state) {
97
- return;
98
- }
99
-
100
- const { shapes, bindings, eventName } = state;
101
- updateLocalState({
102
- shapes,
103
- bindings,
104
- merge: eventName === Events.STATE_CHANGE,
105
- });
106
- applyStateToBoard(getCurrentState());
107
- },
108
- [applyStateToBoard, getCurrentState, updateLocalState],
109
- );
110
-
111
- const setupInitialState = useCallback(() => {
112
- if (!isReady) {
113
- return;
114
- }
115
-
116
- if (amIWhiteboardOwner) {
117
- // On board open, update the document with initial/stored content
118
- handleChanges(room.getStoredEvent(Events.CURRENT_STATE));
119
- // Send current state to other peers in the room currently
120
- sendCurrentState();
121
- } else if (shouldRequestState) {
122
- /**
123
- * Newly joined peers request the owner for current state
124
- * and update their boards when they receive it
125
- */
126
- room.broadcastEvent(Events.REQUEST_STATE);
127
- }
128
- }, [isReady, amIWhiteboardOwner, shouldRequestState, handleChanges, sendCurrentState]);
129
-
130
- // Callbacks --------------
131
- // Put the state into the window, for debugging.
132
- const onMount = useCallback(
133
- app => {
134
- app.loadRoom(roomId);
135
- app.pause(); // Turn off the app's own undo / redo stack
136
- // window.app = app;
137
- setApp(app);
138
- },
139
- [roomId],
140
- );
141
-
142
- // Update the live shapes when the app's shapes change.
143
- const onChangePage = useCallback(
144
- (_app, shapes, bindings) => {
145
- updateLocalState({ shapes, bindings });
146
- room.broadcastEvent(Events.STATE_CHANGE, { shapes, bindings });
147
-
148
- /**
149
- * Tldraw thinks that the next update passed to replacePageContent after onChangePage is the own update triggered by onChangePage
150
- * and the replacePageContent doesn't have any effect if it is a valid update from remote.
151
- *
152
- * To overcome this replacePageContent locally onChangePage(not costly - returns from first line).
153
- *
154
- * Refer: https://github.com/tldraw/tldraw/blob/main/packages/tldraw/src/state/TldrawApp.ts#L684
155
- */
156
- applyStateToBoard(getCurrentState());
157
- },
158
- [updateLocalState, applyStateToBoard, getCurrentState],
159
- );
160
-
161
- // Handle presence updates when the user's pointer / selection changes
162
- // const onChangePresence = useCallback((app, user) => {
163
- // updateMyPresence({ id: app.room?.userId, user });
164
- // }, [][updateMyPresence]);
165
-
166
- // Subscriptions and initial setup
167
- useEffect(() => {
168
- if (!app) {
169
- return;
170
- }
171
- const unsubs = [];
172
-
173
- let stillAlive = true;
174
-
175
- // Setup the document's storage and subscriptions
176
- function setupDocument() {
177
- // Subscribe to changes
178
- if (stillAlive) {
179
- unsubs.push(room.subscribe(Events.STATE_CHANGE, handleChanges));
180
- unsubs.push(room.subscribe(Events.CURRENT_STATE, handleChanges));
181
-
182
- // On request state(peer join), send whole current state to update the new peer's whiteboard
183
- unsubs.push(room.subscribe(Events.REQUEST_STATE, sendCurrentState));
184
-
185
- setIsReady(true);
186
- }
187
- }
188
-
189
- room.init(roomId);
190
- setupDocument();
191
- setupInitialState();
192
-
193
- return () => {
194
- stillAlive = false;
195
- unsubs.forEach(unsub => unsub());
196
- };
197
- }, [app, roomId, setupInitialState, sendCurrentState, handleChanges]);
198
-
199
- useEffect(() => {
200
- // Store last state on closing whitboard so that when the board is reopened the state could be fetched and reapplied
201
- const handleUnmount = () => {
202
- if (isReady && !shouldRequestState) {
203
- console.log('Whiteboard unmount storing', getCurrentState());
204
- room.storeEvent(Events.CURRENT_STATE, getCurrentState());
205
- }
206
- };
207
-
208
- return handleUnmount;
209
- }, [isReady, shouldRequestState, getCurrentState]);
210
-
211
- return { onMount, onChangePage };
212
- }
@@ -1,47 +0,0 @@
1
- import { useCallback, useEffect, useMemo } from 'react';
2
- import { selectLocalPeerID, selectPeerByCondition, useHMSStore } from '@100mslive/react-sdk';
3
- import { useMyMetadata } from '../../components/hooks/useMetadata';
4
- import { getMetadata } from '../../common/utils';
5
- import { FeatureFlags } from '../../services/FeatureFlags';
6
-
7
- const isWhiteboardOwner = peer => {
8
- return !!getMetadata(peer?.metadata).whiteboardOwner;
9
- };
10
-
11
- export const useWhiteboardMetadata = () => {
12
- const localPeerID = useHMSStore(selectLocalPeerID);
13
- const { updateMetaData } = useMyMetadata();
14
- const whiteboardOwner = useHMSStore(selectPeerByCondition(isWhiteboardOwner));
15
- const amIWhiteboardOwner = useMemo(() => localPeerID === whiteboardOwner?.id, [localPeerID, whiteboardOwner]);
16
-
17
- /**
18
- * @param enabled {boolean}
19
- */
20
- const toggleWhiteboard = useCallback(async () => {
21
- if (!process.env.REACT_APP_PUSHER_APP_KEY) {
22
- console.error('Cannot start whiteboard - Pusher Key unavailable');
23
- }
24
- try {
25
- if (!whiteboardOwner || amIWhiteboardOwner) {
26
- await updateMetaData({ whiteboardOwner: !whiteboardOwner });
27
- } else {
28
- console.warn('Cannot toggle whiteboard as it was shared by another peer');
29
- }
30
- } catch (error) {
31
- console.error('failed to toggle whiteboard to ', !whiteboardOwner, error);
32
- }
33
- }, [whiteboardOwner, updateMetaData, amIWhiteboardOwner]);
34
-
35
- useEffect(() => {
36
- window.toggleWhiteboard = toggleWhiteboard;
37
- }, [toggleWhiteboard]);
38
-
39
- return {
40
- /** is whiteboard enabled for the room */
41
- whiteboardEnabled: FeatureFlags.enableWhiteboard,
42
- /** owner of the active whiteboard, can also be used to check if whiteboard is active */
43
- whiteboardOwner,
44
- amIWhiteboardOwner,
45
- toggleWhiteboard,
46
- };
47
- };