Skip to content

Commit d9cbf22

Browse files
feat(api): fetch and generate recurring meetings
1 parent 468a8af commit d9cbf22

File tree

1 file changed

+44
-8
lines changed

1 file changed

+44
-8
lines changed

lib/api/get/meetings.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { Meeting, MeetingsQuery } from 'lib/model';
1+
import { RRule } from 'rrule';
2+
import { nanoid } from 'nanoid';
3+
4+
import { Meeting, MeetingsQuery, Timeslot } from 'lib/model';
25
import { addOptionsFilter, addStringFilter, list } from 'lib/api/search';
36

47
function getFilterStrings(query: MeetingsQuery): string[] {
@@ -9,19 +12,52 @@ function getFilterStrings(query: MeetingsQuery): string[] {
912
const to = query.to.valueOf();
1013
const from = query.from.valueOf();
1114

15+
const endWithin = `time.last >= ${from} AND time.last <= ${to}`;
16+
const startWithin = `time.from >= ${from} AND time.from <= ${to}`;
17+
1218
return [
13-
// TODO: Time overlaps with top of query (start is before query start).
14-
// addStringFilter(str, `(time.to > ${from} AND time.from <= ${from})`),
15-
// Meeting time is contained within query (start is after and end before).
16-
addStringFilter(str, `(time.to <= ${to} AND time.from >= ${from})`),
17-
// TODO: Time overlaps with bottom of query (end is after query end).
18-
// addStringFilter(str, `(time.from < ${to} AND time.to >= ${to})`),
19+
// Start is before window but end is within.
20+
addStringFilter(str, `(time.from < ${from} AND ${endWithin})`),
21+
// Both start and end are within window.
22+
addStringFilter(str, `(${startWithin} AND ${endWithin})`),
23+
// End is after window but start is within.
24+
addStringFilter(str, `(time.last > ${to} AND ${startWithin})`),
25+
// Start is before window and end is after.
26+
addStringFilter(str, `(time.from < ${from} AND time.last > ${to})`),
1927
];
2028
}
2129

30+
// TODO: Generate instance meetings (from recurring parent meetings returned by
31+
// query) within requested time window and send those to the client.
2232
export default async function getMeetings(
2333
query: MeetingsQuery
2434
): Promise<{ hits: number; results: Meeting[] }> {
2535
const filters = getFilterStrings(query);
26-
return list('meetings', query, Meeting.fromSearchHit, filters);
36+
const data = await list('meetings', query, Meeting.fromSearchHit, filters);
37+
let { hits } = data;
38+
const meetings = data.results
39+
.map((meeting) => {
40+
if (!meeting.time.recur) return [meeting];
41+
const options = RRule.parseString(meeting.time.recur);
42+
const rrule = new RRule({ ...options, dtstart: meeting.time.from });
43+
// TODO: What if meeting instance starts before window but end is within?
44+
const starts = rrule.between(query.from, query.to);
45+
console.log('Starts:', starts.map((s) => s.toString()).join(', '));
46+
hits += starts.length - 1;
47+
return starts.map(
48+
(start) =>
49+
new Meeting({
50+
...meeting,
51+
id: nanoid(),
52+
parentId: meeting.id,
53+
time: new Timeslot({
54+
...meeting.time,
55+
from: start,
56+
to: new Date(start.valueOf() + meeting.time.duration),
57+
}),
58+
})
59+
);
60+
})
61+
.flat();
62+
return { hits, results: meetings };
2763
}

0 commit comments

Comments
 (0)