import { GROUP_MEMBER_MAX, GROUP_MEMBER_MIN } from "@rakazo/contracts"; import { botAvatarImageSrc } from "@rakazo/core"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useEffect, useState } from "react"; import { Alert, Pressable, ScrollView, Text, TextInput } from "react-native"; import { BotAvatar } from "../components/bot-avatar"; import { type MobileBot, type MobileGroup, rpc } from "../lib/api"; export default function GroupSettingsScreen() { const router = useRouter(); const { groupId } = useLocalSearchParams<{ groupId: string }>(); const [group, setGroup] = useState(null); const [bots, setBots] = useState([]); const [name, setName] = useState(""); const [selected, setSelected] = useState([]); const [error, setError] = useState(null); const [pending, setPending] = useState(false); useEffect(() => { if (!groupId) return; void Promise.all([ rpc("groups/list").then( (groups) => groups.find((row) => row.id === groupId) ?? null, ), rpc("bots/list"), ]) .then(([nextGroup, nextBots]) => { if (!nextGroup) throw new Error("Group not found"); setGroup(nextGroup); setName(nextGroup.name); setSelected(nextGroup.members.map((member) => member.botId)); setBots(nextBots.filter((bot) => !bot.archivedAt)); }) .catch((err) => setError(err instanceof Error ? err.message : "Could not load group")); }, [groupId]); function toggle(botId: string) { setSelected((current) => { if (current.includes(botId)) return current.filter((id) => id !== botId); if (current.length >= GROUP_MEMBER_MAX) return current; return [...current, botId]; }); } async function save() { if (!groupId || !group || pending) return; setPending(true); setError(null); try { const input: { groupId: string; name?: string; botIds?: string[] } = { groupId }; if (name.trim() !== group.name) input.name = name.trim(); const memberIds = group.members.map((member) => member.botId).join(","); if (selected.join(",") !== memberIds) input.botIds = selected; if (input.name || input.botIds) await rpc("groups/update", input); router.back(); } catch (err) { setError(err instanceof Error ? err.message : "Could not save group"); } finally { setPending(false); } } function remove() { if (!groupId || !group) return; Alert.alert(group.name, "Delete this group? Bots and their solo threads are kept.", [ { text: "Cancel", style: "cancel" }, { text: "Delete", style: "destructive", onPress: () => void rpc("groups/remove", { groupId }) .then(() => router.replace("/")) .catch((err) => Alert.alert( "Could not delete group", err instanceof Error ? err.message : "Try again.", ), ), }, ]); } return ( <> Name Members ({GROUP_MEMBER_MIN}–{GROUP_MEMBER_MAX}) {bots.map((bot) => { const checked = selected.includes(bot.id); return ( toggle(bot.id)} style={{ flexDirection: "row", alignItems: "center", gap: 12, paddingVertical: 12 }} > {bot.name} {checked ? "✓" : ""} ); })} {error ? {error} : null} void save()} disabled={ !name.trim() || selected.length < GROUP_MEMBER_MIN || selected.length > GROUP_MEMBER_MAX || pending } style={{ marginTop: 24, backgroundColor: "#8B5CF6", opacity: !name.trim() || selected.length < GROUP_MEMBER_MIN || selected.length > GROUP_MEMBER_MAX || pending ? 0.5 : 1, borderRadius: 11, padding: 14, alignItems: "center", }} > {pending ? "Saving…" : "Save"} Delete group ); }