eletrotupi / tcc/ frontend/components/notifications/NotificationRow.tsxf8f41f3d02a26b63d278ed849f86e913d1ea381b
3.2 KB | 125 lines Raw
1
import { View, StyleSheet } from "react-native";
2
import { router, type Href } from "expo-router";
3
import { MaterialIcons } from "@expo/vector-icons";
4

5
import { Text } from "@/components/ui/Text";
6
import { Card } from "@/components/ui/Cards";
7
import { Badge } from "@/components/ui/Badge";
8
import { NOTIFICATION_KINDS } from "@/constants/notification-kinds";
9
import { formatMoment } from "@/lib/utils/time";
10
import { Spacing } from "@/constants/theme";
11
import type { Notification } from "@/lib/api";
12

13
type Props = {
14
  notification: Notification;
15
};
16

17
// A row is history once its relevance window has closed; a notification with no
18
// window (an insight alert) never expires
19
const isExpired = (notification: Notification): boolean =>
20
  notification.expiresAt !== null &&
21
  new Date(notification.expiresAt).getTime() <= Date.now();
22

23
// The same deep-link shape the push observer uses: a pathname plus params
24
function destination(notification: Notification): Href {
25
  if (notification.kind === "MEDICINE_REMINDER" && notification.regimenId !== null) {
26
    return {
27
      pathname: "/medicine-regimens/[id]",
28
      params: { id: String(notification.regimenId) },
29
    } as Href;
30
  }
31

32
  if (notification.kind === "MEDICINE_REMINDER") {
33
    return { pathname: "/medicine-regimens" } as Href;
34
  }
35

36
  if (notification.kind === "DAILY_REMINDER") {
37
    return { pathname: "/new/entry" } as Href;
38
  }
39

40
  return { pathname: "/insights" } as Href;
41
}
42

43
export function NotificationRow({ notification }: Props) {
44
  const meta = NOTIFICATION_KINDS[notification.kind];
45
  const expired = isExpired(notification);
46

47
  return (
48
    <Card
49
      style={styles.card}
50
      testID={`notification-${notification.id}`}
51
      onPress={() => router.push(destination(notification))}
52
    >
53
      <View style={[styles.row, expired && styles.expired]}>
54
        <View style={[styles.iconWrap, { backgroundColor: meta.iconBackground }]}>
55
          <MaterialIcons name={meta.icon} size={22} color={meta.color} />
56
        </View>
57

58
        <View style={styles.body}>
59
          <View style={styles.titleRow}>
60
            <Text style={styles.title} numberOfLines={1}>
61
              {notification.title}
62
            </Text>
63

64
            {expired && <Badge label="Expirada" variant="gray" />}
65
          </View>
66

67
          <Text style={styles.summary} numberOfLines={2}>
68
            {notification.body}
69
          </Text>
70

71
          <Text style={styles.moment}>
72
            {formatMoment(new Date(notification.scheduledFor))}
73
          </Text>
74
        </View>
75
      </View>
76
    </Card>
77
  );
78
}
79

80
const styles = StyleSheet.create({
81
  card: {
82
    padding: Spacing.cardGap,
83
  },
84
  row: {
85
    flexDirection: "row",
86
    alignItems: "flex-start",
87
    gap: 12,
88
  },
89
  expired: {
90
    opacity: 0.55,
91
  },
92
  iconWrap: {
93
    width: 40,
94
    height: 40,
95
    borderRadius: 20,
96
    alignItems: "center",
97
    justifyContent: "center",
98
    marginTop: 1,
99
  },
100
  body: {
101
    flex: 1,
102
  },
103
  titleRow: {
104
    flexDirection: "row",
105
    justifyContent: "space-between",
106
    alignItems: "center",
107
    gap: 8,
108
    marginBottom: 2,
109
  },
110
  title: {
111
    flexShrink: 1,
112
    fontSize: 15,
113
    fontWeight: "600",
114
  },
115
  summary: {
116
    fontSize: 13,
117
    opacity: 0.6,
118
    lineHeight: 18,
119
  },
120
  moment: {
121
    fontSize: 12,
122
    opacity: 0.45,
123
    marginTop: 4,
124
  },
125
});