diff --git a/web/src/components/ChatArea.tsx b/web/src/components/ChatArea.tsx index fc817e2..acceae5 100644 --- a/web/src/components/ChatArea.tsx +++ b/web/src/components/ChatArea.tsx @@ -22,6 +22,7 @@ import { FeatureRequestsPanel } from "./FeatureRequestsPanel.tsx"; import { ReactionBar } from "./ReactionBar.tsx"; import { EmojiPicker } from "./EmojiPicker.tsx"; import { ReplyBar } from "./ReplyBar.tsx"; +import { useLayoutStore } from "../stores/layout.ts"; import { ForumView } from "./ForumView.tsx"; import { CalendarView } from "./CalendarView.tsx"; import { DocsView } from "./DocsView.tsx"; @@ -629,22 +630,31 @@ export function ChatArea() { return (
-
- - {activeChannel - ? activeChannel.type === 'forum' - ? `■ ${activeChannel.name}` - : activeChannel.type === 'calendar' - ? `○ ${activeChannel.name}` - : activeChannel.type === 'docs' - ? `☰ ${activeChannel.name}` - : activeChannel.type === 'list' - ? `☑ ${activeChannel.name}` - : `# ${activeChannel.name}` - : activeChannelId - ? `#${activeChannelId}` - : "[NO CHANNEL SELECTED]"} - +
+
+ + + {activeChannel + ? activeChannel.type === 'forum' + ? `■ ${activeChannel.name}` + : activeChannel.type === 'calendar' + ? `○ ${activeChannel.name}` + : activeChannel.type === 'docs' + ? `☰ ${activeChannel.name}` + : activeChannel.type === 'list' + ? `☑ ${activeChannel.name}` + : `# ${activeChannel.name}` + : activeChannelId + ? `#${activeChannelId}` + : "[NO CHANNEL SELECTED]"} + +
{activeChannelId && activeChannel?.type === 'text' && (
+ @ {title}
{isLoadingOlder &&

[loading older messages...]

} diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index 64c9683..a050d2a 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -38,8 +38,11 @@ export function Layout() { const location = useLocation(); const [showStatusMenu, setShowStatusMenu] = useState(false); const isDM = useLayoutStore((s) => s.isDM); + const mobileView = useLayoutStore((s) => s.mobileView); + const setMobileView = useLayoutStore((s) => s.setMobileView); const [showServerSettings, setShowServerSettings] = useState(false); const activeServerId = useServerStore((s) => s.activeServerId); + const activeChannelId = useServerStore((s) => s.activeChannelId); useEffect(() => { wsConnect(); return () => { wsDisconnect(); }; @@ -49,6 +52,21 @@ export function Layout() { navigate('/login', { replace: true }); } }, [isLoading, isAuthenticated, location.pathname, navigate]); + // Navigate to chat when a channel/DM is selected on mobile + useEffect(() => { + const unsub = useServerStore.subscribe((state, prev) => { + if (state.activeChannelId !== prev.activeChannelId && state.activeChannelId) { + setMobileView('chat'); + } + }); + return unsub; + }, [setMobileView]); + // Also switch to chat on mobile when navigating to a DM + useEffect(() => { + if (location.pathname.startsWith('/dm/') && location.pathname !== '/dm') { + setMobileView('chat'); + } + }, [location.pathname, setMobileView]); const handleStatusChange = async (status: UserStatus) => { setShowStatusMenu(false); try { @@ -69,21 +87,32 @@ export function Layout() { return null; } return ( -
+
-
- DUMPSTER -
+ {/* Top bar */} +
+
+ {/* Mobile: hamburger to show sidebar */} + + DUMPSTER +
+
{showStatusMenu && (
@@ -101,24 +130,61 @@ export function Layout() {
)}
- [SETTINGS] + [SETTINGS] {activeServerId && ( )} + {/* Mobile: members toggle */} + {!isDM && ( + + )}
-
- - {isDM ? : } -
+ + {/* Main content area */} +
+ {/* Sidebar: ServerBar + ChannelList/ConversationList */} + {/* Desktop: always visible as left columns */} + {/* Mobile: full-screen overlay when mobileView === 'sidebar' */} +
+ + {isDM ? : } + {/* Close sidebar on mobile after selection */} +
setMobileView('chat')} + /> +
+ + {/* Chat area: always in DOM, hidden on mobile when sidebar/members shown */} +
@@ -126,12 +192,33 @@ export function Layout() {
- {!isDM && } + + {/* Members panel */} + {/* Desktop: right column */} + {/* Mobile: full-screen overlay when mobileView === 'members' */} + {!isDM && ( +
+ {/* Tap background to close on mobile */} +
setMobileView('chat')} + /> + +
+ )}
+ {showServerSettings && activeServerId && ( setShowServerSettings(false)} /> )} -
+
TERM {__APP_VERSION__} {new Date().toISOString().slice(0, 10)}
diff --git a/web/src/stores/layout.ts b/web/src/stores/layout.ts index df9f13b..85b4f9f 100644 --- a/web/src/stores/layout.ts +++ b/web/src/stores/layout.ts @@ -1,11 +1,17 @@ import { create } from 'zustand'; +export type MobileView = 'sidebar' | 'chat' | 'members'; + interface LayoutState { isDM: boolean; setDM: (value: boolean) => void; + mobileView: MobileView; + setMobileView: (view: MobileView) => void; } export const useLayoutStore = create((set) => ({ isDM: false, setDM: (value) => set({ isDM: value }), + mobileView: 'chat', + setMobileView: (view) => set({ mobileView: view }), }));