Skip to content
59 linesCodeBlameRaw
1import { Stack, router } from "expo-router";
2import { Hash } from "lucide-react-native";
3import { FlatList, RefreshControl } from "react-native";
4
5import { Badge, Button, Empty, ErrorView, Loading, Row, Screen } from "@/components/ui";
6import { useBrowseChannels, useChatSidebar, useStartConversation } from "@/lib/chat";
7import { useColors } from "@/theme";
8
9/** The workspace's public channels: open one you are in, or join one you are not. */
10export default function Browse() {
11 const channels = useBrowseChannels();
12 const sidebar = useChatSidebar();
13 const { join } = useStartConversation();
14 const colors = useColors();
15 const mine = new Set((sidebar.data?.entries ?? []).map((entry) => entry.channel.id));
16
17 return (
18 <Screen>
19 <Stack.Screen options={{ title: "Browse channels" }} />
20 {channels.isPending ? (
21 <Loading />
22 ) : channels.error ? (
23 <ErrorView error={channels.error} retry={() => channels.refetch()} />
24 ) : (
25 <FlatList
26 data={channels.data}
27 keyExtractor={(channel) => channel.id}
28 refreshControl={<RefreshControl refreshing={channels.isRefetching} onRefresh={() => channels.refetch()} tintColor={colors.accent} />}
29 ListEmptyComponent={<Empty icon={Hash} title="No other channels" />}
30 renderItem={({ item }) => {
31 const joined = mine.has(item.id);
32 const open = () => router.replace({ pathname: "/chat/[channel]", params: { channel: item.id } });
33 return (
34 <Row
35 leading={<Hash size={20} color={colors.muted} />}
36 title={item.name ?? ""}
37 subtitle={item.topic ?? undefined}
38 chevron={false}
39 onPress={joined ? open : undefined}
40 trailing={
41 joined ? (
42 <Badge label="Joined" />
43 ) : (
44 <Button
45 title="Join"
46 variant="secondary"
47 busy={join.isPending && join.variables === item.id}
48 onPress={() => join.mutate(item.id, { onSuccess: open })}
49 />
50 )
51 }
52 />
53 );
54 }}
55 />
56 )}
57 </Screen>
58 );
59}