diff --git a/assets/src/components/notificationBellIcon.tsx b/assets/src/components/notificationBellIcon.tsx index 7567bba2c..fcba8be03 100644 --- a/assets/src/components/notificationBellIcon.tsx +++ b/assets/src/components/notificationBellIcon.tsx @@ -4,6 +4,8 @@ import { joinClasses } from "../helpers/dom" import { NotificationBellIcon as NotificationBellIconSvg } from "../helpers/icon" import { OpenView } from "../state/pagePanelState" import { usePanelStateFromStateDispatchContext } from "../hooks/usePanelState" +import inTestGroup, { TestGroups } from "../userInTestGroup" +import { NotificationType } from "../realtime" const NotificationBellIcon = ({ extraClasses, @@ -14,8 +16,10 @@ const NotificationBellIcon = ({ currentView: { openView }, } = usePanelStateFromStateDispatchContext() const { notifications } = useContext(NotificationsContext) + + const inDetoursList = inTestGroup(TestGroups.DetoursList) const unreadNotifications = (notifications || []).filter( - (notification) => notification.state === "unread" + (notification) => notification.state === "unread" && !(notification.content.$type === NotificationType.Detour && !inDetoursList) ) const unreadBadge: boolean = unreadNotifications.length > 0 diff --git a/assets/src/components/notificationCard.tsx b/assets/src/components/notificationCard.tsx index df85711fd..24ff33595 100644 --- a/assets/src/components/notificationCard.tsx +++ b/assets/src/components/notificationCard.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement } from "react" +import React, { ReactNode } from "react" import { useRoute, useRoutes } from "../contexts/routesContext" import { BlockWaiverNotification, @@ -11,6 +11,8 @@ import { Route } from "../schedule" import { formattedTime } from "../util/dateTime" import { CardBody, CardProperties, CardReadable } from "./card" import { fullStoryEvent } from "../helpers/fullStory" +import { RoutePill } from "./routePill" +import inTestGroup, { TestGroups } from "../userInTestGroup" export const NotificationCard = ({ notification, @@ -24,7 +26,7 @@ export const NotificationCard = ({ openVPPForCurrentVehicle: (notification: Notification) => void hideLatestNotification?: () => void noFocusOrHover?: boolean -}): ReactElement => { +}) => { const routes = useRoutes( isBlockWaiverNotification(notification) ? notification.content.routeIds : [] ) @@ -33,6 +35,11 @@ export const NotificationCard = ({ ? notification.content.routeIdAtCreation : null ) + + if (notification.content.$type === NotificationType.Detour && !inTestGroup(TestGroups.DetoursList)) { + return null + } + const isUnread = notification.state === "unread" return ( { return "Chelsea St Bridge Raised" } } + + case NotificationType.Detour: { + return "Detour - Active" + } } } export const blockWaiverNotificationTitle = ( @@ -156,7 +167,7 @@ const description = ( notification: Notification, routes: Route[], routeAtCreation: Route | null -): string => { +): ReactNode => { switch (notification.content.$type) { case NotificationType.BlockWaiver: { return blockWaiverDescription( @@ -176,5 +187,22 @@ const description = ( return "OCC reported that the Chelsea St bridge has been lowered." } } + + case NotificationType.Detour: { + return ( + <> +
+ +
+
{notification.content.headsign}
+
+ From {notification.content.origin.split(" - ")[0]} +
+
{notification.content.direction}
+
+
+ + ) + } } } diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts index 92a331eda..8df56eb96 100644 --- a/assets/src/models/detoursList.ts +++ b/assets/src/models/detoursList.ts @@ -1,7 +1,8 @@ import { array, Infer, nullable, number, string, type } from "superstruct" +export type DetourId = number export interface SimpleDetour { - id: number + id: DetourId route: string direction: string name: string @@ -9,8 +10,9 @@ export interface SimpleDetour { updatedAt: number } +export const detourId = number() export const SimpleDetourData = type({ - id: number(), + id: detourId, route: string(), direction: string(), name: string(), diff --git a/assets/src/models/notificationData.ts b/assets/src/models/notificationData.ts index 3b47f6740..602a80c83 100644 --- a/assets/src/models/notificationData.ts +++ b/assets/src/models/notificationData.ts @@ -19,6 +19,7 @@ import { NotificationContentTypes, NotificationType, } from "../realtime" +import { detourId } from "./detoursList" const dateFromSeconds = coerce( date(), @@ -65,12 +66,27 @@ export const BridgeNotificationData = union([ }), ]) +export const DetourNotificationData = type({ + __struct__: literal(NotificationType.Detour), + detour_id: detourId, + headsign: string(), + route: string(), + direction: string(), + origin: string(), +}) + +export type DetourNotificationData = Infer + export const NotificationData = type({ id: coerce(string(), number(), (i) => i.toString()), created_at: dateFromSeconds, state: enums(["unread", "read", "deleted"]), // Requires a field named `__struct__` to be present as the discriminator - content: union([BridgeNotificationData, BlockWaiverNotificationData]), + content: union([ + BridgeNotificationData, + BlockWaiverNotificationData, + DetourNotificationData, + ]), }) export type NotificationData = Infer @@ -116,6 +132,18 @@ export const notificationFromData = ( } break } + + case NotificationType.Detour: { + content = { + $type: NotificationType.Detour, + detourId: notificationData.content.detour_id, + direction: notificationData.content.direction, + headsign: notificationData.content.headsign, + origin: notificationData.content.origin, + route: notificationData.content.route, + } + break + } } return { diff --git a/assets/src/realtime.ts b/assets/src/realtime.ts index 6cfb26e4c..e8a817e44 100644 --- a/assets/src/realtime.ts +++ b/assets/src/realtime.ts @@ -10,6 +10,7 @@ import { } from "./schedule" import { Crowding } from "./models/crowding" +import { DetourId } from "./models/detoursList" export interface BlockWaiver { startTime: Date @@ -59,6 +60,7 @@ export type NotificationId = string export enum NotificationType { BridgeMovement = "Elixir.Notifications.Db.BridgeMovement", BlockWaiver = "Elixir.Notifications.Db.BlockWaiver", + Detour = "Elixir.Notifications.Db.Detour", } export interface BridgeLoweredNotification { @@ -90,9 +92,20 @@ export type BlockWaiverNotification = { endTime: Date | null } +export type DetourNotification = { + $type: NotificationType.Detour + detourId: DetourId + headsign: string + route: string + direction: string + origin: string +} + export type NotificationContentTypes = | BridgeNotification | BlockWaiverNotification + | DetourNotification + export interface Notification< TNotification extends NotificationContentTypes = NotificationContentTypes > {