/ src / App.tsx
App.tsx
 1  import { useState, useEffect, useCallback } from 'react';
 2  import { HomePage } from './pages/HomePage';
 3  import { ReviewPage } from './pages/ReviewPage';
 4  import { UpdateBanner } from '../components/UpdateBanner';
 5  import { applyCodeFont } from '../components/SettingsDialog';
 6  import { applyTheme } from '../lib/theme';
 7  import type { ReviewGuide } from '../lib/types';
 8  
 9  type Page = 'home' | 'review';
10  
11  export function App() {
12    const [page, setPage] = useState<Page>('home');
13  
14    useEffect(() => {
15      void window.electronAPI.loadPreferences().then((prefs) => {
16        if (prefs.codeFont) applyCodeFont(prefs.codeFont);
17        applyTheme(prefs.theme);
18      });
19    }, []);
20  
21    // When a background auto-review completes, the history list will refresh itself
22    // via the new-review-in-history event — handled in HomePage.
23    const [review, setReview] = useState<ReviewGuide | null>(null);
24    const [prefillPrUrl, setPrefillPrUrl] = useState<string | undefined>();
25  
26    const handleReviewReady = useCallback((r: ReviewGuide) => {
27      setPrefillPrUrl(undefined);
28      setReview(r);
29      setPage('review');
30    }, []);
31  
32    function handleBack() {
33      setReview(null);
34      setPage('home');
35    }
36  
37    function handleReReview(prUrl: string) {
38      setPrefillPrUrl(prUrl);
39      setReview(null);
40      setPage('home');
41    }
42  
43    // Navigate to a completed review when notification is clicked
44    useEffect(() => {
45      window.electronAPI.onReviewNavigate((reviewId) => {
46        void window.electronAPI.loadReview(reviewId).then((r) => {
47          handleReviewReady(r);
48        });
49      });
50      return () => {
51        window.electronAPI.offReviewNavigate();
52      };
53    }, [handleReviewReady]);
54  
55    return (
56      <>
57        {/* Skip-to-content link — visually hidden until focused. Lets
58            keyboard users jump straight to the slide/page content
59            without tabbing through the persistent header chrome. */}
60        <a
61          href="#main-content"
62          className="sr-only focus:not-sr-only focus:fixed focus:top-3 focus:left-3 focus:z-[100] focus:bg-background focus:text-foreground focus:px-3 focus:py-2 focus:border focus:border-[var(--ring)] focus:text-sm"
63        >
64          Skip to content
65        </a>
66        <UpdateBanner />
67        <div id="main-content">
68          {page === 'home' && <HomePage onReviewReady={handleReviewReady} prefillPrUrl={prefillPrUrl} />}
69          {page === 'review' && review && (
70            <ReviewPage review={review} onBack={handleBack} onReReview={handleReReview} />
71          )}
72        </div>
73      </>
74    );
75  }