feat: due-date reminders
Every hour the API reminds whoever has a ticket, and whoever follows it, the day before its due date, on the day and once when it is overdue, in the app and by e-mail for those who asked. Cards on the board flag what is due soon or late. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,9 +60,8 @@ export class Notifier {
|
||||
? { id: SYSTEM_ACTOR_ID, name: SYSTEM_ACTOR_NAME }
|
||||
: refs.of(event.actorId);
|
||||
|
||||
const delivered: NotificationReadModel[] = [];
|
||||
for (const draft of wanted) {
|
||||
const notification: NotificationReadModel = {
|
||||
await this.send(
|
||||
wanted.map((draft) => ({
|
||||
id: `${event.aggregateId}:${versionOf(event)}:${draft.userId}`,
|
||||
userId: draft.userId,
|
||||
kind: draft.kind,
|
||||
@@ -71,13 +70,22 @@ export class Notifier {
|
||||
summary: draft.summary,
|
||||
readAt: null,
|
||||
createdAt: event.occurredAt,
|
||||
};
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores, pushes and e-mails ready-made notifications. Their id makes them
|
||||
* idempotent: one that is already there is not sent again.
|
||||
*/
|
||||
async send(notifications: NotificationReadModel[]): Promise<void> {
|
||||
const delivered: NotificationReadModel[] = [];
|
||||
for (const notification of notifications) {
|
||||
// Delivered again (at-least-once): already told, nothing to repeat.
|
||||
if (!(await this.notifications.insert(notification))) continue;
|
||||
await this.changeFeed.publishNotification(notification);
|
||||
delivered.push(notification);
|
||||
}
|
||||
|
||||
if (this.mailer.enabled && delivered.length) {
|
||||
await this.email(delivered);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user