v0.12.11 new drawer menu notification feature
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rosterchirp-backend",
|
"name": "rosterchirp-backend",
|
||||||
"version": "0.12.10",
|
"version": "0.12.11",
|
||||||
"description": "RosterChirp backend server",
|
"description": "RosterChirp backend server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -13,7 +13,7 @@
|
|||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
VERSION="${1:-0.12.10}"
|
VERSION="${1:-0.12.11}"
|
||||||
ACTION="${2:-}"
|
ACTION="${2:-}"
|
||||||
REGISTRY="${REGISTRY:-}"
|
REGISTRY="${REGISTRY:-}"
|
||||||
IMAGE_NAME="rosterchirp"
|
IMAGE_NAME="rosterchirp"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rosterchirp-frontend",
|
"name": "rosterchirp-frontend",
|
||||||
"version": "0.12.10",
|
"version": "0.12.11",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useSocket } from '../contexts/SocketContext.jsx';
|
import { useSocket } from '../contexts/SocketContext.jsx';
|
||||||
import { api } from '../utils/api.js';
|
import { api } from '../utils/api.js';
|
||||||
|
|
||||||
export default function GlobalBar({ isMobile, showSidebar, onBurger }) {
|
export default function GlobalBar({ isMobile, showSidebar, onBurger, hasUnread = false }) {
|
||||||
const { connected } = useSocket();
|
const { connected } = useSocket();
|
||||||
const [settings, setSettings] = useState({ app_name: 'rosterchirp', logo_url: '' });
|
const [settings, setSettings] = useState({ app_name: 'rosterchirp', logo_url: '' });
|
||||||
const [isDark, setIsDark] = useState(() => document.documentElement.getAttribute('data-theme') === 'dark');
|
const [isDark, setIsDark] = useState(() => document.documentElement.getAttribute('data-theme') === 'dark');
|
||||||
@@ -41,11 +41,22 @@ export default function GlobalBar({ isMobile, showSidebar, onBurger }) {
|
|||||||
title="Menu"
|
title="Menu"
|
||||||
aria-label="Open menu"
|
aria-label="Open menu"
|
||||||
>
|
>
|
||||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
<div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
<line x1="3" y1="6" x2="21" y2="6"/>
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
||||||
<line x1="3" y1="12" x2="21" y2="12"/>
|
<line x1="3" y1="6" x2="21" y2="6"/>
|
||||||
<line x1="3" y1="18" x2="21" y2="18"/>
|
<line x1="3" y1="12" x2="21" y2="12"/>
|
||||||
</svg>
|
<line x1="3" y1="18" x2="21" y2="18"/>
|
||||||
|
</svg>
|
||||||
|
{hasUnread && (
|
||||||
|
<span style={{
|
||||||
|
position: 'absolute', bottom: -1, right: -1,
|
||||||
|
width: 9, height: 9, borderRadius: '50%',
|
||||||
|
background: 'var(--primary)',
|
||||||
|
border: '2px solid var(--surface)',
|
||||||
|
flexShrink: 0,
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div className="global-bar-brand">
|
<div className="global-bar-brand">
|
||||||
<img src={logoUrl || '/icons/rosterchirp.png'} alt={appName} className="global-bar-logo" />
|
<img src={logoUrl || '/icons/rosterchirp.png'} alt={appName} className="global-bar-logo" />
|
||||||
|
|||||||
@@ -83,3 +83,12 @@
|
|||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
.nav-drawer-item.active:hover { background: var(--primary-light); }
|
.nav-drawer-item.active:hover { background: var(--primary-light); }
|
||||||
|
|
||||||
|
.nav-drawer-unread-dot {
|
||||||
|
margin-left: auto;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--primary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const NAV_ICON = {
|
|||||||
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>,
|
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, onGroupMessages, 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, unreadMessages = false, unreadGroupMessages = false }) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const drawerRef = useRef(null);
|
const drawerRef = useRef(null);
|
||||||
const isAdmin = user?.role === 'admin';
|
const isAdmin = user?.role === 'admin';
|
||||||
@@ -36,7 +36,7 @@ export default function NavDrawer({ open, onClose, onMessages, onGroupMessages,
|
|||||||
}, [open, onClose]);
|
}, [open, onClose]);
|
||||||
|
|
||||||
const item = (icon, label, onClick, opts = {}) => {
|
const item = (icon, label, onClick, opts = {}) => {
|
||||||
const { active, disabled, badge } = opts;
|
const { active, disabled, badge, dot } = opts;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`nav-drawer-item${active ? ' active' : ''}${disabled ? ' disabled' : ''}`}
|
className={`nav-drawer-item${active ? ' active' : ''}${disabled ? ' disabled' : ''}`}
|
||||||
@@ -46,6 +46,7 @@ export default function NavDrawer({ open, onClose, onMessages, onGroupMessages,
|
|||||||
{icon}
|
{icon}
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
{badge && <span className="nav-drawer-badge">{badge}</span>}
|
{badge && <span className="nav-drawer-badge">{badge}</span>}
|
||||||
|
{dot && <span className="nav-drawer-unread-dot" />}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -64,8 +65,8 @@ export default function NavDrawer({ open, onClose, onMessages, onGroupMessages,
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* User section */}
|
{/* User section */}
|
||||||
{item(NAV_ICON.messages, 'Messages', onMessages, { active: currentPage === 'chat' })}
|
{item(NAV_ICON.messages, 'Messages', onMessages, { active: currentPage === 'chat', dot: unreadMessages })}
|
||||||
{hasUserGroups && item(NAV_ICON.groupmessages, 'Group Messages', onGroupMessages, { active: currentPage === 'groupmessages' })}
|
{hasUserGroups && item(NAV_ICON.groupmessages, 'Group Messages', onGroupMessages, { active: currentPage === 'groupmessages', dot: unreadGroupMessages })}
|
||||||
{features.scheduleManager && item(NAV_ICON.schedules, 'Schedules', onSchedule, { active: currentPage === 'schedule' })}
|
{features.scheduleManager && item(NAV_ICON.schedules, 'Schedules', onSchedule, { active: currentPage === 'schedule' })}
|
||||||
|
|
||||||
{/* Admin section */}
|
{/* Admin section */}
|
||||||
|
|||||||
@@ -395,10 +395,20 @@ export default function Chat() {
|
|||||||
|
|
||||||
const isToolManager = user?.role === 'admin' || (features.teamToolManagers || []).some(gid => (features.userGroupMemberships || []).includes(gid));
|
const isToolManager = user?.role === 'admin' || (features.teamToolManagers || []).some(gid => (features.userGroupMemberships || []).includes(gid));
|
||||||
|
|
||||||
|
// Unread indicators for burger icon and nav drawer
|
||||||
|
const allGroupsFlat = [...(groups.publicGroups || []), ...(groups.privateGroups || [])];
|
||||||
|
const hasUnreadChat = allGroupsFlat.some(g =>
|
||||||
|
(g.type === 'public' || !g.is_managed) && (unreadGroups.get(g.id) || 0) > 0
|
||||||
|
);
|
||||||
|
const hasUnreadGroupMessages = (groups.privateGroups || []).some(g =>
|
||||||
|
g.is_managed && (unreadGroups.get(g.id) || 0) > 0
|
||||||
|
);
|
||||||
|
const hasAnyUnread = hasUnreadChat || hasUnreadGroupMessages;
|
||||||
|
|
||||||
if (page === 'users') {
|
if (page === 'users') {
|
||||||
return (
|
return (
|
||||||
<div className="chat-layout">
|
<div className="chat-layout">
|
||||||
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} />
|
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} hasUnread={hasAnyUnread} />
|
||||||
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
||||||
<UserManagerPage isMobile={isMobile} onProfile={() => setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
|
<UserManagerPage isMobile={isMobile} onProfile={() => setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
|
||||||
</div>
|
</div>
|
||||||
@@ -412,7 +422,8 @@ export default function Chat() {
|
|||||||
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
||||||
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
||||||
features={features} currentPage={page} isMobile={isMobile} />
|
features={features} currentPage={page} isMobile={isMobile}
|
||||||
|
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
|
||||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||||
@@ -425,20 +436,10 @@ export default function Chat() {
|
|||||||
if (page === 'groups') {
|
if (page === 'groups') {
|
||||||
return (
|
return (
|
||||||
<div className="chat-layout">
|
<div className="chat-layout">
|
||||||
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} />
|
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} hasUnread={hasAnyUnread} />
|
||||||
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
||||||
<GroupManagerPage isMobile={isMobile} onProfile={() => setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
|
<GroupManagerPage isMobile={isMobile} onProfile={() => setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
|
||||||
</div>
|
</div>
|
||||||
<NavDrawer
|
|
||||||
open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
|
||||||
onMessages={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('chat'); }}
|
|
||||||
onSchedule={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('schedule'); }}
|
|
||||||
onGroupManager={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('groups'); }}
|
|
||||||
onBranding={() => { setDrawerOpen(false); setModal('branding'); }}
|
|
||||||
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
|
||||||
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
|
||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
|
||||||
features={features} currentPage={page} isMobile={isMobile} />
|
|
||||||
<NavDrawer
|
<NavDrawer
|
||||||
open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
open={drawerOpen} onClose={() => setDrawerOpen(false)}
|
||||||
onMessages={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('chat'); }}
|
onMessages={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('chat'); }}
|
||||||
@@ -449,7 +450,8 @@ export default function Chat() {
|
|||||||
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
||||||
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
||||||
features={features} currentPage={page} isMobile={isMobile} />
|
features={features} currentPage={page} isMobile={isMobile}
|
||||||
|
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
|
||||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||||
@@ -462,7 +464,7 @@ export default function Chat() {
|
|||||||
if (page === 'groupmessages') {
|
if (page === 'groupmessages') {
|
||||||
return (
|
return (
|
||||||
<div className="chat-layout">
|
<div className="chat-layout">
|
||||||
<GlobalBar isMobile={isMobile} showSidebar={showSidebar} onBurger={() => setDrawerOpen(true)} />
|
<GlobalBar isMobile={isMobile} showSidebar={showSidebar} onBurger={() => setDrawerOpen(true)} hasUnread={hasAnyUnread} />
|
||||||
<div className="chat-body">
|
<div className="chat-body">
|
||||||
{(!isMobile || showSidebar) && (
|
{(!isMobile || showSidebar) && (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
@@ -506,7 +508,8 @@ export default function Chat() {
|
|||||||
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
|
||||||
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
|
||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
||||||
features={features} currentPage={page} isMobile={isMobile} />
|
features={features} currentPage={page} isMobile={isMobile}
|
||||||
|
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
|
||||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||||
@@ -520,7 +523,7 @@ export default function Chat() {
|
|||||||
if (page === 'hostpanel') {
|
if (page === 'hostpanel') {
|
||||||
return (
|
return (
|
||||||
<div className="chat-layout">
|
<div className="chat-layout">
|
||||||
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} />
|
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} hasUnread={hasAnyUnread} />
|
||||||
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
||||||
<HostPanel onProfile={() => setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
|
<HostPanel onProfile={() => setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
|
||||||
</div>
|
</div>
|
||||||
@@ -538,7 +541,8 @@ export default function Chat() {
|
|||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
||||||
features={features}
|
features={features}
|
||||||
currentPage={page}
|
currentPage={page}
|
||||||
isMobile={isMobile} />
|
isMobile={isMobile}
|
||||||
|
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
|
||||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||||
@@ -551,7 +555,7 @@ export default function Chat() {
|
|||||||
if (page === 'schedule') {
|
if (page === 'schedule') {
|
||||||
return (
|
return (
|
||||||
<div className="chat-layout">
|
<div className="chat-layout">
|
||||||
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} />
|
<GlobalBar isMobile={isMobile} showSidebar={true} onBurger={() => setDrawerOpen(true)} hasUnread={hasAnyUnread} />
|
||||||
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
<div className="chat-body" style={{ overflow: 'hidden' }}>
|
||||||
<SchedulePage
|
<SchedulePage
|
||||||
isToolManager={isToolManager}
|
isToolManager={isToolManager}
|
||||||
@@ -575,7 +579,8 @@ export default function Chat() {
|
|||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
||||||
features={features}
|
features={features}
|
||||||
currentPage={page}
|
currentPage={page}
|
||||||
isMobile={isMobile} />
|
isMobile={isMobile}
|
||||||
|
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
|
||||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||||
@@ -594,7 +599,7 @@ export default function Chat() {
|
|||||||
return (
|
return (
|
||||||
<div className="chat-layout">
|
<div className="chat-layout">
|
||||||
{/* Global top bar — spans full width on desktop, visible on mobile sidebar view */}
|
{/* Global top bar — spans full width on desktop, visible on mobile sidebar view */}
|
||||||
<GlobalBar isMobile={isMobile} showSidebar={showSidebar} onBurger={() => setDrawerOpen(true)} />
|
<GlobalBar isMobile={isMobile} showSidebar={showSidebar} onBurger={() => setDrawerOpen(true)} hasUnread={hasAnyUnread} />
|
||||||
|
|
||||||
<div className="chat-body">
|
<div className="chat-body">
|
||||||
{(!isMobile || showSidebar) && (
|
{(!isMobile || showSidebar) && (
|
||||||
@@ -645,7 +650,8 @@ export default function Chat() {
|
|||||||
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
|
||||||
features={features}
|
features={features}
|
||||||
currentPage={page}
|
currentPage={page}
|
||||||
isMobile={isMobile} />
|
isMobile={isMobile}
|
||||||
|
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
|
||||||
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
{modal === 'profile' && <ProfileModal onClose={() => setModal(null)} />}
|
||||||
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
{modal === 'settings' && <SettingsModal onClose={() => setModal(null)} onFeaturesChanged={setFeatures} />}
|
||||||
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
{modal === 'branding' && <BrandingModal onClose={() => setModal(null)} />}
|
||||||
|
|||||||
Reference in New Issue
Block a user