eletrotupi / tcc/ api/src/services/notification.service.tsdafa6f8a8e3b0e51605fc953a979d50ff6085b26
2.6 KB | 111 lines Raw
1
import { Notification, NotificationKind } from "@prisma/client";
2
import { endOfDay } from "date-fns";
3

4
import { prisma } from "@app/lib/prisma";
5
import { APP_TIMEZONE } from "@app/lib/appTime";
6
import { logger } from "@app/lib/logger";
7
import { sendPushNotification } from "@app/lib/sendPushNotification";
8

9
export type CreateNotificationInput = {
10
  userId: number;
11
  kind: NotificationKind;
12
  title: string;
13
  body: string;
14
  scheduledFor: Date;
15
  regimenId?: number | null;
16
};
17

18
const endOfAppDay = (scheduledFor: Date): Date =>
19
  endOfDay(scheduledFor, { in: APP_TIMEZONE });
20

21
// The only place a kind's behaviour is described: how long it stays relevant,
22
// and where tapping it should lead
23
type NotificationRule = {
24
  expiresAt: (scheduledFor: Date) => Date | null;
25
  screen?: string;
26
};
27

28
const notificationRules: Record<NotificationKind, NotificationRule> = {
29
  [NotificationKind.DAILY_REMINDER]: {
30
    expiresAt: endOfAppDay,
31
    screen: "moods",
32
  },
33
  [NotificationKind.MEDICINE_REMINDER]: {
34
    expiresAt: endOfAppDay,
35
    screen: "medicine-regimens",
36
  },
37
  [NotificationKind.INSIGHT_ALERT]: {
38
    expiresAt: () => null,
39
  },
40
};
41

42
export const createNotification = async (
43
  input: CreateNotificationInput
44
): Promise<Notification | null> => {
45
  const { userId, kind, title, body, scheduledFor, regimenId = null } = input;
46

47
  const user = await prisma.user.findUnique({
48
    where: { id: userId },
49
    select: { pushToken: true, notificationsEnabled: true },
50
  });
51

52
  if (!user) {
53
    logger.warn({ userId, kind }, "[notification] user not found, skipping");
54

55
    return null;
56
  }
57

58
  const rule = notificationRules[kind];
59

60
  const notification = await prisma.notification.create({
61
    data: {
62
      userId,
63
      kind,
64
      title,
65
      body,
66
      scheduledFor,
67
      regimenId,
68
      expiresAt: rule.expiresAt(scheduledFor),
69
    },
70
  });
71

72
  if (!user.notificationsEnabled || !user.pushToken) {
73
    logger.info(
74
      { notificationId: notification.id, userId, kind },
75
      "[notification] stored without push"
76
    );
77

78
    return notification;
79
  }
80

81
  const data = {
82
    ...(rule.screen ? { screen: rule.screen } : {}),
83
    ...(regimenId !== null ? { regimenId } : {}),
84
  };
85

86
  try {
87
    await sendPushNotification({
88
      token: user.pushToken,
89
      title,
90
      body,
91
      data,
92
    });
93
  } catch (error) {
94
    logger.error(
95
      { notificationId: notification.id, userId, err: error },
96
      "[notification] push failed"
97
    );
98

99
    return notification;
100
  }
101

102
  logger.info(
103
    { notificationId: notification.id, userId, kind },
104
    "[notification] push sent"
105
  );
106

107
  return prisma.notification.update({
108
    where: { id: notification.id },
109
    data: { pushedAt: new Date() },
110
  });
111
};