Skip to content

Commit

Permalink
PF-1660 - Use timer as stable and feature check methods (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
fredcido authored Dec 5, 2023
1 parent 110a35d commit 8b47832
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Participant, Room } from "../../types";
import { Frame, Json, OnlineUserInfo } from "@mirohq/websdk-types";
import {
useBreakout,
useFeatureCheck,
useOnlineUsers,
useSelectedItems,
useTimer,
Expand All @@ -29,16 +30,21 @@ export const BreakoutManager: React.FC = () => {
const [selectedRoom, setSelectedRoom] = React.useState<Room>();
const [duration, setTimerDuration] = React.useState<number>();
const [currentTime, setCurrentTime] = React.useState<number>(0);

Check warning on line 32 in examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx

View workflow job for this annotation

GitHub Actions / Run linters

'currentTime' is assigned a value but never used
const [canUseTimer] = React.useState<boolean>(false);
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: (timestamp) => setCurrentTime(timestamp),
onTick,
});

const participantIds = rooms
Expand Down Expand Up @@ -256,7 +262,7 @@ export const BreakoutManager: React.FC = () => {
>
Stop session
{canUseTimer && timer.state === "started"
? `(${formatDisplayTime(currentTime)})`
? ` (${formatDisplayTime(timer.restDuration)})`
: null}
</Button>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const RoomConfig: React.FunctionComponent<Props> = ({
{room.participants.length ? (
<div className="avatars">
{room.participants.map((participant) => (
<Avatar user={participant} />
<Avatar key={participant.id} user={participant} />
))}
</div>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
.breakout-controls {
width: 100%;
display: flex;
justify-content: space-between;
justify-content: flex-start;
gap: 1em;
align-items: center;
}

.breakout-controls .facilitator-controls {
margin-left: auto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,27 @@ export const RoomsManager: React.FC<Props> = ({
)}

{isFacilitator && (
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconDotsThreeVertical />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<>
<div className="facilitator-controls">
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<IconButton
label="Facilitator controls"
variant="outline"
css={{ borderRadius: "100%" }}
>
<IconDotsThreeVertical />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item onClick={() => onReleaseFacilitator()}>
<DropdownMenu.IconSlot>
<IconHandFilled />
</DropdownMenu.IconSlot>
Release facilitator role
</DropdownMenu.Item>
</>
</DropdownMenu.Content>
</DropdownMenu>
</DropdownMenu.Content>
</DropdownMenu>
</div>
)}
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions examples/breakout-rooms/src/components/WaitingIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export const WaitingIcon = () => {
>
<path
fill="#AFB1BB"
fill-rule="evenodd"
fillRule="evenodd"
d="M5.264 4.167a2.5 2.5 0 0 0-2.5 2.5v11.666a2.5 2.5 0 0 0 2.5 2.5H16.93a2.5 2.5 0 0 0 2.5-2.5V6.667a2.5 2.5 0 0 0-2.5-2.5H5.264ZM6.097 17.5v-10h10v10h-10ZM24.764 9.334a2.5 2.5 0 0 0-2.5 2.5v8.333a2.5 2.5 0 0 0 2.5 2.5h8.333a2.5 2.5 0 0 0 2.5-2.5v-8.334a2.5 2.5 0 0 0-2.5-2.5h-8.333Zm.669 10.166v-7h7v7h-7ZM11.098 23.333a2.5 2.5 0 0 0-2.5 2.5v7.346a2.5 2.5 0 0 0 2.5 2.5h9.138a2.5 2.5 0 0 0 2.5-2.5v-7.346a2.5 2.5 0 0 0-2.5-2.5h-9.138Zm.585 9.267v-6.183h7.967V32.6h-7.967ZM28.068 25.167a2.5 2.5 0 0 0-2.5 2.5v6.666a2.5 2.5 0 0 0 2.5 2.5h6.667a2.5 2.5 0 0 0 2.5-2.5v-6.666a2.5 2.5 0 0 0-2.5-2.5h-6.667Zm.415 8.75v-5.834h5.766v5.834h-5.766Z"
clip-rule="evenodd"
clipRule="evenodd"
/>
</svg>
);
Expand Down
65 changes: 46 additions & 19 deletions examples/breakout-rooms/src/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TimerEvent,
UserInfo,
Session,
BoardFeature,
} from "@mirohq/websdk-types";

import {
Expand Down Expand Up @@ -476,33 +477,34 @@ export const useBreakout = () => {

export const useTimer = (opts: TimerOpts) => {
const [state, setState] = React.useState<TimerState>("idle");
const [restDuration, setRestDuration] = React.useState<number>(opts.duration);
const interval = React.useRef<ReturnType<typeof setInterval>>();

const tick = convertTime(opts.interval ?? 1_000, "milliseconds");

const start = React.useCallback(async () => {
const isStarted = await miro.board.experimental.timer.isStarted();
const isStarted = await miro.board.timer.isStarted();
if (isStarted) {
throw new Error("Timer is already running");
}

log("[TIMER:START]", { opts });
await miro.board.experimental.timer.start(opts.duration);
await miro.board.timer.start(opts.duration);
}, [miro, opts.duration]);

const pause = React.useCallback(async () => {
const isStarted = await miro.board.experimental.timer.isStarted();
const isStarted = await miro.board.timer.isStarted();
if (isStarted) {
await miro.board.experimental.timer.pause();
await miro.board.timer.pause();
} else {
throw new Error("Timer is not running");
}
}, [miro]);

const stop = React.useCallback(async () => {
const isStarted = await miro.board.experimental.timer.isStarted();
const isStarted = await miro.board.timer.isStarted();
if (isStarted) {
await miro.board.experimental.timer.stop();
await miro.board.timer.stop();
}
}, [miro]);

Expand All @@ -511,7 +513,7 @@ export const useTimer = (opts: TimerOpts) => {
setState("started");
opts.onStart?.();

let timeStart = timer.startedAt;
let timeStart = Date.now();
const timeEnd =
timeStart + convertTime(timer.restDuration, "milliseconds");

Expand All @@ -520,8 +522,9 @@ export const useTimer = (opts: TimerOpts) => {
timeStart,
timeEnd,
opts,
startFormatted: new Date(timeStart).toTimeString(),
endFormatted: new Date(timeEnd).toTimeString(),
duration: formatDisplayTime(timer.restDuration),
startFormatted: formatDisplayTime(timeStart),
endFormatted: formatDisplayTime(timeEnd),
});

clearInterval(interval.current);
Expand All @@ -544,6 +547,7 @@ export const useTimer = (opts: TimerOpts) => {
}

opts.onTick?.(restDuration);
setRestDuration(restDuration);
}, tick);
},
[opts.onStart, opts.onTick, stop],
Expand All @@ -556,16 +560,18 @@ export const useTimer = (opts: TimerOpts) => {
setState("ended");
opts.onStop?.();
},
[interval, interval, opts.onStop],
[interval, opts.onStop],
);

const handleTimerUpdate = React.useCallback(async (event: TimerEvent) => {
log("[TIMER:UPDATED]", { event });
switch (event.timer.status) {
case "STARTED":
setState("started");
handleTimerStart(event);
break;
case "PAUSED":
clearInterval(interval.current);
setState("paused");
break;
case "STOPPED":
Expand All @@ -576,30 +582,51 @@ export const useTimer = (opts: TimerOpts) => {

React.useEffect(() => {
const fetchCurrent = async () => {
const isStarted = await miro.board.experimental.timer.isStarted();
log("[TIMER:CURRENT]", { isStarted });
setState(isStarted ? "started" : "idle");
const currentState = await miro.board.timer.get();
log("[TIMER:CURRENT]", { currentState });

if (currentState.status === "STARTED") {
handleTimerStart({ timer: currentState });
setState("started");
}

setRestDuration(currentState.restDuration);
};

fetchCurrent();
}, []);

React.useEffect(() => {
miro.board.ui.on("experimental:timer:start", handleTimerStart);
miro.board.ui.on("experimental:timer:finish", handleTimerFinish);
miro.board.ui.on("experimental:timer:update", handleTimerUpdate);
miro.board.timer.on("start", handleTimerStart);
miro.board.timer.on("finish", handleTimerFinish);
miro.board.timer.on("update", handleTimerUpdate);

return () => {
miro.board.ui.off("experimental:timer:start", handleTimerStart);
miro.board.ui.off("experimental:timer:finish", handleTimerFinish);
miro.board.ui.off("experimental:timer:update", handleTimerUpdate);
miro.board.timer.off("start", handleTimerStart);
miro.board.timer.off("finish", handleTimerFinish);
miro.board.timer.off("update", handleTimerUpdate);
};
}, [handleTimerStart, handleTimerFinish, handleTimerUpdate]);

return {
state,
restDuration,
start,
stop,
pause,
};
};

export const useFeatureCheck = (feature: BoardFeature): boolean => {
const [canUse, setCanUse] = React.useState<boolean>(false);

React.useEffect(() => {
const fetch = async () => {
const canIUse = await miro.board.canUse(feature);
setCanUse(canIUse);
};
fetch();
}, [setCanUse, feature]);

return canUse;
};
4 changes: 2 additions & 2 deletions examples/breakout-rooms/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export const formatDisplayTime = (
): string => {
const timestamp = convertTime(time, "seconds", unit);

const minutes = Math.floor(timestamp / 60);
const seconds = Math.floor(timestamp % 60);
const minutes = Math.round(timestamp / 60);
const seconds = Math.round(timestamp % 60);

return [minutes, seconds]
.map((unit) => unit.toString().padStart(2, "0"))
Expand Down
Loading

2 comments on commit 8b47832

@vercel
Copy link

@vercel vercel bot commented on 8b47832 Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

webhooks-manager – ./examples/webhooks-manager

webhooks-manager-miro-web.vercel.app
webhooks-manager-sepia.vercel.app
webhooks-manager-git-main-miro-web.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 8b47832 Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

app-examples-wordle – ./examples/wordle

app-examples-wordle-anthonyroux.vercel.app
app-examples-wordle-git-main-anthonyroux.vercel.app
app-examples-wordle.vercel.app

Please sign in to comment.