From d10de739b3d154829dffb5208f19fcdb4bd4295c Mon Sep 17 00:00:00 2001 From: talksik Date: Mon, 30 Mar 2026 13:33:02 -0700 Subject: [PATCH] show indicator of stream expiring soon --- .../features/particles/particle-list-view.tsx | 32 +++++++++++++------ js/src/features/particles/stream-card.tsx | 10 ++++++ js/src/hooks/use-expiring-soon.ts | 20 ++++++++++++ 3 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 js/src/hooks/use-expiring-soon.ts diff --git a/js/src/features/particles/particle-list-view.tsx b/js/src/features/particles/particle-list-view.tsx index 8093e5b..786afab 100644 --- a/js/src/features/particles/particle-list-view.tsx +++ b/js/src/features/particles/particle-list-view.tsx @@ -9,6 +9,7 @@ import { FileText, CircleCheck, StickyNote, + Timer, type LucideIcon, } from "lucide-react"; import { cn } from "@/lib/utils"; @@ -26,6 +27,7 @@ import { Progress } from "@/components/ui/progress"; import { Small } from "@/components/ui/typography"; import type { Particle, StreamProperties } from "@/api/types"; import { useNetwork } from "@/hooks/use-networks"; +import { useExpiringSoon } from "@/hooks/use-expiring-soon"; import { useStreamParticles } from "@/hooks/use-stream-particles"; import { useStreamAutoplay } from "@/hooks/use-stream-autoplay"; @@ -94,6 +96,11 @@ function StreamRow({ useStreamAutoplay(latestChild, particle); + const expiringSoon = useExpiringSoon( + particle.last_child_created_at, + network?.message_retention_hours ?? 24, + ); + const isDM = particle.visible_to.length === 2 && particle.visible_to.every((v) => v.startsWith("human:")); @@ -181,16 +188,21 @@ function StreamRow({ > {particle.properties.name}

- {latestChild && ( - - - - )} +
+ {expiringSoon && ( + + )} + {latestChild && ( + + + + )} +
(function S useStreamAutoplay(latestChild, particle); + const expiringSoon = useExpiringSoon( + particle.last_child_created_at, + network?.message_retention_hours ?? 24, + ); + const isDM = particle.visible_to.length === 2 && particle.visible_to.every((v) => v.startsWith("human:")); @@ -134,6 +141,9 @@ export const StreamCard = forwardRef(function S {particle.properties.name}
+ {expiringSoon && ( + + )} {latestChild && ( { + if (!lastChildCreatedAt) return false; + + const retentionMs = retentionHours * 60 * 60 * 1000; + const expiresAt = lastChildCreatedAt.getTime() + retentionMs; + const remaining = expiresAt - Date.now(); + + return remaining > 0 && remaining < retentionMs * 0.1; + }, [lastChildCreatedAt, retentionHours]); +}