Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions backend/src/cronjobs/cronjobs.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type ReceivableUser = Pick<
| "id"
| "ghostModeRemindersEmail"
| "restrictedViewToken"
| "lastDateModeReminderSent"
>;

export enum TimeSpan {
Expand All @@ -40,10 +41,25 @@ export interface OfflineUserSince {

export const goBackInTimeFor = (
value: number,
unit: "hours" | "days",
unit: "hours" | "days" | "months" | "years",
): Date => {
const hours = unit === "days" ? value * 24 : value;
return new Date(new Date().getTime() - hours * 60 * 60 * 1000);
const now = new Date();
switch (unit) {
case "hours":
return new Date(now.getTime() - value * 60 * 60 * 1000);
case "days":
return new Date(now.getTime() - value * 24 * 60 * 60 * 1000);
case "months": {
const date = new Date(now);
date.setMonth(date.getMonth() - value);
return date;
}
case "years": {
const date = new Date(now);
date.setFullYear(date.getFullYear() - value);
return date;
}
}
};

/** @dev String value used for translation keys */
Expand Down
27 changes: 19 additions & 8 deletions backend/src/cronjobs/ghostmode-reminder.cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
goBackInTimeFor,
IntervalHour,
OfflineUserSince,
ReceivableUser,
TimeSpan,
} from "@/cronjobs/cronjobs.types";
import { ENotificationType } from "@/DTOs/abstract/base-notification.adto";
Expand Down Expand Up @@ -57,24 +58,27 @@ export class GhostModeReminderCronJob extends BaseCronJob {
"user.lastDateModeChange",
"user.ghostModeRemindersEmail",
"user.restrictedViewToken",
"user.lastDateModeReminderSent",
])
.where("user.dateMode = :mode", { mode: EDateMode.GHOST })
.andWhere("user.lastDateModeChange < :dayAgo", {
dayAgo: goBackInTimeFor(24, "hours"),
})
.andWhere(
"(user.lastDateModeReminderSent IS NULL OR " +
"(user.lastDateModeReminderSent IS NULL) OR " + // Reminded if > 24 hrs off and never reminded
"(user.lastDateModeReminderSent < user.lastDateModeChange) OR " + // Remind if went online in-between
"CASE " +
"WHEN user.lastDateModeChange < :twoWeeksAgo THEN user.lastDateModeReminderSent < :twoWeeksMinTime " +
"WHEN user.lastDateModeChange < :threeDaysAgo THEN user.lastDateModeReminderSent < :threeDaysMinTime " +
"ELSE user.lastDateModeReminderSent < :oneDayMinTime " +
"END)",
"WHEN user.lastDateModeChange < :twoWeeksAgo AND user.lastDateModeReminderSent >= :twoWeeksMinTime THEN 0 " +
"WHEN user.lastDateModeChange < :threeDaysAgo AND user.lastDateModeReminderSent < :threeDaysMinTime AND user.lastDateModeReminderSent > :twoWeeksAgo THEN 1 " +
"WHEN user.lastDateModeChange < :oneDayAgo AND user.lastDateModeReminderSent IS NULL THEN 1 " +
"ELSE 0 " +
"END = 1",
{
twoWeeksAgo: goBackInTimeFor(336, "hours"),
threeDaysAgo: goBackInTimeFor(72, "hours"),
oneDayAgo: goBackInTimeFor(24, "hours"),
twoWeeksMinTime: goBackInTimeFor(336 - 72, "hours"),
threeDaysMinTime: goBackInTimeFor(72 - 24, "hours"),
oneDayMinTime: goBackInTimeFor(24, "hours"),
},
);

Expand All @@ -92,15 +96,22 @@ export class GhostModeReminderCronJob extends BaseCronJob {

return users.map((user) => ({
user,
type: this.determineOfflineType(user.lastDateModeChange),
type: this.determineOfflineType(user, user.lastDateModeChange),
}));
}

private determineOfflineType(lastDateModeChange: Date): TimeSpan {
private determineOfflineType(
user: ReceivableUser,
lastDateModeChange: Date,
): TimeSpan {
const hoursGhostMode = differenceInHours(
new Date(),
lastDateModeChange,
);
/** @DEV Edge case: if a user has never been reminded he is always in the 24 hrs bucket */
if (!user.lastDateModeReminderSent) {
return TimeSpan.ONE_DAY;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Do we also have test cases for users that:

  1. Have been reminded e.g. 2 months ago and now are in ghost mode? Meaning they should be in 24h bucket again

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Edge case above is only if something is in ghost mode for 2 months and has never been reminded (for whatever reason). Checking the other case you mentioned, good call!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Do we also have test cases for users that:

  1. Have been reminded e.g. 2 months ago and now are in ghost mode? Meaning they should be in 24h bucket again

Well, they shouldn't (currently) - because we do not have a set-back logic. If you've been reminded the last time 2 months ago, that would mean you have received all the 3 notifications back then (from one day to 14 days off) and then the algorithm will not consider you, as you probably removed the app - somewhat (max 3 notifications).

So we'd need to reset the lastDateModeReminderSent to re-start that cycle.

}
if (hoursGhostMode >= 336) return TimeSpan.TWO_WEEKS;
if (hoursGhostMode >= 72) return TimeSpan.THREE_DAYS;
return TimeSpan.ONE_DAY;
Expand Down
Loading