fix: give Explore its own /explore route so PiP navigates back to voice correctly

The Explore page shared /channels/@me with Friends, differentiated by a
showExplore UI flag. When PiP navigated to a voice channel, the stale
flag caused MainContent to render text chat instead of the voice grid.
Replace the flag with a dedicated /explore route so the URL is the
single source of truth.
This commit is contained in:
Jannis Braun
2026-03-05 01:29:16 +01:00
parent b840d2f82b
commit b642989e6e
7 changed files with 25 additions and 23 deletions
+8
View File
@@ -52,6 +52,14 @@ export function App() {
</ProtectedRoute>
}
/>
<Route
path="/explore"
element={
<ProtectedRoute>
<AppLayout />
</ProtectedRoute>
}
/>
<Route path="/" element={<Navigate to="/channels/@me" replace />} />
<Route path="*" element={<Navigate to="/channels/@me" replace />} />
</Routes>
@@ -2,14 +2,12 @@ import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'
import { useNavigate } from 'react-router-dom';
import { useExploreStore, type TaggedExploreServer } from '../../stores/exploreStore';
import { useServerStore } from '../../stores/serverStore';
import { useUIStore } from '../../stores/uiStore';
import { LoadingSpinner } from '../ui/LoadingSpinner';
import { getServerGradient } from '../../utils/gradients';
export function ExplorePage() {
const navigate = useNavigate();
const setCurrentServer = useServerStore((s) => s.setCurrentServer);
const setShowExplore = useUIStore((s) => s.setShowExplore);
const servers = useExploreStore((s) => s.servers);
const myRequests = useExploreStore((s) => s.myRequests);
@@ -47,7 +45,6 @@ export function ExplorePage() {
}, []);
const handleJoinSuccess = (serverId: string) => {
setShowExplore(false);
setCurrentServer(serverId);
navigate(`/channels/${serverId}`);
};
@@ -183,14 +183,15 @@ export function AppLayout() {
// Handle route params
useEffect(() => {
if (serverId === '@me') {
if (!useUIStore.getState().showExplore) {
setShowDms(true);
}
setShowDms(true);
setCurrentServer(null);
} else if (serverId) {
setShowDms(false);
setCurrentServer(serverId);
loadServerDetail(serverId);
} else {
setShowDms(false);
setCurrentServer(null);
}
}, [serverId, setCurrentServer, loadServerDetail, setShowDms]);
@@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom';
import { useServerStore, getChannelOrigin } from '../../stores/serverStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
@@ -35,6 +35,7 @@ export function ChannelSidebar() {
const toggleMic = useVoiceStore((s) => s.toggleMic);
const toggleDeafen = useVoiceStore((s) => s.toggleDeafen);
const navigate = useNavigate();
const location = useLocation();
const handleMicToggle = async () => {
toggleMic();
@@ -142,7 +143,7 @@ export function ChannelSidebar() {
<div
onClick={handleHomeClick}
className={`flex items-center gap-3 px-2 h-[42px] rounded-[4px] cursor-pointer mb-[2px] transition-colors group ${
!currentChannelId
!currentChannelId && location.pathname !== '/explore'
? 'bg-interactive-selected text-white'
: 'text-txt-tertiary hover:bg-interactive-hover hover:text-txt-secondary'
}`}
@@ -1,4 +1,5 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { useServerStore } from '../../stores/serverStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
@@ -30,7 +31,8 @@ export function MainContent() {
const isLiveKitConnected = useVoiceStore((s) => s.isLiveKitConnected);
const connectionError = useVoiceStore((s) => s.connectionError);
const showDms = useUIStore((s) => s.showDms);
const showExplore = useUIStore((s) => s.showExplore);
const location = useLocation();
const isExplorePage = location.pathname === '/explore';
const activeDmCall = useVoiceStore((s) => s.activeDmCall);
const outgoingCall = useVoiceStore((s) => s.outgoingCall);
const dmChannels = useServerStore((s) => s.dmChannels);
@@ -62,9 +64,9 @@ export function MainContent() {
const channel = channels.find(c => c.id === currentChannelId);
const isVoiceChannel = channel?.type === 'voice' || channel?.type === 'video';
if (showDms || showExplore || !currentServerId) {
if (showDms || isExplorePage || !currentServerId) {
if (!currentChannelId) {
if (showExplore) return <ExplorePage />;
if (isExplorePage) return <ExplorePage />;
return <FriendsPage />;
}
@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom';
import { useServerStore } from '../../stores/serverStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
@@ -120,14 +120,13 @@ export function ServerSidebar() {
const dmChannels = useServerStore((s) => s.dmChannels);
const showDms = useUIStore((s) => s.showDms);
const setShowDms = useUIStore((s) => s.setShowDms);
const showExplore = useUIStore((s) => s.showExplore);
const setShowExplore = useUIStore((s) => s.setShowExplore);
const openModal = useUIStore((s) => s.openModal);
const addToast = useUIStore((s) => s.addToast);
const setCurrentChannel = useChatStore((s) => s.setCurrentChannel);
const unreadChannels = useChatStore((s) => s.unreadChannels);
const instances = useInstanceStore((s) => s.instances);
const navigate = useNavigate();
const location = useLocation();
// Group servers by origin
const groupedServers = useMemo(() => {
@@ -182,7 +181,6 @@ export function ServerSidebar() {
}
setCurrentServer(serverId);
setShowDms(false);
setShowExplore(false);
navigate(`/channels/${serverId}`);
};
@@ -194,10 +192,9 @@ export function ServerSidebar() {
};
const handleExploreClick = () => {
setShowExplore(true);
setCurrentServer(null);
setCurrentChannel(null);
navigate('/channels/@me');
navigate('/explore');
};
return (
@@ -276,7 +273,7 @@ export function ServerSidebar() {
<SidebarItem
id="explore"
name="Explore Servers"
active={showExplore}
active={location.pathname === '/explore'}
onClick={handleExploreClick}
type="action"
actionType="explore"
+1 -5
View File
@@ -28,7 +28,6 @@ interface UIState {
modalData: Record<string, unknown>;
isMobile: boolean;
showDms: boolean;
showExplore: boolean;
imagePreviewUrl: string | null;
userProfilePopout: {
user: User | null;
@@ -41,7 +40,6 @@ interface UIState {
closeModal: () => void;
setIsMobile: (isMobile: boolean) => void;
setShowDms: (show: boolean) => void;
setShowExplore: (show: boolean) => void;
openImagePreview: (url: string) => void;
closeImagePreview: () => void;
openUserProfile: (user: User, position: { top: number; left: number }) => void;
@@ -66,7 +64,6 @@ export const useUIStore = create<UIState>()(
modalData: {},
isMobile: false,
showDms: false,
showExplore: false,
imagePreviewUrl: null,
userProfilePopout: {
user: null,
@@ -92,8 +89,7 @@ export const useUIStore = create<UIState>()(
}
},
setShowDms: (show) => set({ showDms: show, ...(show ? { showExplore: false } : {}) }),
setShowExplore: (show) => set({ showExplore: show, ...(show ? { showDms: false } : {}) }),
setShowDms: (show) => set({ showDms: show }),
openImagePreview: (url) => set({ activeModal: 'imagePreview', imagePreviewUrl: url }),
closeImagePreview: () => set({ activeModal: null, imagePreviewUrl: null }),