v0.11.19 bug fixes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jama-frontend",
|
||||
"version": "0.11.18",
|
||||
"version": "0.11.19",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -253,7 +253,10 @@ export default function ChatWindow({ group, onBack, onGroupUpdated, onDirectMess
|
||||
{group.is_readonly ? <span className="readonly-badge" style={{ marginLeft: 8 }}>read-only</span> : null}
|
||||
</div>
|
||||
{isDirect && <div className="chat-header-sub">Private message</div>}
|
||||
{!isDirect && group.type === 'private' && <div className="chat-header-sub">Private group</div>}
|
||||
{!isDirect && group.type === 'public' && <div className="chat-header-sub">Public message</div>}
|
||||
{!isDirect && group.type === 'private' && group.is_managed && !group.is_multi_group && <div className="chat-header-sub">Private user group</div>}
|
||||
{!isDirect && group.type === 'private' && group.is_managed && group.is_multi_group && <div className="chat-header-sub">Private group</div>}
|
||||
{!isDirect && group.type === 'private' && !group.is_managed && <div className="chat-header-sub">Private group</div>}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
@@ -3,21 +3,23 @@ import { useAuth } from '../contexts/AuthContext.jsx';
|
||||
import './NavDrawer.css';
|
||||
|
||||
const NAV_ICON = {
|
||||
messages: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>,
|
||||
schedules: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>,
|
||||
users: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
|
||||
groups: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2"/><line x1="12" y1="12" x2="12" y2="16"/><line x1="10" y1="14" x2="14" y2="14"/></svg>,
|
||||
branding: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="3"/><path d="M12 2a10 10 0 1 0 10 10"/></svg>,
|
||||
hostpanel: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>,
|
||||
settings: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93l-1.41 1.41M5.34 18.66l-1.41 1.41M12 2v2M12 20v2M4.93 4.93l1.41 1.41M18.66 18.66l1.41 1.41M2 12h2M20 12h2"/></svg>,
|
||||
messages: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>,
|
||||
groupmessages: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
|
||||
schedules: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>,
|
||||
users: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
|
||||
groups: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2"/><line x1="12" y1="12" x2="12" y2="16"/><line x1="10" y1="14" x2="14" y2="14"/></svg>,
|
||||
branding: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="3"/><path d="M12 2a10 10 0 1 0 10 10"/></svg>,
|
||||
hostpanel: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>,
|
||||
settings: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93l-1.41 1.41M5.34 18.66l-1.41 1.41M12 2v2M12 20v2M4.93 4.93l1.41 1.41M18.66 18.66l1.41 1.41M2 12h2M20 12h2"/></svg>,
|
||||
};
|
||||
|
||||
export default function NavDrawer({ open, onClose, onMessages, onSchedule, onScheduleManager, onBranding, onSettings, onUsers, onGroupManager, onHostPanel, features = {}, currentPage = 'chat', isMobile = false }) {
|
||||
export default function NavDrawer({ open, onClose, onMessages, onGroupMessages, onSchedule, onScheduleManager, onBranding, onSettings, onUsers, onGroupManager, onHostPanel, features = {}, currentPage = 'chat', isMobile = false }) {
|
||||
const { user } = useAuth();
|
||||
const drawerRef = useRef(null);
|
||||
const isAdmin = user?.role === 'admin';
|
||||
const userGroupIds = features.userGroupMemberships || [];
|
||||
const canAccessTools = isAdmin || (features.teamToolManagers || []).some(gid => userGroupIds.includes(gid));
|
||||
const hasUserGroups = userGroupIds.length > 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
@@ -62,7 +64,8 @@ export default function NavDrawer({ open, onClose, onMessages, onSchedule, onSch
|
||||
</div>
|
||||
|
||||
{/* User section */}
|
||||
{item(NAV_ICON.messages, 'Messages', onMessages, { active: currentPage === 'chat' })}
|
||||
{item(NAV_ICON.messages, 'Messages', onMessages, { active: currentPage === 'chat' })}
|
||||
{hasUserGroups && item(NAV_ICON.groupmessages, 'Group Messages', onGroupMessages, { active: currentPage === 'groupmessages' })}
|
||||
{features.scheduleManager && item(NAV_ICON.schedules, 'Schedules', onSchedule, { active: currentPage === 'schedule' })}
|
||||
|
||||
{/* Admin section */}
|
||||
|
||||
@@ -7,6 +7,192 @@ import Avatar from './Avatar.jsx';
|
||||
import './Sidebar.css';
|
||||
import UserFooter from './UserFooter.jsx';
|
||||
|
||||
// Must match Avatar.jsx exactly so sidebar colours are consistent with message avatars
|
||||
const AVATAR_COLORS = ['#1a73e8','#ea4335','#34a853','#fa7b17','#a142f4','#00897b','#e91e8c','#0097a7'];
|
||||
function nameToColor(name) {
|
||||
return AVATAR_COLORS[(name || '').charCodeAt(0) % AVATAR_COLORS.length];
|
||||
}
|
||||
|
||||
function useAppSettings() {
|
||||
const [settings, setSettings] = useState({ app_name: 'jama', logo_url: '', color_avatar_public: '', color_avatar_dm: '' });
|
||||
const fetchSettings = () => {
|
||||
api.getSettings().then(({ settings }) => setSettings(settings)).catch(() => {});
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchSettings();
|
||||
window.addEventListener('jama:settings-changed', fetchSettings);
|
||||
return () => window.removeEventListener('jama:settings-changed', fetchSettings);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const name = settings.app_name || 'jama';
|
||||
const prefix = document.title.match(/^(\(\d+\)\s*)/)?.[1] || '';
|
||||
document.title = prefix + name;
|
||||
const faviconUrl = settings.logo_url || '/icons/jama.png';
|
||||
let link = document.querySelector("link[rel~='icon']");
|
||||
if (!link) { link = document.createElement('link'); link.rel = 'icon'; document.head.appendChild(link); }
|
||||
link.href = faviconUrl;
|
||||
}, [settings]);
|
||||
return settings;
|
||||
}
|
||||
|
||||
function formatTime(dateStr) {
|
||||
if (!dateStr) return '';
|
||||
const date = parseTS(dateStr);
|
||||
const now = new Date();
|
||||
const diff = now - date;
|
||||
if (diff < 86400000 && date.getDate() === now.getDate()) {
|
||||
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
if (diff < 604800000) {
|
||||
return date.toLocaleDateString([], { weekday: 'short' });
|
||||
}
|
||||
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
export default function Sidebar({ groups, activeGroupId, onSelectGroup, notifications, unreadGroups = new Map(), onNewChat, onProfile, onUsers, onSettings: onOpenSettings, onBranding, onGroupManager, onGroupsUpdated, isMobile, onAbout, onHelp, onlineUserIds = new Set(), features = {}, groupMessagesMode = false }) {
|
||||
const { user } = useAuth();
|
||||
const { connected } = useSocket();
|
||||
const toast = useToast();
|
||||
const settings = useAppSettings();
|
||||
|
||||
const allGroups = [
|
||||
...(groups.publicGroups || []),
|
||||
...(groups.privateGroups || [])
|
||||
];
|
||||
|
||||
const publicFiltered = allGroups.filter(g => g.type === 'public');
|
||||
|
||||
// In groupMessagesMode show only managed groups; on main Messages hide managed groups
|
||||
const privateFiltered = [...allGroups.filter(g => g.type === 'private' && (groupMessagesMode ? g.is_managed : !g.is_managed))].sort((a, b) => {
|
||||
if (!a.last_message_at && !b.last_message_at) return 0;
|
||||
if (!a.last_message_at) return 1;
|
||||
if (!b.last_message_at) return -1;
|
||||
return new Date(b.last_message_at) - new Date(a.last_message_at);
|
||||
});
|
||||
|
||||
const getNotifCount = (groupId) => notifications.filter(n => n.groupId === groupId).length;
|
||||
|
||||
const GroupItem = ({ group }) => {
|
||||
const notifs = getNotifCount(group.id);
|
||||
const unreadCount = unreadGroups.get(group.id) || 0;
|
||||
const hasUnread = unreadCount > 0;
|
||||
const isActive = group.id === activeGroupId;
|
||||
const isOnline = !!group.is_direct && !!group.peer_id && (onlineUserIds instanceof Set ? onlineUserIds.has(Number(group.peer_id)) : false);
|
||||
|
||||
// Peer avatar colour: use the same algorithm as Avatar.jsx so it matches message bubbles
|
||||
const peerColor = group.is_direct && !group.is_managed && group.peer_real_name
|
||||
? nameToColor(group.peer_real_name)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`group-item ${isActive ? 'active' : ''} ${hasUnread ? 'has-unread' : ''}`}
|
||||
onClick={() => onSelectGroup(group.id)}
|
||||
>
|
||||
<div className="group-icon-wrap">
|
||||
{group.is_direct && group.peer_avatar && !group.is_managed ? (
|
||||
<img src={group.peer_avatar} alt={group.name} className="group-icon" style={{ objectFit: 'cover', padding: 0 }} />
|
||||
) : group.is_direct && !group.is_managed ? (
|
||||
// No custom avatar — use the per-user colour matching Avatar.jsx
|
||||
<div className="group-icon" style={{ background: peerColor }}>
|
||||
{(group.peer_real_name || group.name)[0]?.toUpperCase()}
|
||||
</div>
|
||||
) : group.is_managed && group.is_multi_group ? (
|
||||
<div className="group-icon" style={{ background: settings.color_avatar_dm || '#a142f4', borderRadius: 8, fontSize: 11, fontWeight: 700 }}>MG</div>
|
||||
) : group.is_managed ? (
|
||||
<div className="group-icon" style={{ background: settings.color_avatar_dm || '#a142f4', borderRadius: 8, fontSize: 11, fontWeight: 700 }}>UG</div>
|
||||
) : (
|
||||
<div className="group-icon" style={{ background: group.type === 'public' ? (settings.color_avatar_public || '#1a73e8') : (settings.color_avatar_dm || '#a142f4') }}>
|
||||
{group.type === 'public' ? '#' : group.name[0]?.toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
{isOnline && <span className="online-dot" />}
|
||||
</div>
|
||||
<div className="group-info flex-1 overflow-hidden">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className={`group-name truncate ${hasUnread ? 'unread-name' : ''}`}>
|
||||
{group.is_direct && group.peer_display_name
|
||||
? <>{group.peer_display_name}<span className="dm-real-name"> ({group.peer_real_name})</span></>
|
||||
: group.is_direct && group.peer_real_name ? group.peer_real_name : group.name}
|
||||
</span>
|
||||
{group.last_message_at && (
|
||||
<span className="group-time">{formatTime(group.last_message_at)}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="group-last-msg truncate">
|
||||
{(() => {
|
||||
const preview = (group.last_message || '').replace(/@\[([^\]]+)\]/g, '@$1');
|
||||
if (!preview) return group.is_readonly ? '📢 Read-only' : 'No messages yet';
|
||||
const isOwn = group.last_message_user_id && user && group.last_message_user_id === user.id;
|
||||
return isOwn ? <><strong style={{ fontWeight: 600 }}>You:</strong> {preview}</> : preview;
|
||||
})()}
|
||||
</span>
|
||||
{notifs > 0 && <span className="badge shrink-0">{notifs}</span>}
|
||||
{hasUnread && notifs === 0 && <span className="badge badge-unread shrink-0">{unreadCount}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
<div className="sidebar-newchat-bar">
|
||||
{!isMobile && !groupMessagesMode && (
|
||||
<button className="newchat-btn" onClick={onNewChat}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" width="18" height="18">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 0 1 .865-.501 48.172 48.172 0 0 0 3.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
|
||||
</svg>
|
||||
New Chat
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="groups-list">
|
||||
{!groupMessagesMode && publicFiltered.length > 0 && (
|
||||
<div className="group-section">
|
||||
<div className="section-label">PUBLIC MESSAGES</div>
|
||||
{publicFiltered.map(g => <GroupItem key={g.id} group={g} />)}
|
||||
</div>
|
||||
)}
|
||||
{!groupMessagesMode && privateFiltered.length > 0 && (
|
||||
<div className="group-section">
|
||||
<div className="section-label">PRIVATE MESSAGES</div>
|
||||
{privateFiltered.map(g => <GroupItem key={g.id} group={g} />)}
|
||||
</div>
|
||||
)}
|
||||
{groupMessagesMode && privateFiltered.length > 0 && (
|
||||
<div className="group-section">
|
||||
<div className="section-label">PRIVATE GROUP MESSAGES</div>
|
||||
{privateFiltered.map(g => <GroupItem key={g.id} group={g} />)}
|
||||
</div>
|
||||
)}
|
||||
{groupMessagesMode && privateFiltered.length === 0 && (
|
||||
<div style={{ textAlign: 'center', padding: '40px 20px', color: 'var(--text-tertiary)', fontSize: 14 }}>
|
||||
No group messages yet
|
||||
</div>
|
||||
)}
|
||||
{!groupMessagesMode && allGroups.filter(g => !g.is_managed || g.type === 'public').length === 0 && (
|
||||
<div style={{ textAlign: 'center', padding: '40px 20px', color: 'var(--text-tertiary)', fontSize: 14 }}>
|
||||
No chats yet
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isMobile && !groupMessagesMode && (
|
||||
<button className="newchat-fab" onClick={onNewChat} title="New Chat">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" width="24" height="24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 0 1 .865-.501 48.172 48.172 0 0 0 3.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<UserFooter onProfile={onProfile} onHelp={onHelp} onAbout={onAbout} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function useAppSettings() {
|
||||
const [settings, setSettings] = useState({ app_name: 'jama', logo_url: '', color_avatar_public: '', color_avatar_dm: '' });
|
||||
const fetchSettings = () => {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function Chat() {
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const [unreadGroups, setUnreadGroups] = useState(new Map());
|
||||
const [modal, setModal] = useState(null); // 'profile' | 'users' | 'settings' | 'newchat' | 'help' | 'groupmanager'
|
||||
const [page, setPage] = useState('chat'); // 'chat' | 'schedule'
|
||||
const [page, setPage] = useState('chat'); // 'chat' | 'schedule' | 'groupmessages'
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [features, setFeatures] = useState({ branding: false, groupManager: false, scheduleManager: false, appType: 'JAMA-Chat', teamToolManagers: [], isHostDomain: false });
|
||||
const [helpDismissed, setHelpDismissed] = useState(true); // true until status loaded
|
||||
@@ -367,6 +367,7 @@ export default function Chat() {
|
||||
<NavDrawer
|
||||
open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
||||
onMessages={() => { setDrawerOpen(false); setPage('chat'); }}
|
||||
onGroupMessages={() => { setDrawerOpen(false); setPage('groupmessages'); }}
|
||||
onSchedule={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onGroupManager={() => { setDrawerOpen(false); setPage('groups'); }}
|
||||
onBranding={() => { setDrawerOpen(false); setModal('branding'); }}
|
||||
@@ -400,6 +401,72 @@ export default function Chat() {
|
||||
onUsers={() => { setDrawerOpen(false); setPage('users'); }}
|
||||
onHostPanel={() => { setDrawerOpen(false); setPage('hostpanel'); }}
|
||||
features={features} currentPage={page} isMobile={isMobile} />
|
||||
<NavDrawer
|
||||
open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
||||
onMessages={() => { setDrawerOpen(false); setPage('chat'); }}
|
||||
onGroupMessages={() => { setDrawerOpen(false); setPage('groupmessages'); }}
|
||||
onSchedule={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onGroupManager={() => { setDrawerOpen(false); setPage('groups'); }}
|
||||
onBranding={() => { setDrawerOpen(false); setModal('branding'); }}
|
||||
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
||||
onUsers={() => { setDrawerOpen(false); setPage('users'); }}
|
||||
onHostPanel={() => { setDrawerOpen(false); setPage('hostpanel'); }}
|
||||
features={features} currentPage={page} isMobile={isMobile} />
|
||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||
{modal === 'help' && <HelpModal onClose={() => setModal(null)} dismissed={helpDismissed} />}
|
||||
{modal === 'about' && <AboutModal onClose={() => setModal(null)} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (page === 'groupmessages') {
|
||||
return (
|
||||
<div className="chat-layout">
|
||||
<GlobalBar isMobile={isMobile} showSidebar={showSidebar} onBurger={() => setDrawerOpen(true)} />
|
||||
<div className="chat-body">
|
||||
{(!isMobile || showSidebar) && (
|
||||
<Sidebar
|
||||
groups={groups}
|
||||
activeGroupId={activeGroupId}
|
||||
onSelectGroup={selectGroup}
|
||||
notifications={notifications}
|
||||
unreadGroups={unreadGroups}
|
||||
onProfile={() => setModal('profile')}
|
||||
onUsers={() => setPage('users')}
|
||||
onSettings={() => setModal('settings')}
|
||||
onBranding={() => setModal('branding')}
|
||||
onGroupManager={() => setPage('groups')}
|
||||
features={features}
|
||||
onGroupsUpdated={loadGroups}
|
||||
isMobile={isMobile}
|
||||
onAbout={() => setModal('about')}
|
||||
onHelp={() => setModal('help')}
|
||||
onlineUserIds={onlineUserIds}
|
||||
groupMessagesMode={true} />
|
||||
)}
|
||||
{(!isMobile || !showSidebar) && (
|
||||
<ChatWindow
|
||||
group={activeGroup}
|
||||
onBack={isMobile ? () => { setShowSidebar(true); setActiveGroupId(null); } : null}
|
||||
onGroupUpdated={loadGroups}
|
||||
onDirectMessage={(g) => { loadGroups(); selectGroup(g.id); }}
|
||||
onMessageDeleted={handleMessageDeleted}
|
||||
onlineUserIds={onlineUserIds} />
|
||||
)}
|
||||
</div>
|
||||
<NavDrawer
|
||||
open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
||||
onMessages={() => { setDrawerOpen(false); setPage('chat'); }}
|
||||
onGroupMessages={() => { setDrawerOpen(false); setPage('groupmessages'); }}
|
||||
onSchedule={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onGroupManager={() => { setDrawerOpen(false); setPage('groups'); }}
|
||||
onBranding={() => { setDrawerOpen(false); setModal('branding'); }}
|
||||
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
||||
onUsers={() => { setDrawerOpen(false); setPage('users'); }}
|
||||
onHostPanel={() => { setDrawerOpen(false); setPage('hostpanel'); }}
|
||||
features={features} currentPage={page} isMobile={isMobile} />
|
||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||
@@ -420,6 +487,7 @@ export default function Chat() {
|
||||
open={drawerOpen}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
onMessages={() => { setDrawerOpen(false); setPage('chat'); }}
|
||||
onGroupMessages={() => { setDrawerOpen(false); setPage('groupmessages'); }}
|
||||
onSchedule={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onScheduleManager={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onGroupManager={() => { setDrawerOpen(false); setPage('groups'); }}
|
||||
@@ -456,6 +524,7 @@ export default function Chat() {
|
||||
open={drawerOpen}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
onMessages={() => { setDrawerOpen(false); setPage('chat'); }}
|
||||
onGroupMessages={() => { setDrawerOpen(false); setPage('groupmessages'); }}
|
||||
onSchedule={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onScheduleManager={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onGroupManager={() => { setDrawerOpen(false); setPage('groups'); }}
|
||||
@@ -505,7 +574,8 @@ export default function Chat() {
|
||||
isMobile={isMobile}
|
||||
onAbout={() => setModal('about')}
|
||||
onHelp={() => setModal('help')}
|
||||
onlineUserIds={onlineUserIds} />
|
||||
onlineUserIds={onlineUserIds}
|
||||
groupMessagesMode={false} />
|
||||
)}
|
||||
|
||||
{(!isMobile || !showSidebar) && (
|
||||
@@ -523,6 +593,7 @@ export default function Chat() {
|
||||
open={drawerOpen}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
onMessages={() => { setDrawerOpen(false); setPage('chat'); }}
|
||||
onGroupMessages={() => { setDrawerOpen(false); setPage('groupmessages'); }}
|
||||
onSchedule={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onScheduleManager={() => { setDrawerOpen(false); setPage('schedule'); }}
|
||||
onGroupManager={() => { setDrawerOpen(false); setPage('groups'); }}
|
||||
|
||||
Reference in New Issue
Block a user