55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import { berlinParts, berlinWallTimeToUTC } from "@/lib/calendar/timezone";
|
|
|
|
export type MeetingStatus = "scheduled" | "open" | "live" | "ended";
|
|
|
|
/**
|
|
* Payload's "time only" date fields still store a full ISO timestamp, just
|
|
* with an arbitrary date portion — this combines the real event date with
|
|
* the hour/minute from a time-only field into one correct Date.
|
|
*/
|
|
export function combineDateAndTime(dateISO: string, timeISO?: string | null): Date {
|
|
const date = new Date(dateISO);
|
|
if (!timeISO) return date;
|
|
const time = new Date(timeISO);
|
|
if (Number.isNaN(date.getTime()) || Number.isNaN(time.getTime())) return new Date(NaN);
|
|
const dateParts = berlinParts(date);
|
|
const timeParts = berlinParts(time);
|
|
return berlinWallTimeToUTC(dateParts, timeParts.hour, timeParts.minute);
|
|
}
|
|
|
|
export type MeetingWindow = {
|
|
start: Date;
|
|
end: Date;
|
|
joinWindowMinutes: number;
|
|
closeAfterMinutes: number;
|
|
now?: Date;
|
|
};
|
|
|
|
export function getMeetingStatus({
|
|
start,
|
|
end,
|
|
joinWindowMinutes,
|
|
closeAfterMinutes,
|
|
now = new Date(),
|
|
}: MeetingWindow): MeetingStatus {
|
|
const openAt = start.getTime() - joinWindowMinutes * 60_000;
|
|
const closeAt = end.getTime() + closeAfterMinutes * 60_000;
|
|
const t = now.getTime();
|
|
|
|
if (t >= closeAt) return "ended";
|
|
if (t >= start.getTime()) return "live";
|
|
if (t >= openAt) return "open";
|
|
return "scheduled";
|
|
}
|
|
|
|
export function canJoinMeeting(status: MeetingStatus): boolean {
|
|
return status === "open" || status === "live";
|
|
}
|
|
|
|
export const statusLabel: Record<MeetingStatus, string> = {
|
|
scheduled: "Online-Termin",
|
|
open: "Termin beginnt bald",
|
|
live: "Jetzt teilnehmen",
|
|
ended: "Termin beendet",
|
|
};
|