mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
* fix: remove side panel elements from screen reader when hidden There's both left & right side panels; elements of both of them are hidden when dismissed. However, currently they are being hidden by using classes to hide their UI (such as making the sidebar zero width). That works for visually dismissing these elements, but they can still be viewed by a screen reader (using the tab key to jump between interactable elements). That can be a rather confusing experience for anyone visually impaired (such as duplicate buttons, or buttons that do nothing). -------- I've changed it so hidden elements are fully removed from the render. This prevents them from being interactable via keyboard. I leveraged Motion to duplicate the animations as they happened before. I subtly cleaned up the animations while I was at it. * Implemented reasonable suggestions from Copilot review
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { Outlet } from 'react-router-dom';
|
|
import type { ContextType } from '~/common';
|
|
import {
|
|
useSearchEnabled,
|
|
useAssistantsMap,
|
|
useAuthContext,
|
|
useAgentsMap,
|
|
useFileMap,
|
|
} from '~/hooks';
|
|
import {
|
|
PromptGroupsProvider,
|
|
AssistantsMapContext,
|
|
AgentsMapContext,
|
|
SetConvoProvider,
|
|
FileMapContext,
|
|
} from '~/Providers';
|
|
import { useUserTermsQuery, useGetStartupConfig } from '~/data-provider';
|
|
import { TermsAndConditionsModal } from '~/components/ui';
|
|
import { Nav, MobileNav } from '~/components/Nav';
|
|
import { useHealthCheck } from '~/data-provider';
|
|
import { Banner } from '~/components/Banners';
|
|
|
|
export default function Root() {
|
|
const [showTerms, setShowTerms] = useState(false);
|
|
const [bannerHeight, setBannerHeight] = useState(0);
|
|
const [navVisible, setNavVisible] = useState(() => {
|
|
const savedNavVisible = localStorage.getItem('navVisible');
|
|
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : true;
|
|
});
|
|
|
|
const { isAuthenticated, logout } = useAuthContext();
|
|
|
|
// Global health check - runs once per authenticated session
|
|
useHealthCheck(isAuthenticated);
|
|
|
|
const assistantsMap = useAssistantsMap({ isAuthenticated });
|
|
const agentsMap = useAgentsMap({ isAuthenticated });
|
|
const fileMap = useFileMap({ isAuthenticated });
|
|
|
|
const { data: config } = useGetStartupConfig();
|
|
const { data: termsData } = useUserTermsQuery({
|
|
enabled: isAuthenticated && config?.interface?.termsOfService?.modalAcceptance === true,
|
|
});
|
|
|
|
useSearchEnabled(isAuthenticated);
|
|
|
|
useEffect(() => {
|
|
if (termsData) {
|
|
setShowTerms(!termsData.termsAccepted);
|
|
}
|
|
}, [termsData]);
|
|
|
|
const handleAcceptTerms = () => {
|
|
setShowTerms(false);
|
|
};
|
|
|
|
const handleDeclineTerms = () => {
|
|
setShowTerms(false);
|
|
logout('/login?redirect=false');
|
|
};
|
|
|
|
if (!isAuthenticated) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<SetConvoProvider>
|
|
<FileMapContext.Provider value={fileMap}>
|
|
<AssistantsMapContext.Provider value={assistantsMap}>
|
|
<AgentsMapContext.Provider value={agentsMap}>
|
|
<PromptGroupsProvider>
|
|
<Banner onHeightChange={setBannerHeight} />
|
|
<div className="flex" style={{ height: `calc(100dvh - ${bannerHeight}px)` }}>
|
|
<div className="relative z-0 flex h-full w-full overflow-hidden">
|
|
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
|
|
<div className="relative flex h-full max-w-full flex-1 flex-col overflow-hidden">
|
|
<MobileNav navVisible={navVisible} setNavVisible={setNavVisible} />
|
|
<Outlet context={{ navVisible, setNavVisible } satisfies ContextType} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</PromptGroupsProvider>
|
|
</AgentsMapContext.Provider>
|
|
{config?.interface?.termsOfService?.modalAcceptance === true && (
|
|
<TermsAndConditionsModal
|
|
open={showTerms}
|
|
onOpenChange={setShowTerms}
|
|
onAccept={handleAcceptTerms}
|
|
onDecline={handleDeclineTerms}
|
|
title={config.interface.termsOfService.modalTitle}
|
|
modalContent={config.interface.termsOfService.modalContent}
|
|
/>
|
|
)}
|
|
</AssistantsMapContext.Provider>
|
|
</FileMapContext.Provider>
|
|
</SetConvoProvider>
|
|
);
|
|
}
|