mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-22 13:19:05 +00:00
- Created MeetingSelection component for choosing between multiple active meetings
- Shows both active meetings and upcoming calendar events (30 min ahead)
- Displays meeting metadata with privacy controls (owner-only details)
- Supports creation of unscheduled meetings alongside calendar meetings
- Added waiting page for users joining before scheduled start time
- Shows countdown timer until meeting begins
- Auto-transitions to meeting when calendar event becomes active
- Handles early joining with proper routing
- Created collapsible info panel showing meeting details
- Displays calendar metadata (title, description, attendees)
- Shows participant count and duration
- Privacy-aware: sensitive info only visible to room owners
- Integrated ICS settings into room configuration dialog
- Test connection functionality with immediate feedback
- Manual sync trigger with detailed results
- Shows last sync time and ETag for monitoring
- Configurable sync intervals (1 min to 1 hour)
- New /room/{roomName} route for meeting selection
- Waiting room at /room/{roomName}/wait?eventId={id}
- Classic room page at /{roomName} with meeting info
- Uses sessionStorage to pass selected meeting between pages
- Added new endpoints for active/upcoming meetings
- Regenerated TypeScript client with latest OpenAPI spec
- Proper error handling and loading states
- Auto-refresh every 30 seconds for live updates
- Color-coded badges for meeting status
- Attendee status indicators (accepted/declined/tentative)
- Responsive design with Chakra UI components
- Clear visual hierarchy between active and upcoming meetings
- Smart truncation for long attendee lists
This completes the frontend implementation for calendar integration,
enabling users to seamlessly join scheduled meetings from their
calendar applications.
93 lines
2.5 KiB
TypeScript
93 lines
2.5 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { useError } from "../(errors)/errorContext";
|
|
import type { components } from "../reflector-api";
|
|
import { shouldShowError } from "../lib/errorUtils";
|
|
|
|
type Meeting = components["schemas"]["Meeting"];
|
|
import { useRoomsCreateMeeting } from "../lib/apiHooks";
|
|
import { notFound } from "next/navigation";
|
|
|
|
type ErrorMeeting = {
|
|
error: Error;
|
|
loading: false;
|
|
response: null;
|
|
reload: () => void;
|
|
};
|
|
|
|
type LoadingMeeting = {
|
|
response: null;
|
|
loading: true;
|
|
error: false;
|
|
reload: () => void;
|
|
};
|
|
|
|
type SuccessMeeting = {
|
|
response: Meeting;
|
|
loading: false;
|
|
error: null;
|
|
reload: () => void;
|
|
};
|
|
|
|
const useRoomMeeting = (
|
|
roomName: string | null | undefined,
|
|
): ErrorMeeting | LoadingMeeting | SuccessMeeting => {
|
|
const [response, setResponse] = useState<Meeting | null>(null);
|
|
const [reload, setReload] = useState(0);
|
|
const { setError } = useError();
|
|
const createMeetingMutation = useRoomsCreateMeeting();
|
|
const reloadHandler = () => setReload((prev) => prev + 1);
|
|
|
|
useEffect(() => {
|
|
if (!roomName) return;
|
|
|
|
// Check if meeting was pre-selected from meeting selection page
|
|
const storedMeeting = sessionStorage.getItem(`meeting_${roomName}`);
|
|
if (storedMeeting) {
|
|
try {
|
|
const meeting = JSON.parse(storedMeeting);
|
|
sessionStorage.removeItem(`meeting_${roomName}`); // Clean up
|
|
setResponse(meeting);
|
|
setLoading(false);
|
|
return;
|
|
} catch (e) {
|
|
console.error("Failed to parse stored meeting:", e);
|
|
}
|
|
}
|
|
|
|
const createMeeting = async () => {
|
|
try {
|
|
const result = await createMeetingMutation.mutateAsync({
|
|
params: {
|
|
path: {
|
|
room_name: roomName,
|
|
},
|
|
},
|
|
});
|
|
setResponse(result);
|
|
} catch (error: any) {
|
|
const shouldShowHuman = shouldShowError(error);
|
|
if (shouldShowHuman && error.status !== 404) {
|
|
setError(
|
|
error,
|
|
"There was an error loading the meeting. Please try again by refreshing the page.",
|
|
);
|
|
} else {
|
|
setError(error);
|
|
}
|
|
}
|
|
};
|
|
|
|
createMeeting();
|
|
}, [roomName, reload]);
|
|
|
|
const loading = createMeetingMutation.isPending && !response;
|
|
const error = createMeetingMutation.error as Error | null;
|
|
|
|
return { response, loading, error, reload: reloadHandler } as
|
|
| ErrorMeeting
|
|
| LoadingMeeting
|
|
| SuccessMeeting;
|
|
};
|
|
|
|
export default useRoomMeeting;
|