import React, { useState } from 'react'; import { View, Text, Image, StyleSheet, TouchableOpacity } from 'react-native'; import ActionSheet, { SheetManager } from 'react-native-actions-sheet'; import { Colors } from 'src/theme'; import { API_HOST } from 'src/constants'; import { getFontSize } from 'src/utils'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ChatProps, WarningProps } from '../types'; import { useNavigation } from '@react-navigation/native'; import { NAVIGATION_PAGES } from 'src/types'; import { usePostReportConversationMutation } from '@api/chat'; import { useChatStore } from 'src/stores/chatStore'; import TrashIcon from 'assets/icons/travels-screens/trash-solid.svg'; import BanIcon from 'assets/icons/messages/ban.svg'; import BellSlashIcon from 'assets/icons/messages/bell-slash.svg'; import { AvatarWithInitials } from 'src/components'; import MegaphoneIcon from 'assets/icons/messages/megaphone.svg'; import ExitIcon from 'assets/icons/messages/exit.svg'; import GroupIcon from 'assets/icons/messages/group-chat.svg'; import { database } from 'src/watermelondb'; import { BlockedUser, Chat } from 'src/watermelondb/models'; import { Q } from '@nozbe/watermelondb'; import { addDirtyAction, syncChatsIncremental } from 'src/watermelondb/features/chat/data/chat.sync'; async function findChatRecord(chatData: ChatProps | null): Promise { if (!chatData) return null; const chatsCollection = database.get('chats'); if (chatData.uid) { const res = await chatsCollection.query(Q.where('chat_uid', chatData.uid)).fetch(); return res[0] ?? null; } if (chatData.groupToken) { const res = await chatsCollection .query(Q.where('group_chat_token', chatData.groupToken)) .fetch(); return res[0] ?? null; } return null; } async function upsertBlockedUserFromChat(chatData: ChatProps) { if (!chatData.uid) return; const blockedCollection = database.get('blocked_users'); const existing = await blockedCollection.query(Q.where('user_id', chatData.uid)).fetch(); const [firstName, ...rest] = (chatData.name ?? '').split(' '); const lastName = rest.join(' '); await database.write(async () => { if (existing.length > 0) { await existing[0].update((r) => { r.firstName = firstName || r.firstName; r.lastName = lastName || r.lastName; r.avatar = chatData.avatar ?? r.avatar; r.removed = false; }); } else { await blockedCollection.create((r) => { r.userId = chatData.uid!; r.firstName = firstName || ''; r.lastName = lastName || ''; r.avatar = chatData.avatar ?? null; r.removed = false; }); } }); } const MoreModal = () => { const insets = useSafeAreaInsets(); const navigation = useNavigation(); const { setIsWarningModalVisible } = useChatStore(); const [chatData, setChatData] = useState< | (ChatProps & { token: string; }) | null >(null); const [name, setName] = useState(null); const { mutateAsync: reportUser } = usePostReportConversationMutation(); const [shouldOpenWarningModal, setShouldOpenWarningModal] = useState(null); const handleSheetOpen = ( payload: | (ChatProps & { token: string; }) | null ) => { setChatData(payload); setName( payload?.userType === 'blocked' ? 'Account is blocked' : payload?.userType === 'not_exist' ? 'Account does not exist' : (payload?.name ?? null) ); }; const handleMute = async () => { if (!chatData) return; const newMuted = chatData.muted === 1 ? 0 : 1; const chatRec = await findChatRecord(chatData); if (chatRec) { await database.write(() => chatRec.update((r) => { r.muted = newMuted; addDirtyAction(r, { type: 'mute', value: newMuted }); }) ); } setChatData((prev) => (prev ? { ...prev, muted: newMuted } : prev)); try { await syncChatsIncremental(chatData.token); } catch (e) { console.warn('mute sync failed (will retry later):', e); } }; const handleBlock = async () => { if (!chatData) return; setShouldOpenWarningModal({ visible: true, title: 'Block user', buttonTitle: 'Block', message: `Are you sure you want to block ${name}?\nThis user will be blocked and you will not be able to send or receive messages from him/her.`, action: async () => { const chatRec = await findChatRecord(chatData); if (chatRec) { await database.write(() => chatRec.update((r) => { r.removed = true; addDirtyAction(r, { type: 'block' }); }) ); } await upsertBlockedUserFromChat(chatData); try { await syncChatsIncremental(chatData.token); } catch (e) { console.warn('block sync failed:', e); } } }); setTimeout(() => { SheetManager.hide('more-modal'); setShouldOpenWarningModal(null); }, 300); }; const handleReport = async () => { if (!chatData) return; setShouldOpenWarningModal({ visible: true, title: `Report ${name}`, buttonTitle: 'Report', message: `Are you sure you want to report ${name}?\nIf you proceed, the chat history with ${name} will become visible to NomadMania admins for investigation.`, action: async () => { chatData.uid && (await reportUser({ token: chatData.token, reported_user_id: chatData.uid })); } }); setTimeout(() => { SheetManager.hide('more-modal'); setShouldOpenWarningModal(null); }, 300); }; const handleDelete = async () => { if (!chatData) return; setShouldOpenWarningModal({ visible: true, title: 'Delete conversation', message: `Are you sure you want to delete conversation with ${name}?\nThis conversation will be deleted for both sides.`, action: async () => { const chatRec = await findChatRecord(chatData); if (chatRec) { await database.write(() => chatRec.update((r) => { r.removed = true; addDirtyAction(r, { type: 'delete' }); }) ); } try { await syncChatsIncremental(chatData.token); } catch (e) { console.warn('delete sync failed:', e); } } }); setTimeout(() => { SheetManager.hide('more-modal'); setShouldOpenWarningModal(null); }, 300); }; const handleLeaveGroup = async () => { if (!chatData) return; setShouldOpenWarningModal({ visible: true, title: `Leave group ${name}`, buttonTitle: 'Leave', message: `Are you sure you want to leave ${name}?`, action: async () => { const chatRec = await findChatRecord(chatData); if (chatRec) { await database.write(() => chatRec.update((r) => { r.removed = true; addDirtyAction(r, { type: 'leave_group' }); }) ); } try { await syncChatsIncremental(chatData.token); } catch (e) { console.warn('leaveGroup sync failed:', e); } } }); setTimeout(() => { SheetManager.hide('more-modal'); setShouldOpenWarningModal(null); }, 300); }; const handleDeleteGroup = async () => { if (!chatData) return; setShouldOpenWarningModal({ visible: true, title: `Delete ${name}`, message: `Are you sure you want to delete this group chat?\nThis action will remove the chat from your history, but it won't affect other participants.`, action: async () => { const chatRec = await findChatRecord(chatData); if (chatRec) { await database.write(() => chatRec.update((r) => { r.removed = true; addDirtyAction(r, { type: 'delete' }); }) ); } try { await syncChatsIncremental(chatData.token); } catch (e) { console.warn('deleteGroup sync failed:', e); } } }); setTimeout(() => { SheetManager.hide('more-modal'); setShouldOpenWarningModal(null); }, 300); }; return ( { const payload = sheetRef || null; handleSheetOpen(payload); }} onClose={() => { if (shouldOpenWarningModal) { setIsWarningModalVisible(shouldOpenWarningModal); } }} containerStyle={styles.sheetContainer} defaultOverlayOpacity={0.5} indicatorStyle={{ backgroundColor: 'transparent' }} safeAreaInsets={{ top: insets.top, bottom: insets.bottom || 24, left: 0, right: 0 }} > {chatData && ( { SheetManager.hide('more-modal'); if (chatData?.uid) { navigation.navigate( ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: chatData.uid }] as never) ); } }} disabled={chatData?.userType !== 'normal'} > {chatData?.avatar && (chatData.userType === 'normal' || !chatData.userType) ? ( ) : chatData.uid && (chatData.userType === 'normal' || !chatData.userType) ? ( n[0]) .join('') ?? '' } flag={API_HOST + 'flag.png'} size={32} fontSize={12} /> ) : chatData.userType === 'normal' || !chatData.userType ? ( ) : ( )} {name} {chatData?.userType === 'normal' && ( {chatData.muted === 1 ? 'Unmute' : 'Mute'} )} {!chatData.announcement ? ( {chatData?.groupToken && ( Leave group chat )} {chatData?.userType === 'normal' && chatData?.uid && ( <> Report {name} Block {name} )} {chatData?.uid ? ( Delete chat ) : ( Delete group chat )} ) : null} )} ); }; const styles = StyleSheet.create({ sheetContainer: { borderTopLeftRadius: 15, borderTopRightRadius: 15 }, container: { backgroundColor: 'white', paddingHorizontal: 16, paddingTop: 8, gap: 16 }, header: { flexDirection: 'row', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: Colors.FILL_LIGHT, gap: 8 }, avatar: { width: 32, height: 32, borderRadius: 16, borderWidth: 1, borderColor: Colors.FILL_LIGHT }, name: { fontSize: getFontSize(14), fontFamily: 'montserrat-700', color: Colors.DARK_BLUE }, optionsContainer: { paddingVertical: 10, paddingHorizontal: 8, gap: 16, borderRadius: 8, backgroundColor: Colors.FILL_LIGHT }, option: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, optionText: { fontSize: getFontSize(12), fontWeight: '600', color: Colors.DARK_BLUE }, dangerOption: { paddingVertical: 10, borderBottomWidth: 1, borderBlockColor: Colors.WHITE }, dangerText: { color: Colors.RED } }); export default MoreModal;