* chore: only set visibility for container particles * create reusable controls indicator for reply or new * compress the size of top bar * refactor: restructure state, routing, and more * introduce stream compose flow * feat: compose new stream full flow * implement stream player * fix: prevent redirect for signed object urls * fix: implement stream playback cleaner structure * refactor: layout file name * feat: show stream name in breadcrumbs * chore: tweak padding * chore: adjust position of audio bars * feat: show latest particle preview in stream list * fix: remove console log * refactor: reorder classes * fix: avoid passing in updated_at to firestore particle * refactor: extract properties for container particles to flat fields in firestore * make the stream previews look alive * feat: show audio bars during audio clip playback * feat: order streams by last child creation * feat: playback where I left off * chore: remove unused store * fix: recording mode not using shared state * chore: clean unused variable * remove unused imports * fix: improve controls indicator immersion * feat: show playback progress in bar & auto-play text * feat: auto-exit stream on playback completion * fix: jittery media playback progress * fix: navigate during state change is invalid with react router * fix: buggy exit progress when changing clips * feat: add app icon * update package.json info * feat: only show streams visible to me * feat: show seen indicator on particles * fix: prevent unnecessary effects * fix: play new particle after playback is ended * use contols indicator for exit timer
93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import { useNavigate } from "react-router-dom";
|
|
import { Users } from "lucide-react";
|
|
import { Small } from "@/components/ui/typography";
|
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { useNetworks } from "@/hooks/use-networks";
|
|
import { Progress } from "@/components/ui/progress";
|
|
import type { Network } from "@/api/types";
|
|
|
|
function NetworkRow({
|
|
network,
|
|
onClick,
|
|
}: {
|
|
network: Network;
|
|
onClick: () => void;
|
|
}) {
|
|
const initials = network.name.slice(0, 2).toUpperCase();
|
|
const memberCount = network.humans.length;
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={onClick}
|
|
className="flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent"
|
|
>
|
|
<Avatar>
|
|
<AvatarFallback className="bg-primary/10 text-primary font-medium">
|
|
{initials}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="truncate text-sm font-medium">{network.name}</p>
|
|
<div className="text-muted-foreground flex items-center gap-1">
|
|
<Users className="size-3" />
|
|
<Small className="text-muted-foreground">
|
|
{memberCount} {memberCount === 1 ? "member" : "members"}
|
|
</Small>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
);
|
|
}
|
|
|
|
export default function NetworkSelector() {
|
|
const navigate = useNavigate();
|
|
|
|
const { data, isPending, error } = useNetworks();
|
|
|
|
if (isPending) {
|
|
return <Progress />;
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="flex h-full items-center justify-center">
|
|
<p className="text-destructive text-sm">Failed to load networks</p>
|
|
<p>{error.message}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (data?.length === 0) {
|
|
return (
|
|
<div className="mx-auto flex h-full max-w-sm flex-col items-center justify-center gap-4 px-4 text-center">
|
|
<p className="text-muted-foreground">
|
|
You don't have access to any networks yet. Please email us to get
|
|
started.
|
|
</p>
|
|
<a href="mailto:[email protected]" className="text-primary underline">
|
|
team@flowylabs.ai
|
|
</a>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ScrollArea className="h-full">
|
|
<div className="py-1">
|
|
{data?.map((network, index) => (
|
|
<div key={network.id}>
|
|
<NetworkRow
|
|
network={network}
|
|
onClick={() => navigate(`/${network.id}`)}
|
|
/>
|
|
{index < data.length - 1 && <Separator className="mx-4" />}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</ScrollArea>
|
|
);
|
|
}
|