diff --git a/examples/breakout-rooms/package.json b/examples/breakout-rooms/package.json index ae848ee47..e41a64c60 100644 --- a/examples/breakout-rooms/package.json +++ b/examples/breakout-rooms/package.json @@ -20,6 +20,8 @@ }, "dependencies": { "@mirohq/design-system": "^0.18.2", + "@mirohq/websdk-react-hooks": "^0.0.3", + "@mirohq/websdk-types": "2.11.0", "@stitches/react": "^1.2.8", "classnames": "^2.3.2", "mirotone": "5", diff --git a/examples/breakout-rooms/src/app.tsx b/examples/breakout-rooms/src/app.tsx index f44606687..67e5cc4d2 100644 --- a/examples/breakout-rooms/src/app.tsx +++ b/examples/breakout-rooms/src/app.tsx @@ -1,6 +1,8 @@ "use client"; import * as React from "react"; +import { MiroProvider } from "@mirohq/websdk-react-hooks"; + import { useBreakout } from "./hooks"; import { BreakoutManager } from "./components/BreakoutManager"; import { WaitingRoom } from "./components/WaitingRoom/WaitingRoom"; @@ -14,7 +16,9 @@ const App: React.FC = () => { return ( - {areYouReady ? : } + + {areYouReady ? : } + ); }; diff --git a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx index 723cc05bf..1d42d9f89 100644 --- a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx +++ b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx @@ -4,13 +4,8 @@ import * as React from "react"; import { Participant, Room } from "../../types"; import { Frame, Json, OnlineUserInfo } from "@mirohq/websdk-types"; -import { - useBreakout, - useFeatureCheck, - useOnlineUsers, - useSelectedItems, - useTimer, -} from "../../hooks"; +import { useSelectedItems, useOnlineUsers } from "@mirohq/websdk-react-hooks"; +import { useBreakout, useFeatureCheck, useTimer } from "../../hooks"; import { formatDisplayTime, isUser } from "../../utils"; import { DEFAULT_TIME } from "../Timer/Timer"; import { Button } from "@mirohq/design-system"; @@ -29,22 +24,15 @@ export const BreakoutManager: React.FC = () => { }); const [selectedRoom, setSelectedRoom] = React.useState(); const [duration, setTimerDuration] = React.useState(); - const [currentTime, setCurrentTime] = React.useState(0); const canUseTimer = useFeatureCheck("timer"); const onTimerStop = React.useCallback(() => { service.endSession(); }, [breakout?.id]); - const onTick = React.useCallback( - (timestamp: number) => setCurrentTime(timestamp), - [], - ); - const timer = useTimer({ duration: duration ?? DEFAULT_TIME, onStop: onTimerStop, - onTick, }); const participantIds = rooms @@ -54,7 +42,11 @@ export const BreakoutManager: React.FC = () => { .join("-"); const unassignedUsers = React.useMemo(() => { - return onlineUsers.filter((user) => + if (onlineUsers.status !== "success") { + return []; + } + + return onlineUsers.result.filter((user) => rooms.every((room) => room.participants.every((participant) => participant.id !== user.id), ), @@ -63,11 +55,15 @@ export const BreakoutManager: React.FC = () => { React.useEffect(() => { const handleSelectionUpdate = async () => { - if (!selectedItems.length || !selectedRoom) { + if ( + selectedItems.status !== "success" || + !selectedItems.result.length || + !selectedRoom + ) { return; } - const [frame] = selectedItems; + const [frame] = selectedItems.result; if (frame) { await service.setRoomTarget(selectedRoom, frame.id); diff --git a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx index 17f448920..6ab4d318a 100644 --- a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx +++ b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx @@ -80,6 +80,11 @@ export const RoomConfig: React.FunctionComponent = ({ + {unassignedUsers.length < 1 && room.participants.length < 1 && ( + + No more users left to assign + + )} {unassignedUsers.length ? (
diff --git a/examples/breakout-rooms/src/hooks.tsx b/examples/breakout-rooms/src/hooks.tsx index 6d98c7530..9f523a81f 100644 --- a/examples/breakout-rooms/src/hooks.tsx +++ b/examples/breakout-rooms/src/hooks.tsx @@ -1,9 +1,7 @@ import * as React from "react"; import { - Item, OnlineUserInfo, - SelectionUpdateEvent, TimerEvent, UserInfo, Session, @@ -14,7 +12,6 @@ import { Breakout, Participant, Room, - SelectItemsOpts, TimerOpts, TimerState, UserSessionEvent, @@ -42,55 +39,6 @@ export const useCurrentUser = () => { return userInfo; }; -export const useOnlineUsers = () => { - const [onlineUsers, setOnlineUsers] = React.useState([]); - - React.useEffect(() => { - const fetch = async () => { - const users = await miro.board.getOnlineUsers(); - setOnlineUsers(users); - }; - - miro.board.ui.on("online_users:update", fetch); - - fetch(); - - return () => { - miro.board.ui.off("online_users:update", fetch); - }; - }, []); - - return onlineUsers; -}; - -export const useSelectedItems = ( - opts?: SelectItemsOpts, -) => { - const [items, setItems] = React.useState([]); - - React.useEffect(() => { - const subscribe = () => { - const handleSelectionUpdate = async (event: SelectionUpdateEvent) => { - let items = event.items as T[]; - if (opts?.predicate) { - items = items.filter(opts.predicate); - } - setItems(items); - }; - - miro.board.ui.on("selection:update", handleSelectionUpdate); - - return () => { - miro.board.ui.off("selection:update", handleSelectionUpdate); - }; - }; - - return subscribe(); - }, []); - - return items; -}; - export const useBreakout = () => { const [breakout, setBreakout] = React.useState(); const currentUser = useCurrentUser(); @@ -322,9 +270,7 @@ export const useBreakout = () => { return; } - if (participant.id === currentUser?.id) { - await miro.board.viewport.zoomTo(frame); - } else { + if (participant.id !== currentUser?.id) { await miro.board.collaboration.zoomTo(participant, frame); } }; @@ -410,11 +356,21 @@ export const useBreakout = () => { if (myself) { await session.join(); + const frame = await miro.board.get({ + type: "frame", + id: room.targetId, + }); + await miro.board.viewport.zoomTo(frame); } await session.invite(everyoneElse); room.participants.map((participant) => - updateParticipant(room, participant, { state: "Invitation Pending" }), + updateParticipant(room, participant, { + state: + currentUser?.id !== participant.id + ? "Invitation Pending" + : "In Session", + }), ); session.on("user-joined", handleUserJoined); diff --git a/examples/breakout-rooms/src/types.ts b/examples/breakout-rooms/src/types.ts index b356fc1f7..506f29d67 100644 --- a/examples/breakout-rooms/src/types.ts +++ b/examples/breakout-rooms/src/types.ts @@ -45,10 +45,6 @@ export type TimerOpts = { onStart?: () => void; }; -export type SelectItemsOpts = { - predicate?: (items: Item) => boolean; -}; - export type UserSessionEvent = { userId: string; sessionId: string; diff --git a/yarn.lock b/yarn.lock index b4bae1689..3bc6b9b49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1064,10 +1064,17 @@ node-fetch "^2.6.9" typedoc "0.23.24" -"@mirohq/websdk-types@2.10.0": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@mirohq/websdk-types/-/websdk-types-2.10.0.tgz#acb7335957be62290ae651ff6c23b29b24608f7f" - integrity sha512-JMBxUBZW1Pz/NeOuoXhpPGp7DcmkIOICpoSramhNpx5OFnzLed8ECZjQt13axIRpMqQ6IKcGQGGRmOh9zYIHhw== +"@mirohq/websdk-react-hooks@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@mirohq/websdk-react-hooks/-/websdk-react-hooks-0.0.3.tgz#0a912abc273b78e115872d4dac31b83b54207f2a" + integrity sha512-pIambSGRVa+tb9v1hkj6h47ZfqEqS9NXMkvUvO70Rjxkq6s1v0c2equKapkU6JE5YdliYSDzsOjgmyd1cI5FCQ== + dependencies: + "@react-hookz/web" "^23.1.0" + +"@mirohq/websdk-types@2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@mirohq/websdk-types/-/websdk-types-2.11.0.tgz#b72113d71c6f6b3a9fe0dd66ad6bf664a3bbe22e" + integrity sha512-LTy8dUJurKL+GwNIl1ba20XbV7yc1t8irhBXUN/vDJR1G1JlMvpqR6UXghUvEAGee9XGLjxGNWtwI8iNbMbPAQ== dependencies: typescript ">=4.6.3 || ~5" @@ -2255,6 +2262,18 @@ "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" +"@react-hookz/deep-equal@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@react-hookz/deep-equal/-/deep-equal-1.0.4.tgz#68a71f36cbc88724b3ce6f4036183778b6e7f282" + integrity sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg== + +"@react-hookz/web@^23.1.0": + version "23.1.0" + resolved "https://registry.yarnpkg.com/@react-hookz/web/-/web-23.1.0.tgz#4e9bf133c56519924b4c2988aca20d09387f5e0a" + integrity sha512-fvbURdsa1ukttbLR1ASE/XmqXP09vZ1PiCYppYeR1sNMzCrdkG0iBnjxniFSVjJ8gIw2fRs6nqMTbeBz2uAkuA== + dependencies: + "@react-hookz/deep-equal" "^1.0.4" + "@react-stately/calendar@^3.4.2": version "3.4.2" resolved "https://registry.yarnpkg.com/@react-stately/calendar/-/calendar-3.4.2.tgz#7dd55cd2f0689bd0a5825326507dcb6b3d7f3d05"