import { useState, useEffect, useCallback } from 'react'; import UserFooter from '../components/UserFooter.jsx'; // ── useKeyboardOpen — true when software keyboard is visible ───────────────── function useKeyboardOpen() { const [open, setOpen] = useState(false); useEffect(() => { const vv = window.visualViewport; if (!vv) return; const handler = () => setOpen(vv.height < window.innerHeight * 0.75); vv.addEventListener('resize', handler); return () => vv.removeEventListener('resize', handler); }, []); return open; } import { api } from '../utils/api.js'; import { useToast } from '../contexts/ToastContext.jsx'; import Avatar from '../components/Avatar.jsx'; // ── Shared sub-components (identical logic to modal versions) ───────────────── function UserCheckList({ allUsers, selectedIds, onChange }) { const [search, setSearch] = useState(''); const filtered = allUsers.filter(u => (u.display_name||u.name).toLowerCase().includes(search.toLowerCase())); return (
A matching Direct Message group will be created automatically.
}{members.size} selected
Delete {selected?.name}? This also deletes the associated direct message group.
Select two or more user groups. All their members get access to this conversation.
{groupIds.size} group{groupIds.size!==1?'s':''} selected
Delete {selected?.name}? Also deletes the associated DM group.
Members of {selectedGroup.name} can initiate 1-to-1 direct messages with members of all checked groups. Unchecking a group blocks initiation — existing conversations are preserved. Admins are always exempt. If a user is in multiple groups, the least restrictive rule applies.