Skip to content

Commit 483c6f5

Browse files
committed
refactor: delegate calendar status lifecycle to presence engine
1 parent 14f506e commit 483c6f5

14 files changed

Lines changed: 73 additions & 776 deletions

File tree

apps/meteor/server/services/calendar/service.ts

Lines changed: 14 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ICalendarService } from '@rocket.chat/core-services';
2-
import { Presence, ServiceClassInternal, api } from '@rocket.chat/core-services';
2+
import { ServiceClassInternal, api } from '@rocket.chat/core-services';
33
import type { IUser, ICalendarEvent } from '@rocket.chat/core-typings';
44
import { cronJobs } from '@rocket.chat/cron';
55
import { Logger } from '@rocket.chat/logger';
@@ -8,8 +8,6 @@ import { CalendarEvent } from '@rocket.chat/models';
88
import type { UpdateResult, DeleteResult } from 'mongodb';
99

1010
import { applyStatusChange } from './statusEvents/applyStatusChange';
11-
import { cancelUpcomingStatusChanges } from './statusEvents/cancelUpcomingStatusChanges';
12-
import { removeCronJobs } from './statusEvents/removeCronJobs';
1311
import { getShiftedTime } from './utils/getShiftedTime';
1412
import { settings } from '../../../app/settings/server';
1513
import { getUserPreference } from '../../../app/utils/server/lib/getUserPreference';
@@ -134,7 +132,6 @@ export class CalendarService extends ServiceClassInternal implements ICalendarSe
134132
await this.setupNextNotification();
135133

