import {
ActivityIndicator,
FlatList,
Pressable,
RefreshControl,
Text,
View,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { toast } from "sonner-native";
import { toUserMessage } from "@/lib/errors";
import { particlePath } from "@/lib/particle-path";
import { useNetwork, useNetworks } from "@/hooks/use-networks";
import { useStreamParticles } from "@/hooks/use-stream-particles";
import type { RootStackScreenProps } from "@/navigation/types";
import { StreamCard } from "./StreamCard";
export function StreamListScreen({
route,
navigation,
}: RootStackScreenProps<"StreamList">) {
const { networkId } = route.params;
const network = useNetwork(networkId);
// Re-fetching the network list is the closest stand-in for a hard refresh —
// Firestore subscriptions are already realtime, so pull-to-refresh mainly
// reassures the user and re-tries network metadata.
const { refetch: refetchNetworks, isRefetching: isRefetchingNetworks } =
useNetworks();
const path = particlePath(networkId, []);
// v1: only show open streams. Closed streams are reachable on desktop —
// an "Archived" surface is a follow-up (PRD §20).
const { streams, isLoading, error } = useStreamParticles(path, {
status: "open",
});
return (
navigation.goBack()}
/>
{error ? (
) : isLoading && streams.length === 0 ? (
) : streams.length === 0 ? (
) : (
s.id}
contentContainerClassName="p-3 gap-2 pb-24"
refreshControl={
refetchNetworks()}
/>
}
renderItem={({ item }) => (
navigation.navigate("StreamView", {
networkId,
streamId: item.id,
})
}
/>
)}
/>
)}
{
// Real compose flow lands in step 5.
toast("New stream — coming soon");
}}
/>
);
}
function Header({
title,
onBack,
}: {
title: string;
onBack: () => void;
}) {
return (
‹
{title}
);
}
function LoadingState() {
return (
);
}
function EmptyState() {
return (
No streams yet.
Tap the button below to start one — voice, video, or text.
);
}
function ErrorState({ message }: { message: string }) {
return (
{message}
Streams reconnect automatically once the network is back.
);
}
function ComposeFab({ onPress }: { onPress: () => void }) {
return (
+
);
}