v0.12.41 New settings options for messages

This commit is contained in:
2026-03-30 08:04:36 -04:00
parent ff6743c9b1
commit 6a2f4438f9
11 changed files with 154 additions and 24 deletions

View File

@@ -4,10 +4,17 @@ import { api } from '../utils/api.js';
import { useToast } from '../contexts/ToastContext.jsx';
import Avatar from './Avatar.jsx';
export default function NewChatModal({ onClose, onCreated }) {
export default function NewChatModal({ onClose, onCreated, features = {} }) {
const { user } = useAuth();
const toast = useToast();
const [tab, setTab] = useState('private'); // 'private' | 'public'
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('');
@@ -15,8 +22,8 @@ export default function NewChatModal({ onClose, onCreated }) {
const [selected, setSelected] = useState([]);
const [loading, setLoading] = useState(false);
// True when exactly 1 user selected on private tab = direct message
const isDirect = tab === 'private' && selected.length === 1;
// 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(() => {});
@@ -30,12 +37,17 @@ export default function NewChatModal({ onClose, onCreated }) {
const toggle = (u) => {
if (u.id === user.id) return;
setSelected(prev => prev.find(p => p.id === u.id) ? prev.filter(p => p.id !== u.id) : [...prev, u]);
// 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' && selected.length > 1 && !name.trim()) return toast('Name required', 'error');
if (tab === 'private' && !isDirect && !name.trim()) return toast('Name required', 'error');
if (tab === 'public' && !name.trim()) return toast('Name required', 'error');
setLoading(true);
@@ -86,15 +98,15 @@ export default function NewChatModal({ onClose, onCreated }) {
</button>
</div>
{user.role === 'admin' && (
{user.role === 'admin' && (msgU2U || msgPrivateGroup || msgPublic) && (
<div className="flex gap-2" style={{ marginBottom: 20 }}>
<button className={`btn ${tab === 'private' ? 'btn-primary' : 'btn-secondary'} btn-sm`} onClick={() => setTab('private')}>Direct Message</button>
<button className={`btn ${tab === 'public' ? 'btn-primary' : 'btn-secondary'} btn-sm`} onClick={() => setTab('public')}>Public Message</button>
{(msgU2U || msgPrivateGroup) && <button className={`btn ${tab === 'private' ? 'btn-primary' : 'btn-secondary'} btn-sm`} onClick={() => setTab('private')}>Direct Message</button>}
{msgPublic && <button className={`btn ${tab === 'public' ? 'btn-primary' : 'btn-secondary'} btn-sm`} onClick={() => setTab('public')}>Public Message</button>}
</div>
)}
{/* Message Name — only shown when needed: public always, private only when 2+ members selected */}
{(tab === 'public' || (tab === 'private' && selected.length > 1)) && (
{/* Message Name — public always, private when not a DM and at least 1 member selected */}
{(tab === 'public' || (tab === 'private' && !isDirect && selected.length > 0)) && (
<div className="flex-col gap-2" style={{ marginBottom: 16 }}>
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Message Name</label>
<input