Skip to content

Commit e701915

Browse files
authored
Feat: Videochat start adjustments (#972)
## What was done? - Allow instructors to join a meeting 48h in advance - Allow users to join past meetings within 30 days
1 parent 1817c6f commit e701915

15 files changed

Lines changed: 70 additions & 30 deletions

src/Utility.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export const REDIRECT_PASSWORD = `/login`;
1111
export const MIN_MAX_GRADE_RANGE = { min: 1, max: 14 };
1212
export const TRAINEE_GRADE = 14;
1313
export const MIN_AGE_PUPIL = 7;
14+
export const INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES = 60 * 48;
15+
export const PARTICIPANT_JOIN_IN_ADVANCE_MINUTES = 10;
1416

1517
export const toTimerString = (referenceDate: DateTime, theDate: DateTime) => {
1618
const inPast = theDate < referenceDate;

src/components/VideoButton.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Button } from './Button';
99
import { IconVideo } from '@tabler/icons-react';
1010
import { useCanJoinMeeting } from '@/hooks/useCanJoinMeeting';
1111
import { toast } from 'sonner';
12+
import { INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES, PARTICIPANT_JOIN_IN_ADVANCE_MINUTES } from '@/Utility';
1213

1314
type VideoButtonProps = {
1415
isInstructor?: boolean;
@@ -85,7 +86,10 @@ const VideoButton: React.FC<VideoButtonProps> = ({
8586
}
8687
};
8788

88-
const canStartMeeting = useCanJoinMeeting(isInstructor ? 240 : 10, startDateTime, duration);
89+
const canStartMeeting = useCanJoinMeeting({
90+
joinBeforeMinutes: isInstructor ? INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES : PARTICIPANT_JOIN_IN_ADVANCE_MINUTES,
91+
appointment: { start: startDateTime, duration: duration ?? 60 },
92+
});
8993

9094
return (
9195
<>
@@ -98,7 +102,7 @@ const VideoButton: React.FC<VideoButtonProps> = ({
98102
/>
99103
<Button
100104
isLoading={isLoadingOverrideMeetingLink || isLoadingZoomMeetingLink}
101-
disabled={!(canJoin ?? canStartMeeting) || isOver}
105+
disabled={canJoin !== undefined ? !canJoin : !canStartMeeting}
102106
reasonDisabled={isInstructor ? t('course.meeting.hint.student') : t('course.meeting.hint.pupil')}
103107
onClick={openMeeting}
104108
className={className}

src/components/appointment/AppointmentDetail.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { IconInfoCircle, IconClockEdit, IconTrash, IconPencil } from '@tabler/ic
1919
import { Button } from '../Button';
2020
import AddToCalendarDropdown from '../AddToCalendarDropdown';
2121
import { useCanJoinMeeting } from '@/hooks/useCanJoinMeeting';
22+
import { INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES, PARTICIPANT_JOIN_IN_ADVANCE_MINUTES } from '@/Utility';
2223

2324
type AppointmentDetailProps = {
2425
appointment: Appointment;
@@ -113,7 +114,10 @@ const AppointmentDetail: React.FC<AppointmentDetailProps> = ({ appointment, isHo
113114
const wasRejectedByMe = appointment.declinedBy?.includes(user?.userID!);
114115
const wasRejectedByMatch = appointment.appointmentType === 'match' && wasRejected && byMatch;
115116

116-
const isCurrent = useCanJoinMeeting(appointment.isOrganizer ? 240 : 10, appointment.start, appointment.duration);
117+
const isCurrent = useCanJoinMeeting({
118+
joinBeforeMinutes: appointment.isOrganizer ? INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES : PARTICIPANT_JOIN_IN_ADVANCE_MINUTES,
119+
appointment: { start: appointment.start, duration: appointment.duration ?? 60 },
120+
});
117121

118122
const canAddToCalendar = !wasRejected && !appointment.declinedBy?.length && !isPastAppointment && !isCurrent;
119123

src/components/appointment/AppointmentMetaDetails.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { IconDeviceMobileMessage, IconPointFilled, IconArrowNarrowRight } from '
1919
import { useCanJoinMeeting } from '@/hooks/useCanJoinMeeting';
2020
import { QRCodeSVG } from 'qrcode.react';
2121
import { gql } from '../../gql';
22+
import { INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES, PARTICIPANT_JOIN_IN_ADVANCE_MINUTES } from '@/Utility';
2223

2324
type MetaProps = {
2425
date: string;
@@ -89,7 +90,10 @@ const AppointmentMetaDetails: React.FC<MetaProps> = ({
8990
// eslint-disable-next-line react-hooks/exhaustive-deps
9091
}, []);
9192

92-
const canStartMeeting = useCanJoinMeeting(isOrganizer ? 240 : 10, startDateTime, duration);
93+
const canStartMeeting = useCanJoinMeeting({
94+
joinBeforeMinutes: isOrganizer ? INSTRUCTOR_JOIN_IN_ADVANCE_MINUTES : PARTICIPANT_JOIN_IN_ADVANCE_MINUTES,
95+
appointment: { start: startDateTime, duration },
96+
});
9397

9498
useEffect(() => {
9599
canStartMeeting && createShortTimeLoginData();

src/hooks/useCanJoinMeeting.tsx

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
1+
import { Lecture } from '@/gql/graphql';
12
import { DateTime } from 'luxon';
23
import { useMemo, useState } from 'react';
34
import useInterval from './useInterval';
45

5-
export const useCanJoinMeeting = (joinBeforeMinutes: number, start?: string, duration?: number) => {
6+
interface UseCanJoinMeetingProps {
7+
joinBeforeMinutes: number;
8+
appointment: Pick<Lecture, 'start' | 'duration'>;
9+
}
10+
11+
export const useCanJoinMeeting = ({ joinBeforeMinutes, appointment }: UseCanJoinMeetingProps) => {
612
const [now, setNow] = useState(DateTime.now());
713

814
useInterval(() => {
915
setNow(DateTime.now());
1016
}, 60000);
1117

12-
const valCanJoinMeeting = useMemo(() => canJoinMeeting(joinBeforeMinutes, now, start, duration), [duration, joinBeforeMinutes, start, now]) as boolean;
18+
const canJoinMeeting = useMemo(() => {
19+
const end = DateTime.fromISO(appointment.start).plus({ minutes: appointment.duration });
20+
const isAppointmentInTheFuture = DateTime.now() <= end;
21+
// Allow users to join meetings that are already over. (only if it was in the last 30 days).
22+
if (!isAppointmentInTheFuture && DateTime.fromISO(appointment.start).diffNow('days').days > -30) return true;
1323

14-
return valCanJoinMeeting;
15-
};
24+
if (appointment.start && appointment.duration) {
25+
const startDate = DateTime.fromISO(appointment.start).minus({ minutes: joinBeforeMinutes });
26+
const end = DateTime.fromISO(appointment.start).plus({ minutes: appointment.duration });
27+
return now.toUnixInteger() >= startDate.toUnixInteger() && now.toUnixInteger() <= end.toUnixInteger();
28+
} else {
29+
return false;
30+
}
31+
}, [appointment.duration, joinBeforeMinutes, appointment.start, now]) as boolean;
1632

17-
const canJoinMeeting = (joinBeforeMinutes: number, now: DateTime, start?: string, duration?: number): boolean => {
18-
if (start && duration) {
19-
const startDate = DateTime.fromISO(start).minus({ minutes: joinBeforeMinutes });
20-
const end = DateTime.fromISO(start).plus({ minutes: duration });
21-
return now.toUnixInteger() >= startDate.toUnixInteger() && now.toUnixInteger() <= end.toUnixInteger();
22-
} else {
23-
return false;
24-
}
33+
return canJoinMeeting;
2534
};

src/lang/ar.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2396,7 +2396,8 @@
23962396
"Pupil": "طالب",
23972397
"Retiree": "متقاعد",
23982398
"Student": "طالب",
2399-
"Azubi": "متدرب"
2399+
"Azubi": "متدرب",
2400+
"SeekingEmployment": "باحث عن عمل"
24002401
},
24012402
"reasonsDisabled": {
24022403
"loading": "لا تزال البيانات قيد التحميل...",

src/lang/de.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2503,7 +2503,8 @@
25032503
"Pupil": "Schüler:in",
25042504
"Retiree": "Rentner:in",
25052505
"Student": "Student:in",
2506-
"Azubi": "Auszubildende:r"
2506+
"Azubi": "Auszubildende:r",
2507+
"SeekingEmployment": "Arbeitssuchend"
25072508
},
25082509
"formalEducation": {
25092510
"teacher": "Lehrer:in",

src/lang/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2400,7 +2400,8 @@
24002400
"Pupil": "pupil",
24012401
"Retiree": "Pensioner",
24022402
"Student": "Student",
2403-
"Azubi": "Aprentice"
2403+
"Azubi": "Aprentice",
2404+
"SeekingEmployment": "Job seeker"
24042405
},
24052406
"reasonsDisabled": {
24062407
"loading": "Data is still being loaded...",

src/lang/ru.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2400,7 +2400,8 @@
24002400
"Pupil": "ученик",
24012401
"Retiree": "пенсионер",
24022402
"Student": "студент",
2403-
"Azubi": "стажер"
2403+
"Azubi": "стажер",
2404+
"SeekingEmployment": "Ищущий работу"
24042405
},
24052406
"reasonsDisabled": {
24062407
"loading": "Данные все еще загружаются...",

src/lang/tr.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2400,7 +2400,8 @@
24002400
"Pupil": "Öğrenciler",
24012401
"Retiree": "Emekli",
24022402
"Student": "Öğrenci",
2403-
"Azubi": "Stajyer"
2403+
"Azubi": "Stajyer",
2404+
"SeekingEmployment": "İş arayan"
24042405
},
24052406
"reasonsDisabled": {
24062407
"loading": "Veriler hala yükleniyor...",

0 commit comments

Comments
 (0)