path = "src/screens/GroupSettingsScreen.js" with open(path, "r", encoding="utf-8") as f: src = f.read() edits = [] # 1. imports edits.append(( '''import { View, Text, TouchableOpacity, StyleSheet, ScrollView, Switch, Alert, ActivityIndicator, Modal, Share } from 'react-native'; import { BlurView } from 'expo-blur'; import { useRoute, useNavigation } from '@react-navigation/native'; import { ChevronLeft, BadgeCheck, X } from 'lucide-react-native';''', '''import { View, Text, TouchableOpacity, StyleSheet, ScrollView, Switch, Alert, ActivityIndicator, Modal, Share, FlatList, TextInput } from 'react-native'; import { BlurView } from 'expo-blur'; import { useRoute, useNavigation } from '@react-navigation/native'; import { ChevronLeft, BadgeCheck, X, UserPlus, Check } from 'lucide-react-native';''' )) # 2. state edits.append(( ''' const [inviteVisible, setInviteVisible] = useState(false); const [inviteCode, setInviteCode] = useState(null);''', ''' const [inviteVisible, setInviteVisible] = useState(false); const [inviteCode, setInviteCode] = useState(null); const [addFriendsVisible, setAddFriendsVisible] = useState(false); const [friends, setFriends] = useState([]); const [friendSearch, setFriendSearch] = useState(''); const [selectedFriendIds, setSelectedFriendIds] = useState([]); const [addingMembers, setAddingMembers] = useState(false);''' )) # 3. showAddFriendsRow flag edits.append(( ''' function guardGroupId() {''', ''' const showAddFriendsRow = canManage || editPerm === 'all'; function guardGroupId() {''' )) # 4. handler functions edits.append(( ''' function confirmClearHistory() {''', ''' async function openAddFriends() { if (!guardGroupId()) return; setFriendSearch(''); setSelectedFriendIds([]); setAddFriendsVisible(true); try { const res = await apiRequest('/friends/list'); const memberIds = members.map(m => String(m.id)); const list = (res?.friends || []) .filter(f => !memberIds.includes(String(f.id))) .map(f => ({ id: f.id, username: f.username || f.handle || 'Friend', verified: f.verified })); setFriends(list); } catch (e) { Alert.alert('Error', e.message || "Couldn't load your friends list."); } } function toggleFriendSelect(id) { setSelectedFriendIds(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); } async function submitAddFriends() { if (!selectedFriendIds.length) return; setAddingMembers(true); try { await apiRequest(`/groups/${groupId}/members`, { method: 'POST', body: JSON.stringify({ member_ids: selectedFriendIds }), }); setAddFriendsVisible(false); await load(); } catch (e) { Alert.alert('Error', e.message || "Couldn't add those friends to the group."); } finally { setAddingMembers(false); } } function confirmClearHistory() {''' )) # 5. member row border fix edits.append(( ''' ''', ''' ''' )) # 6. Add Friends row in Members card edits.append(( ''' ))} Permissions''', ''' ))} {showAddFriendsRow && ( Add Friends )} Permissions''' )) # 7. modal edits.append(( ''' ); }''', ''' setAddFriendsVisible(false)}> setAddFriendsVisible(false)}> Add friends to {group.name || 'group'} f.username.toLowerCase().includes(friendSearch.toLowerCase()))} keyExtractor={f => String(f.id)} ListEmptyComponent={No friends to add.} renderItem={({ item }) => { const isSelected = selectedFriendIds.includes(item.id); return ( toggleFriendSelect(item.id)}> {item.username?.[0]?.toUpperCase()} {item.username} {item.verified && } {isSelected && } ); }} /> {addingMembers ? ( ) : ( Add{selectedFriendIds.length ? ` (${selectedFriendIds.length})` : ''} )} ); }''' )) # 8. styles edits.append(( ''' shareBtn: { marginTop: 12, backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 14 }, shareBtnText: { color: 'white', fontWeight: '700', fontSize: 14 }, });''', ''' shareBtn: { marginTop: 12, backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 14, alignItems: 'center', justifyContent: 'center', minWidth: 120 }, shareBtnDisabled: { backgroundColor: '#c4c4cc' }, shareBtnText: { color: 'white', fontWeight: '700', fontSize: 14 }, addFriendIconWrap: { width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', marginRight: 10, backgroundColor: 'rgba(79,70,229,0.12)' }, addFriendLabel: { fontWeight: '600', color: '#4f46e5' }, addFriendsCard: { width: '100%', maxHeight: '75%', padding: 24, gap: 10 }, friendSearchInput: { backgroundColor: 'rgba(255,255,255,0.6)', borderRadius: 12, paddingHorizontal: 14, paddingVertical: 10, fontSize: 14, color: '#0f0f1a', marginTop: 4 }, friendList: { maxHeight: 280, marginVertical: 6 }, friendRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10 }, friendEmptyText: { textAlign: 'center', color: '#6b6b7a', fontSize: 13, paddingVertical: 20 }, checkCircle: { width: 22, height: 22, borderRadius: 11, borderWidth: 1.5, borderColor: '#c4c4cc', alignItems: 'center', justifyContent: 'center' }, checkCircleSelected: { backgroundColor: '#4f46e5', borderColor: '#4f46e5' }, });''' )) for i, (old, new) in enumerate(edits, 1): if src.count(old) != 1: raise SystemExit(f"PATCH FAILED at edit {i}/8: marker not found exactly once (file may already be patched or changed).") src = src.replace(old, new, 1) with open(path, "w", encoding="utf-8") as f: f.write(src) print("✅ GroupSettingsScreen.js patched: Add Friends row + picker modal wired to /friends/list and /groups/{id}/members.")