import React, { useEffect, useState, useRef, useCallback } from 'react'; import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { ThemeProvider } from '@mui/material/styles'; import { backstoryTheme } from '../BackstoryTheme'; import { SeverityType } from '../Components/Snack'; import { Query } from '../Components/ChatQuery'; import { ConversationHandle } from './Components/Conversation'; import { UserProvider } from './Components/UserContext'; import { BetaPage } from './Pages/BetaPage'; import { UserRoute } from './routes/UserRoute'; import { BackstoryLayout } from './Components/BackstoryLayout'; import './BackstoryApp.css'; import '@fontsource/roboto/300.css'; import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css'; import { connectionBase } from '../Global'; // Cookie handling functions const getCookie = (name: string) => { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop()?.split(';').shift(); return null; }; const setCookie = (name: string, value: string, days = 7) => { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Strict`; }; const BackstoryApp = () => { const navigate = useNavigate(); const location = useLocation(); const snackRef = useRef(null); const chatRef = useRef(null); const [sessionId, setSessionId] = useState(undefined); const setSnack = useCallback((message: string, severity?: SeverityType) => { snackRef.current?.setSnack(message, severity); }, [snackRef]); const submitQuery = (query: Query) => { console.log(`handleSubmitChatQuery:`, query, chatRef.current ? ' sending' : 'no handler'); chatRef.current?.submitQuery(query); navigate('/chat'); }; const [page, setPage] = useState(""); const [storeInCookie, setStoreInCookie] = useState(true); // Extract session ID from URL query parameter or cookie const urlParams = new URLSearchParams(window.location.search); const urlSessionId = urlParams.get('id'); const cookieSessionId = getCookie('session_id'); // Fetch or join session on mount useEffect(() => { const fetchSession = async () => { try { let response; let newSessionId; let action = "" if (urlSessionId) { // Attempt to join session from URL response = await fetch(`${connectionBase}/api/join-session/${urlSessionId}`, { credentials: 'include', }); if (!response.ok) { throw new Error('Session not found'); } newSessionId = (await response.json()).id; action = "Joined"; } else if (cookieSessionId) { // Attempt to join session from cookie response = await fetch(`${connectionBase}/api/join-session/${cookieSessionId}`, { credentials: 'include', }); if (!response.ok) { // Cookie session invalid, create new session response = await fetch(`${connectionBase}/api/create-session`, { method: 'POST', credentials: 'include', }); if (!response.ok) { throw new Error('Failed to create session'); } action = "Created new"; } else { action = "Joined"; } newSessionId = (await response.json()).id; } else { // Create a new session response = await fetch(`${connectionBase}/api/create-session`, { method: 'POST', credentials: 'include', }); if (!response.ok) { throw new Error('Failed to create session'); } action = "Created new"; newSessionId = (await response.json()).id; } setSessionId(newSessionId); setSnack(`${action} session ${newSessionId}`); // Store in cookie if user opts in if (storeInCookie) { setCookie('session_id', newSessionId); } // Update URL without reloading if (!storeInCookie) { // Update only the 'id' query parameter, preserving the current path navigate(`${location.pathname}?id=${newSessionId}`, { replace: true }); } else { // Clear all query parameters, preserve the current path navigate(location.pathname, { replace: true }); } } catch (err) { setSnack("" + err); } }; fetchSession(); }, [cookieSessionId, setSnack, storeInCookie, urlSessionId]); useEffect(() => { const currentRoute = location.pathname.split("/")[1] ? `/${location.pathname.split("/")[1]}` : "/"; setPage(currentRoute); }, [location.pathname]); // Render appropriate routes based on user type return ( } /> {/* Static/shared routes */} } /> ); }; export { BackstoryApp };