136134
if (startTime || endTime) {
137-
await removeCronJobs(eventId, event.uid);
138135
const isBusy = busy !== undefined ? busy : event.busy !== false;
139136
if (isBusy) {
140137
await this.setupNextStatusChange();
@@ -146,11 +143,6 @@ export class CalendarService extends ServiceClassInternal implements ICalendarSe
146143
}
147144

148145
public async delete(eventId: ICalendarEvent['_id']): Promise<DeleteResult> {
149-
const event = await this.get(eventId);
150-
if (event) {
151-
await removeCronJobs(eventId, event.uid);
152-
}
153-
154146
const result = await CalendarEvent.deleteOne({
155147
_id: eventId,
156148
});
@@ -170,10 +162,6 @@ export class CalendarService extends ServiceClassInternal implements ICalendarSe
170162
return this.doSetupNextStatusChange();
171163
}
172164

173-
public async cancelUpcomingStatusChanges(uid: IUser['_id'], endTime = new Date()): Promise<void> {
174-
return cancelUpcomingStatusChanges(uid, endTime);
175-
}
176-
177165
private async getMeetingUrl(eventData: Partial<ICalendarEvent>): Promise<string | undefined> {
178166
if (eventData.meetingUrl !== undefined) {
179167
return eventData.meetingUrl || undefined;
@@ -204,101 +192,51 @@ export class CalendarService extends ServiceClassInternal implements ICalendarSe
204192
}
205193

206194
private async doSetupNextStatusChange(): Promise<void> {
207-
// This method is called in the following moments:
208-
// 1. When a new busy event is created or imported
209-
// 2. When a busy event is updated (time/busy status changes)
210-
// 3. When a busy event is deleted
211-
// 4. When a status change job executes and completes
212-
// 5. When an event ends and the status is restored
213-
// 6. From Outlook Calendar integration (ee/server/configuration/outlookCalendar.ts)
195+
// Schedules a cron job for the next event start time.
196+
// End-time handling is delegated to the presence engine via statusExpiresAt.
214197

215198
const busyStatusEnabled = settings.get<boolean>('Calendar_BusyStatus_Enabled');
199+
const schedulerJobId = 'calendar-status-scheduler';
200+
216201
if (!busyStatusEnabled) {
217-
const schedulerJobId = 'calendar-status-scheduler';
218202
if (await cronJobs.has(schedulerJobId)) {
219203
await cronJobs.remove(schedulerJobId);
220204
}
221205
return;
222206
}
223207

224-
const schedulerJobId = 'calendar-status-scheduler';
225208
if (await cronJobs.has(schedulerJobId)) {
226209
await cronJobs.remove(schedulerJobId);
227210
}
228211

229212
const now = new Date();
230213
const nextStartEvent = await CalendarEvent.findNextFutureEvent(now);
231-
const inProgressEvents = await CalendarEvent.findInProgressEvents(now).toArray();
232-
const eventsWithEndTime = inProgressEvents.filter((event) => event.endTime && event.busy !== false);
233-
if (eventsWithEndTime.length === 0 && !nextStartEvent) {
214+
if (!nextStartEvent) {
234215
return;
235216
}
236217

237-
let nextEndTime: Date | null = null;
238-
if (eventsWithEndTime.length > 0 && eventsWithEndTime[0].endTime) {
239-
nextEndTime = eventsWithEndTime.reduce((earliest, event) => {
240-
if (!event.endTime) return earliest;
241-
return event.endTime.getTime() < earliest.getTime() ? event.endTime : earliest;
242-
}, eventsWithEndTime[0].endTime);
243-
}
244-
245-
let nextProcessTime: Date;
246-
if (nextStartEvent && nextEndTime) {
247-
nextProcessTime = nextStartEvent.startTime.getTime() < nextEndTime.getTime() ? nextStartEvent.startTime : nextEndTime;
248-
} else if (nextStartEvent) {
249-
nextProcessTime = nextStartEvent.startTime;
250-
} else if (nextEndTime) {
251-
nextProcessTime = nextEndTime;
252-
} else {
253-
// This should never happen due to the earlier check, but just in case
254-
return;
255-
}
256-
257-
await cronJobs.addAtTimestamp(schedulerJobId, nextProcessTime, async () => this.processStatusChangesAtTime());
218+
await cronJobs.addAtTimestamp(schedulerJobId, nextStartEvent.startTime, async () => this.processStatusChangesAtTime());
258219
}
259220

260221
private async processStatusChangesAtTime(): Promise<void> {
261222
const processTime = new Date();
262223

263224
const eventsStartingNow = await CalendarEvent.findEventsStartingNow({ now: processTime, offset: 5000 }).toArray();
264225
for await (const event of eventsStartingNow) {
265-
if (event.busy === false) {
226+
if (event.busy === false || !event.endTime) {
266227
continue;
267228
}
268-
await this.processEventStart(event);
269-
}
270-
271-
const eventsEndingNow = await CalendarEvent.findEventsEndingNow({ now: processTime, offset: 5000 }).toArray();
272-
for await (const event of eventsEndingNow) {
273-
if (event.busy === false) {
274-
continue;
275-
}
276-
await this.processEventEnd(event);
229+
await applyStatusChange({
230+
eventId: event._id,
231+
uid: event.uid,
232+
subject: event.subject,
233+
endTime: event.endTime,
234+
});
277235
}
278236

279237
await this.doSetupNextStatusChange();
280238
}
281239

282-
private async processEventStart(event: ICalendarEvent): Promise<void> {
283-
if (!event.endTime) {
284-
return;
285-
}
286-
287-
await applyStatusChange({
288-
eventId: event._id,
289-
uid: event.uid,
290-
endTime: event.endTime,
291-
});
292-
}
293-
294-
private async processEventEnd(event: ICalendarEvent): Promise<void> {
295-
if (!event.endTime) {
296-
return;
297-
}
298-
299-
await Presence.endActiveState(event.uid);
300-
}
301-
302240
private async sendCurrentNotifications(date: Date): Promise<void> {
303241
const events = await CalendarEvent.findEventsToNotify(date, 1).toArray();
304242
for await (const event of events) {

apps/meteor/server/services/calendar/statusEvents/applyStatusChange.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,25 @@ const logger = new Logger('Calendar');
88
export async function applyStatusChange({
99
eventId,
1010
uid,
11+
subject,
1112
endTime,
1213
}: {
1314
eventId: ICalendarEvent['_id'];
1415
uid: IUser['_id'];
16+
subject?: string;
1517
endTime?: Date;
1618
}): Promise<void> {
1719
logger.debug({
1820
msg: 'Applying status change for event via presence engine',
1921
eventId,
2022
uid,
23+
subject,
2124
endTime,
2225
});
2326

2427
await Presence.setActiveState(uid, {
2528
statusDefault: UserStatus.BUSY,
26-
statusText: '',
29+
statusText: subject ?? '',
2730
statusSource: 'external',
2831
statusEmoji: '📅',
2932
...(endTime && { statusExpiresAt: endTime }),

apps/meteor/server/services/calendar/statusEvents/cancelUpcomingStatusChanges.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

apps/meteor/server/services/calendar/statusEvents/generateCronJobId.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

apps/meteor/server/services/calendar/statusEvents/removeCronJobs.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)