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]); +}