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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. package/README.md +27 -2
  2. package/dist/{HLSView-BWR4T6PI.js → HLSView-ULB4DC6B.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-SYBH2G3R.js → chunk-GVA4I77Z.js} +2802 -2740
  24. package/dist/chunk-GVA4I77Z.js.map +7 -0
  25. package/dist/index.cjs.js +3035 -2967
  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 +476 -394
  30. package/dist/meta.esbuild.json +486 -402
  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/components/Chat/ChatActions.tsx +25 -8
  35. package/src/Prebuilt/components/Chat/ChatBody.tsx +64 -21
  36. package/src/Prebuilt/components/Chat/ChatFooter.tsx +1 -0
  37. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +2 -2
  38. package/src/Prebuilt/components/Header/AdditionalRoomState.jsx +1 -38
  39. package/src/Prebuilt/components/Header/StreamActions.tsx +1 -1
  40. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +11 -1
  41. package/src/Prebuilt/components/Polls/CreateQuestions/{DeleteQuestionModal.jsx → DeleteQuestionModal.tsx} +9 -1
  42. package/src/Prebuilt/components/Polls/CreateQuestions/{QuestionForm.jsx → QuestionForm.tsx} +71 -30
  43. package/src/Prebuilt/components/Polls/CreateQuestions/{SavedQuestion.jsx → SavedQuestion.tsx} +24 -15
  44. package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +1 -1
  45. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +61 -80
  46. package/src/Prebuilt/components/Polls/Voting/{StandardVoting.jsx → StandardVoting.tsx} +3 -7
  47. package/src/Prebuilt/components/Polls/Voting/{TimedVoting.jsx → TimedVoting.tsx} +4 -7
  48. package/src/Prebuilt/components/Polls/Voting/{Voting.jsx → Voting.tsx} +4 -3
  49. package/src/Prebuilt/components/Polls/common/Line.tsx +4 -0
  50. package/src/Prebuilt/components/Polls/common/{OptionInputWithDelete.jsx → OptionInputWithDelete.tsx} +14 -2
  51. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +1 -1
  52. package/src/Prebuilt/components/Polls/common/{StatusIndicator.jsx → StatusIndicator.tsx} +1 -2
  53. package/src/Prebuilt/components/Polls/common/{VoteCount.jsx → VoteCount.tsx} +1 -2
  54. package/src/Prebuilt/components/Polls/common/{VoteProgress.jsx → VoteProgress.tsx} +3 -2
  55. package/src/Prebuilt/components/Polls/common/{VoterList.jsx → VoterList.tsx} +1 -1
  56. package/src/Prebuilt/components/TileMenu/TileMenu.jsx +3 -1
  57. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +15 -3
  58. package/src/Prebuilt/components/TileMenu/utils.ts +7 -0
  59. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +7 -3
  60. package/src/Prebuilt/components/VideoTile.jsx +2 -4
  61. package/src/Prebuilt/components/hooks/usePinnedBy.tsx +22 -0
  62. package/src/Prebuilt/components/hooks/{useSetPinnedMessages.ts → usePinnedMessages.ts} +2 -2
  63. package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +1 -4
  64. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +0 -1
  65. package/src/TextArea/TextArea.tsx +30 -0
  66. package/src/TextArea/index.tsx +1 -0
  67. package/src/index.ts +1 -0
  68. package/src/store/StorybookSDK.ts +3 -1
  69. package/dist/Prebuilt/plugins/whiteboard/ToggleWhiteboard.d.ts +0 -5
  70. package/dist/chunk-SYBH2G3R.js.map +0 -7
  71. package/src/Prebuilt/components/Polls/common/Votes.jsx +0 -72
  72. package/src/Prebuilt/layouts/WhiteboardView.jsx +0 -40
  73. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +0 -110
  74. package/src/Prebuilt/plugins/whiteboard/README.md +0 -29
  75. package/src/Prebuilt/plugins/whiteboard/ToggleWhiteboard.tsx +0 -37
  76. package/src/Prebuilt/plugins/whiteboard/Whiteboard.css +0 -12
  77. package/src/Prebuilt/plugins/whiteboard/Whiteboard.jsx +0 -11
  78. package/src/Prebuilt/plugins/whiteboard/WhiteboardEvents.js +0 -8
  79. package/src/Prebuilt/plugins/whiteboard/index.js +0 -3
  80. package/src/Prebuilt/plugins/whiteboard/useMultiplayerState.js +0 -212
  81. package/src/Prebuilt/plugins/whiteboard/useWhiteboardMetadata.js +0 -47
  82. /package/dist/{HLSView-BWR4T6PI.js.map → HLSView-ULB4DC6B.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
- };