mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-04-24 06:05:19 +00:00
fix: deactivate meeting button and better deactivation heuristics (#950)
This commit is contained in:
committed by
GitHub
parent
08c276e4f4
commit
26239f05a3
@@ -869,29 +869,30 @@ async def process_meetings():
|
|||||||
elif has_had_sessions:
|
elif has_had_sessions:
|
||||||
should_deactivate = True
|
should_deactivate = True
|
||||||
logger_.info("Meeting ended - all participants left")
|
logger_.info("Meeting ended - all participants left")
|
||||||
elif current_time > end_date:
|
elif not has_had_sessions:
|
||||||
should_deactivate = True
|
# No sessions recorded — either no one joined, or webhooks
|
||||||
logger_.info(
|
# didn't arrive (e.g. local dev without tunnel).
|
||||||
"Meeting deactivated - scheduled time ended with no participants",
|
|
||||||
)
|
|
||||||
elif meeting.platform == "livekit" and not has_had_sessions:
|
|
||||||
# LiveKit rooms are destroyed after empty_timeout. Once gone,
|
|
||||||
# list_participants returns [] — indistinguishable from "never used".
|
|
||||||
# Check if meeting was created >10 min ago; if so, assume room is gone.
|
|
||||||
meeting_start = meeting.start_date
|
meeting_start = meeting.start_date
|
||||||
if meeting_start.tzinfo is None:
|
if meeting_start.tzinfo is None:
|
||||||
meeting_start = meeting_start.replace(tzinfo=timezone.utc)
|
meeting_start = meeting_start.replace(tzinfo=timezone.utc)
|
||||||
age_minutes = (current_time - meeting_start).total_seconds() / 60
|
age_minutes = (current_time - meeting_start).total_seconds() / 60
|
||||||
if age_minutes > 10:
|
is_scheduled = bool(meeting.calendar_event_id)
|
||||||
|
|
||||||
|
if is_scheduled and current_time > end_date:
|
||||||
|
# Scheduled meeting past its end time with no participants
|
||||||
should_deactivate = True
|
should_deactivate = True
|
||||||
logger_.info(
|
logger_.info(
|
||||||
"LiveKit meeting deactivated - room likely destroyed (no sessions after 10 min)",
|
"Meeting deactivated - scheduled time ended with no participants",
|
||||||
|
)
|
||||||
|
elif not is_scheduled and age_minutes > 30:
|
||||||
|
# On-the-fly meeting with no sessions after 30 min
|
||||||
|
should_deactivate = True
|
||||||
|
logger_.info(
|
||||||
|
"Meeting deactivated - no sessions after 30 min",
|
||||||
age_minutes=round(age_minutes, 1),
|
age_minutes=round(age_minutes, 1),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger_.debug("LiveKit meeting still young, keep it")
|
logger_.debug("Meeting not yet started, keep it")
|
||||||
else:
|
|
||||||
logger_.debug("Meeting not yet started, keep it")
|
|
||||||
|
|
||||||
if should_deactivate:
|
if should_deactivate:
|
||||||
await meetings_controller.update_meeting(
|
await meetings_controller.update_meeting(
|
||||||
|
|||||||
@@ -10,14 +10,17 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
VStack,
|
VStack,
|
||||||
Icon,
|
Icon,
|
||||||
|
Tooltip,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { LuLink, LuRefreshCw } from "react-icons/lu";
|
import { LuLink, LuRefreshCw } from "react-icons/lu";
|
||||||
|
import { FaStop } from "react-icons/fa";
|
||||||
import { FaCalendarAlt } from "react-icons/fa";
|
import { FaCalendarAlt } from "react-icons/fa";
|
||||||
import type { components } from "../../../reflector-api";
|
import type { components } from "../../../reflector-api";
|
||||||
import {
|
import {
|
||||||
useRoomActiveMeetings,
|
useRoomActiveMeetings,
|
||||||
useRoomUpcomingMeetings,
|
useRoomUpcomingMeetings,
|
||||||
useRoomIcsSync,
|
useRoomIcsSync,
|
||||||
|
useMeetingDeactivate,
|
||||||
} from "../../../lib/apiHooks";
|
} from "../../../lib/apiHooks";
|
||||||
|
|
||||||
type Room = components["schemas"]["Room"];
|
type Room = components["schemas"]["Room"];
|
||||||
@@ -107,6 +110,7 @@ const getZulipDisplay = (
|
|||||||
function MeetingStatus({ roomName }: { roomName: string }) {
|
function MeetingStatus({ roomName }: { roomName: string }) {
|
||||||
const activeMeetingsQuery = useRoomActiveMeetings(roomName);
|
const activeMeetingsQuery = useRoomActiveMeetings(roomName);
|
||||||
const upcomingMeetingsQuery = useRoomUpcomingMeetings(roomName);
|
const upcomingMeetingsQuery = useRoomUpcomingMeetings(roomName);
|
||||||
|
const deactivateMutation = useMeetingDeactivate();
|
||||||
|
|
||||||
const activeMeetings = activeMeetingsQuery.data || [];
|
const activeMeetings = activeMeetingsQuery.data || [];
|
||||||
const upcomingMeetings = upcomingMeetingsQuery.data || [];
|
const upcomingMeetings = upcomingMeetingsQuery.data || [];
|
||||||
@@ -121,14 +125,46 @@ function MeetingStatus({ roomName }: { roomName: string }) {
|
|||||||
meeting.calendar_metadata?.["title"] || "Active Meeting",
|
meeting.calendar_metadata?.["title"] || "Active Meeting",
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<VStack gap={1} alignItems="start">
|
<Flex alignItems="center" gap={2}>
|
||||||
<Text fontSize="xs" color="gray.600" lineHeight={1}>
|
<VStack gap={1} alignItems="start">
|
||||||
{title}
|
<Text fontSize="xs" color="gray.600" lineHeight={1}>
|
||||||
</Text>
|
{title}
|
||||||
<Text fontSize="xs" color="gray.500" lineHeight={1}>
|
</Text>
|
||||||
{meeting.num_clients} participants
|
<Text fontSize="xs" color="gray.500" lineHeight={1}>
|
||||||
</Text>
|
{meeting.num_clients} participants
|
||||||
</VStack>
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
{activeMeetings.length === 1 && (meeting.num_clients ?? 0) < 2 && (
|
||||||
|
<Tooltip.Root openDelay={100}>
|
||||||
|
<Tooltip.Trigger asChild>
|
||||||
|
<IconButton
|
||||||
|
aria-label="End meeting"
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
color="red.500"
|
||||||
|
_hover={{ bg: "transparent", color: "red.600" }}
|
||||||
|
onClick={() =>
|
||||||
|
deactivateMutation.mutate({
|
||||||
|
params: { path: { meeting_id: meeting.id } },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={deactivateMutation.isPending}
|
||||||
|
>
|
||||||
|
{deactivateMutation.isPending ? (
|
||||||
|
<Spinner size="xs" />
|
||||||
|
) : (
|
||||||
|
<FaStop />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Positioner>
|
||||||
|
<Tooltip.Content>
|
||||||
|
End this meeting and stop any active recordings
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Positioner>
|
||||||
|
</Tooltip.Root>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user