Skip to content

Commit

Permalink
feat(ts/notifications): create notification card for detours
Browse files Browse the repository at this point in the history
  • Loading branch information
firestack committed Sep 26, 2024
1 parent 8d46a21 commit 998f950
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 7 deletions.
10 changes: 9 additions & 1 deletion assets/src/components/notificationBellIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -14,8 +16,14 @@ 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

Expand Down
39 changes: 36 additions & 3 deletions assets/src/components/notificationCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactElement } from "react"
import React, { ReactNode } from "react"
import { useRoute, useRoutes } from "../contexts/routesContext"
import {
BlockWaiverNotification,
Expand All @@ -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,
Expand All @@ -24,7 +26,7 @@ export const NotificationCard = ({
openVPPForCurrentVehicle: (notification: Notification) => void
hideLatestNotification?: () => void
noFocusOrHover?: boolean
}): ReactElement<HTMLElement> => {
}) => {
const routes = useRoutes(
isBlockWaiverNotification(notification) ? notification.content.routeIds : []
)
Expand All @@ -33,6 +35,14 @@ export const NotificationCard = ({
? notification.content.routeIdAtCreation
: null
)

if (
notification.content.$type === NotificationType.Detour &&
!inTestGroup(TestGroups.DetoursList)
) {
return null
}

const isUnread = notification.state === "unread"
return (
<CardReadable
Expand Down Expand Up @@ -87,6 +97,11 @@ export const title = (notification: Notification) => {
case NotificationType.BlockWaiver: {
return blockWaiverNotificationTitle(notification.content.reason)
}

case NotificationType.Detour: {
return "Detour - Active"
}

case NotificationType.BridgeMovement: {
switch (notification.content.status) {
case "lowered":
Expand Down Expand Up @@ -156,7 +171,7 @@ const description = (
notification: Notification,
routes: Route[],
routeAtCreation: Route | null
): string => {
): ReactNode => {
switch (notification.content.$type) {
case NotificationType.BlockWaiver: {
return blockWaiverDescription(
Expand All @@ -165,6 +180,24 @@ const description = (
routeAtCreation
)
}

case NotificationType.Detour: {
return (
<>
<div className="d-flex flex-row gap-2">
<RoutePill routeName={notification.content.route} />
<div>
<div className="fw-semibold">{notification.content.headsign}</div>
<div className="fw-normal text-body-secondary">
From {notification.content.origin.split(" - ")[0]}
</div>
<div className="fw-normal">{notification.content.direction}</div>
</div>
</div>
</>
)
}

case NotificationType.BridgeMovement: {
switch (notification.content.status) {
case "raised":
Expand Down
6 changes: 4 additions & 2 deletions assets/src/models/detoursList.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
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
intersection: string
updatedAt: number
}

export const detourId = number()
export const SimpleDetourData = type({
id: number(),
id: detourId,
route: string(),
direction: string(),
name: string(),
Expand Down
30 changes: 29 additions & 1 deletion assets/src/models/notificationData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
NotificationContentTypes,
NotificationType,
} from "../realtime"
import { detourId } from "./detoursList"

const dateFromSeconds = coerce(
date(),
Expand Down Expand Up @@ -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<typeof DetourNotificationData>

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<typeof NotificationData>

Expand Down Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions assets/src/realtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "./schedule"

import { Crowding } from "./models/crowding"
import { DetourId } from "./models/detoursList"

export interface BlockWaiver {
startTime: Date
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
> {
Expand Down

0 comments on commit 998f950

Please sign in to comment.