import React from 'react'; import { View, Text, TouchableOpacity, StyleSheet, Modal, FlatList, ActivityIndicator, TextInput, KeyboardAvoidingView } from 'react-native'; import { useSettings } from '@/theme'; import { useFriends } from '@/hooks/useFriends'; interface AssigneeSelectorProps { value: string | null; onChange: (assigneeId: string | null) => void; disabled?: boolean; friends: string[]; } export function AssigneeSelector({ value, onChange, disabled = false }: AssigneeSelectorProps) { const { theme } = useSettings(); const { friends, searchUsers, loading: friendsLoading } = useFriends(); const [showModal, setShowModal] = React.useState(false); const [searchQuery, setSearchQuery] = React.useState(''); const [searchResults, setSearchResults] = React.useState<{ id: string; username: string }[]>([]); const [searching, setSearching] = React.useState(false); const selectedFriend = value ? friends.find(f => f.id === value) : null; const searchKey = `${showModal}:${searchQuery}`; const [prevSearchKey, setPrevSearchKey] = React.useState(searchKey); if (prevSearchKey !== searchKey && (!showModal || searchQuery.length < 2)) { setPrevSearchKey(searchKey); setSearchResults([]); } React.useEffect(() => { if (showModal && searchQuery.length >= 2) { const timeout = setTimeout(async () => { setSearching(true); try { const results = await searchUsers(searchQuery); setSearchResults(results.filter(r => !friends.some(f => f.id === r.id))); } catch (err) { console.error('Search failed:', err); } finally { setSearching(false); } }, 300); return () => clearTimeout(timeout); } }, [searchQuery, showModal, friends, searchUsers]); const handleSelectFriend = (friendId: string) => { onChange(friendId); setShowModal(false); }; const handleClear = () => { onChange(null); }; const handleSearchAdd = async (friendId: string, username: string) => { try { const { sendFriendRequest } = await import('@/services/friends'); await sendFriendRequest(username); onChange(friendId); setShowModal(false); } catch (err: any) { console.error('Failed to add friend:', err.message, err.details || ''); if (err.details) { console.error('Validation details:', JSON.stringify(err.details, null, 2)); } } }; return ( !disabled && setShowModal(true)} activeOpacity={0.8} > 👤 Assignee {selectedFriend ? selectedFriend.username : 'Unassigned'} {value && !disabled && ( { e.stopPropagation(); handleClear(); }} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} > ✕ )} setShowModal(false)}> Assign Task setShowModal(false)} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}> ✕ handleClear()} > 🚫 Unassigned {selectedFriend && ( ✓ )} Friends {friendsLoading ? ( ) : friends.length === 0 ? ( No friends yet. Add friends to assign tasks. ) : ( f.id} renderItem={({ item }) => ( handleSelectFriend(item.id)} > 👤 {item.username} {value === item.id && ( ✓ )} )} /> )} {searching ? ( ) : searchQuery.length >= 2 && searchResults.length > 0 ? ( u.id} renderItem={({ item }) => ( handleSearchAdd(item.id, item.username)} > ➕ {item.username} Add as friend & assign )} /> ) : searchQuery.length >= 2 && searchResults.length === 0 && !searching ? ( No users found ) : searchQuery.length < 2 ? ( Type at least 2 characters to search ) : null} ); } const styles = StyleSheet.create({ container: { width: '100%', }, selectorButton: { flexDirection: 'row', alignItems: 'center', gap: 8, paddingVertical: 12, paddingHorizontal: 10, borderRadius: 10, borderWidth: 1, minHeight: 48, }, selectorIcon: { fontSize: 18, }, selectorContent: { flex: 1, justifyContent: 'center', }, selectorLabel: { fontSize: 10, fontWeight: '600', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 1, }, selectorValue: { fontSize: 14, fontWeight: '500', }, clearButton: { padding: 3, }, clearText: { fontSize: 16, fontWeight: '300', }, modalOverlay: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 20, }, modalSheet: { width: '100%', maxWidth: 360, borderRadius: 16, overflow: 'hidden', maxHeight: '85%', }, modalHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', padding: 16, borderBottomWidth: 1, }, modalTitle: { fontSize: 16, fontWeight: '700', }, closeText: { fontSize: 20, fontWeight: '300', }, modalSection: { padding: 10, paddingBottom: 16, borderBottomWidth: 1, }, sectionTitle: { fontSize: 11, fontWeight: '600', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6, paddingHorizontal: 6, }, optionRow: { flexDirection: 'row', alignItems: 'center', gap: 10, paddingVertical: 10, paddingHorizontal: 14, borderRadius: 9, borderWidth: 1, }, optionIcon: { width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.05)', }, optionIconText: { fontSize: 15, }, optionText: { fontSize: 15, fontWeight: '500', flex: 1, }, optionSubtext: { fontSize: 11, marginTop: 1, }, checkmark: { fontSize: 16, fontWeight: '700', }, loading: { padding: 16, alignItems: 'center', }, emptyText: { fontSize: 13, textAlign: 'center', paddingHorizontal: 16, }, searchContainer: { padding: 10, paddingBottom: 6, }, searchInput: { fontSize: 15, paddingVertical: 10, paddingHorizontal: 14, borderRadius: 10, borderWidth: 1, }, });