Messenger_back_database / patch_group_add_friends.py
Nrighton233j
Update backend files and add group friends patch
0e67224
Raw
History Blame Contribute Delete
8.79 kB
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((
''' <View key={m.id} style={[styles.row, i < members.length - 1 && styles.rowBorder]}>''',
''' <View key={m.id} style={[styles.row, (i < members.length - 1 || showAddFriendsRow) && styles.rowBorder]}>'''
))
# 6. Add Friends row in Members card
edits.append((
''' ))}
</GlassCard>
<Text style={styles.sectionTitle}>Permissions</Text>''',
''' ))}
{showAddFriendsRow && (
<TouchableOpacity
style={styles.row}
onPress={openAddFriends}
>
<View style={styles.addFriendIconWrap}>
<UserPlus size={16} color="#4f46e5" />
</View>
<Text style={styles.addFriendLabel}>Add Friends</Text>
</TouchableOpacity>
)}
</GlassCard>
<Text style={styles.sectionTitle}>Permissions</Text>'''
))
# 7. modal
edits.append((
''' </GlassCard>
</View>
</Modal>
</ScrollView>
);
}''',
''' </GlassCard>
</View>
</Modal>
<Modal visible={addFriendsVisible} transparent animationType="fade" onRequestClose={() => setAddFriendsVisible(false)}>
<View style={styles.modalBackdrop}>
<GlassCard style={styles.addFriendsCard} blurAmount={24} tint={0.55}>
<TouchableOpacity style={styles.closeBtn} onPress={() => setAddFriendsVisible(false)}>
<X size={20} color="#0f0f1a" />
</TouchableOpacity>
<Text style={styles.inviteTitle}>Add friends to {group.name || 'group'}</Text>
<TextInput
style={styles.friendSearchInput}
placeholder="Search friends..."
placeholderTextColor="#9b9ba8"
value={friendSearch}
onChangeText={setFriendSearch}
/>
<FlatList
style={styles.friendList}
data={friends.filter(f => f.username.toLowerCase().includes(friendSearch.toLowerCase()))}
keyExtractor={f => String(f.id)}
ListEmptyComponent={<Text style={styles.friendEmptyText}>No friends to add.</Text>}
renderItem={({ item }) => {
const isSelected = selectedFriendIds.includes(item.id);
return (
<TouchableOpacity style={styles.friendRow} onPress={() => toggleFriendSelect(item.id)}>
<View style={[styles.memberDot, { backgroundColor: colorForId(item.id) }]}>
<Text style={styles.memberDotText}>{item.username?.[0]?.toUpperCase()}</Text>
</View>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 4 }}>
<Text style={styles.memberName}>{item.username}</Text>
{item.verified && <BadgeCheck size={14} color={item.verified === 'cyan' ? '#0ea5e9' : '#9333ea'} />}
</View>
<View style={[styles.checkCircle, isSelected && styles.checkCircleSelected]}>
{isSelected && <Check size={14} color="white" />}
</View>
</TouchableOpacity>
);
}}
/>
<TouchableOpacity
style={[styles.shareBtn, !selectedFriendIds.length && styles.shareBtnDisabled]}
onPress={submitAddFriends}
disabled={!selectedFriendIds.length || addingMembers}
>
{addingMembers ? (
<ActivityIndicator color="white" />
) : (
<Text style={styles.shareBtnText}>
Add{selectedFriendIds.length ? ` (${selectedFriendIds.length})` : ''}
</Text>
)}
</TouchableOpacity>
</GlassCard>
</View>
</Modal>
</ScrollView>
);
}'''
))
# 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.")