feat: show latest particle preview in stream list
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
import { useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Radio } from "lucide-react";
|
||||
import { useLiveParticleChildren } from "@/hooks/use-particle";
|
||||
import { parseParticlePath, type ParticlePath } from "@/lib/particle-path";
|
||||
import { useLiveParticleChildren, useLiveLatestChild } from "@/hooks/use-particle";
|
||||
import {
|
||||
parseParticlePath,
|
||||
particlePath,
|
||||
type ParticlePath,
|
||||
} from "@/lib/particle-path";
|
||||
import { getInitials } from "@/lib/utils";
|
||||
import { formatDistanceToNow } from "@/lib/time-utils";
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
@@ -10,14 +16,42 @@ import { Progress } from "@/components/ui/progress";
|
||||
import { Small } from "@/components/ui/typography";
|
||||
import type { Particle, StreamProperties } from "@/api/types";
|
||||
|
||||
function getMessagePreview(particle: Particle): string {
|
||||
switch (particle.type) {
|
||||
case "text":
|
||||
return particle.properties.content;
|
||||
case "media":
|
||||
return "Clip received";
|
||||
case "file":
|
||||
return particle.properties.filename;
|
||||
case "quest":
|
||||
return particle.properties.title;
|
||||
case "paper":
|
||||
return particle.properties.title;
|
||||
default:
|
||||
return particle.type;
|
||||
}
|
||||
}
|
||||
|
||||
function StreamRow({
|
||||
particle,
|
||||
networkId,
|
||||
onClick,
|
||||
}: {
|
||||
particle: Particle & { type: "stream"; properties: StreamProperties };
|
||||
networkId: string;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const initials = particle.properties.name.slice(0, 2).toUpperCase();
|
||||
const streamPath = particlePath(networkId, [particle.id]);
|
||||
const { latestChild } = useLiveLatestChild(streamPath);
|
||||
|
||||
const initials = latestChild
|
||||
? getInitials(latestChild.created_by_email)
|
||||
: particle.properties.name.slice(0, 2).toUpperCase();
|
||||
|
||||
const subtitle = latestChild
|
||||
? getMessagePreview(latestChild)
|
||||
: particle.properties.status;
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -31,13 +65,20 @@ function StreamRow({
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium">
|
||||
{particle.properties.name}
|
||||
</p>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="truncate text-sm font-medium">
|
||||
{particle.properties.name}
|
||||
</p>
|
||||
{latestChild && (
|
||||
<Small className="text-muted-foreground shrink-0">
|
||||
{formatDistanceToNow(latestChild.created_at.toISOString())}
|
||||
</Small>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-muted-foreground flex items-center gap-1">
|
||||
<Radio className="size-3" />
|
||||
<Small className="text-muted-foreground">
|
||||
{particle.properties.status}
|
||||
{!latestChild && <Radio className="size-3" />}
|
||||
<Small className="text-muted-foreground truncate">
|
||||
{subtitle}
|
||||
</Small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,6 +114,7 @@ export function ParticleListView({ path }: ParticleListViewProps) {
|
||||
<div key={stream.id}>
|
||||
<StreamRow
|
||||
particle={stream}
|
||||
networkId={networkId}
|
||||
onClick={() => navigate(`/${networkId}/${stream.id}`)}
|
||||
/>
|
||||
{index < streams.length - 1 && <Separator className="mx-4" />}
|
||||
|
||||
Reference in New Issue
Block a user