From c3dc700b3a048c3ca4685eb3cb5cb71e37800d8c Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Mon, 27 Nov 2023 11:56:46 -0600 Subject: [PATCH] group notifications by day --- src/views/notifications/index.tsx | 60 ++++++++++++++++--- src/views/notifications/notification-item.tsx | 4 +- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/views/notifications/index.tsx b/src/views/notifications/index.tsx index b1ba41341..80901bb10 100644 --- a/src/views/notifications/index.tsx +++ b/src/views/notifications/index.tsx @@ -1,19 +1,54 @@ -import { useEffect, useMemo } from "react"; -import { Flex, useDisclosure } from "@chakra-ui/react"; +import { useEffect, useMemo, useRef } from "react"; +import { Divider, Flex, Heading, useDisclosure } from "@chakra-ui/react"; import { Kind } from "nostr-tools"; import RequireCurrentAccount from "../../providers/require-current-account"; import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status"; -import IntersectionObserverProvider from "../../providers/intersection-observer"; +import IntersectionObserverProvider, { useRegisterIntersectionEntity } from "../../providers/intersection-observer"; import useSubject from "../../hooks/use-subject"; import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback"; import { useNotificationTimeline } from "../../providers/notification-timeline"; -import { isReply } from "../../helpers/nostr/events"; +import { getEventUID, isReply } from "../../helpers/nostr/events"; import PeopleListProvider, { usePeopleListContext } from "../../providers/people-list-provider"; import PeopleListSelection from "../../components/people-list-selection/people-list-selection"; import VerticalPageLayout from "../../components/vertical-page-layout"; -import NotificationItem from "./notification-item"; +import NotificationItem, { ExpandableToggleButton } from "./notification-item"; import NotificationTypeToggles from "./notification-type-toggles"; +import { NostrEvent } from "../../types/nostr-event"; +import dayjs from "dayjs"; +import SuperMap from "../../classes/super-map"; + +const specialNames = { + [dayjs().startOf("day").unix()]: "Today", + [dayjs().subtract(1, "day").startOf("day").unix()]: "Yesterday", +}; + +function NotificationDay({ day, events }: { day: number; events: NostrEvent[] }) { + const expanded = useDisclosure({ defaultIsOpen: true }); + const now = dayjs(); + const date = dayjs.unix(day); + let title = specialNames[day] || date.fromNow(); + if (now.diff(date, "week") > 2) { + title = date.format("L"); + } + + const ref = useRef(null); + useRegisterIntersectionEntity(ref, expanded.isOpen ? undefined : getEventUID(events[events.length - 1])); + + return ( + <> + + + + {title} + + + + + {expanded.isOpen && events.map((event) => )} + + ); +} function NotificationsPage() { const { people } = usePeopleListContext(); @@ -54,6 +89,17 @@ function NotificationsPage() { return true; }); + const grouped = useMemo(() => { + const map = new SuperMap(() => []); + for (const event of events) { + const day = dayjs.unix(event.created_at).startOf("day").unix(); + map.get(day).push(event); + } + return map; + }, [events]); + + const sortedDays = Array.from(grouped.entries()).sort((a, b) => b[0] - a[0]); + return ( @@ -68,8 +114,8 @@ function NotificationsPage() { - {events.map((event) => ( - + {sortedDays.map(([day, events]) => ( + ))} diff --git a/src/views/notifications/notification-item.tsx b/src/views/notifications/notification-item.tsx index f10299954..acd0bcf7c 100644 --- a/src/views/notifications/notification-item.tsx +++ b/src/views/notifications/notification-item.tsx @@ -7,7 +7,7 @@ import { NostrEvent, isATag, isETag } from "../../types/nostr-event"; import { useRegisterIntersectionEntity } from "../../providers/intersection-observer"; import { parseZapEvent } from "../../helpers/nostr/zaps"; import { readablizeSats } from "../../helpers/bolt11"; -import { getEventUID, getReferences, isMentionedInContent, isReply, parseCoordinate } from "../../helpers/nostr/events"; +import { getEventUID, getReferences, isMentionedInContent, parseCoordinate } from "../../helpers/nostr/events"; import { EmbedEvent, EmbedEventPointer } from "../../components/embed-event"; import EmbeddedUnknown from "../../components/embed-event/event-types/embedded-unknown"; import { ErrorBoundary } from "../../components/error-boundary"; @@ -22,7 +22,7 @@ const IconBox = ({ children }: PropsWithChildren) => ( {children} ); -const ExpandableToggleButton = ({ +export const ExpandableToggleButton = ({ toggle, ...props }: { toggle: { isOpen: boolean; onToggle: () => void } } & Omit) => (