import { useState, useEffect } from 'react'; import { useAuth } from '../contexts/AuthContext.jsx'; import { api } from '../utils/api.js'; import { useToast } from '../contexts/ToastContext.jsx'; import Avatar from './Avatar.jsx'; export default function NewChatModal({ onClose, onCreated, features = {} }) { const { user } = useAuth(); const toast = useToast(); const msgPublic = features.msgPublic ?? true; const msgU2U = features.msgU2U ?? true; const msgPrivateGroup = features.msgPrivateGroup ?? true; // Default to private if available, otherwise public const defaultTab = (msgU2U || msgPrivateGroup) ? 'private' : 'public'; const [tab, setTab] = useState(defaultTab); const [name, setName] = useState(''); const [isReadonly, setIsReadonly] = useState(false); const [search, setSearch] = useState(''); const [users, setUsers] = useState([]); const [selected, setSelected] = useState([]); const [loading, setLoading] = useState(false); // Mixed Age: guardian confirmation modal const [guardianConfirm, setGuardianConfirm] = useState(null); // { group, guardianName } const loginType = features.loginType || 'all_ages'; // True when exactly 1 user selected on private tab AND U2U messages are enabled const isDirect = tab === 'private' && selected.length === 1 && msgU2U; useEffect(() => { api.searchUsers('').then(({ users }) => setUsers(users)).catch(() => {}); }, []); useEffect(() => { if (search) { api.searchUsers(search).then(({ users }) => setUsers(users)).catch(() => {}); } }, [search]); const toggle = (u) => { if (u.id === user.id) return; // If private groups are disabled, cap selection at 1 (DM only) setSelected(prev => { if (prev.find(p => p.id === u.id)) return prev.filter(p => p.id !== u.id); if (!msgPrivateGroup && prev.length >= 1) return prev; // can't add more for DM-only return [...prev, u]; }); }; const handleCreate = async () => { if (tab === 'private' && selected.length === 0) return toast('Add at least one member', 'error'); if (tab === 'private' && !isDirect && !name.trim()) return toast('Name required', 'error'); if (tab === 'public' && !name.trim()) return toast('Name required', 'error'); setLoading(true); try { let payload; if (isDirect) { // Direct message: no name, isDirect flag payload = { type: 'private', memberIds: selected.map(u => u.id), isDirect: true, }; } else { payload = { name: name.trim(), type: tab, memberIds: selected.map(u => u.id), isReadonly: tab === 'public' && isReadonly, }; } const { group, duplicate, guardianAdded, guardianName } = await api.createGroup(payload); if (duplicate) { toast('A group with these members already exists — opening it now.', 'info'); onCreated(group); } else { toast(isDirect ? 'Direct message started!' : `${tab === 'public' ? 'Public message' : 'Group message'} created!`, 'success'); if (guardianAdded && guardianName) { setGuardianConfirm({ group, guardianName }); } else { onCreated(group); } } } catch (e) { toast(e.message, 'error'); } finally { setLoading(false); } }; // Placeholder for the name field const namePlaceholder = isDirect ? selected[0]?.name || '' : tab === 'public' ? 'e.g. Announcements' : 'e.g. Project Team'; return (
e.target === e.currentTarget && onClose()}>

Start a Chat

{user.role === 'admin' && (msgU2U || msgPrivateGroup || msgPublic) && (
{(msgU2U || msgPrivateGroup) && } {msgPublic && }
)} {/* Message Name — public always, private when not a DM and at least 1 member selected */} {(tab === 'public' || (tab === 'private' && !isDirect && selected.length > 0)) && (
setName(e.target.value)} placeholder={namePlaceholder} autoComplete="off" autoCorrect="off" autoCapitalize="words" spellCheck={false} />
)} {/* Readonly toggle for public */} {tab === 'public' && user.role === 'admin' && ( )} {/* Member selector for private tab */} {tab === 'private' && ( <>
setSearch(e.target.value)} />
{selected.length > 0 && (
{selected.map(u => ( {u.name} toggle(u)}>× ))}
)} {isDirect && (

A private two-person conversation. Select a second person to create a group instead.

)}
{users.filter(u => u.id !== user.id && u.allow_dm !== 0).sort((a, b) => a.name.localeCompare(b.name)).map(u => ( ))}
)}
{guardianConfirm && (

Guardian Added

{guardianConfirm.guardianName} has been added to this conversation as the guardian of this minor.

)}
); }