diff --git a/docs/APP_REFACTORING_DESIGN.md b/docs/APP_REFACTORING_DESIGN.md index cb2048a..d2ce23e 100644 --- a/docs/APP_REFACTORING_DESIGN.md +++ b/docs/APP_REFACTORING_DESIGN.md @@ -102,7 +102,7 @@ We will gradually eliminate React state variables in `src/app.tsx` and derive th #### Phase 4: Structural Page Nesting * [x] **Step 4.1:** Create `src/hooks/use_google_auth.ts` to manage Google Drive authentication token updates. Remove `hasGoogleToken` state from `src/app.tsx`. * [x] **Step 4.2:** Create `src/pages/view_page.tsx` containing layout rendering (`renderMainArea`), asynchronous data loaders, view state management, `revokeObjectUrls` cleanups, and `` error fallback state and rendering. -* [ ] **Step 4.3:** Create `src/pages/intro_page.tsx` rendering the landing screen and its own local header. +* [x] **Step 4.3:** Create `src/pages/intro_page.tsx` rendering the landing screen and its own local header. * [ ] **Step 4.4:** Refactor `src/app.tsx` to serve as a pure routing switch routing to `` or ``. In `App`, handle Google Drive redirection synchronously during the render pass by returning a `` element (preventing render flashing), and ensure the redirection parser checks both the router `location.search` and the external `window.location.search` to handle HashRouter query structure robustly. Once external parameters (like `state` or `authcode`) are detected, they must be stripped from `window.location.search` using `window.history.replaceState` to prevent redirection loops on page refresh. Remove all layout state logic from the root file, including moving the `useWebMcpBridge` hook invocation into ``. Run final verification scripts (`npm run check:all`). ## Future Considerations diff --git a/src/app.tsx b/src/app.tsx index 324f167..47edb4d 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,33 +1,20 @@ import queryString from 'query-string'; -import {useEffect, useMemo, useRef} from 'react'; +import {useEffect, useRef} from 'react'; import {Navigate, Route, Routes, useLocation, useNavigate} from 'react-router'; -import { - clearGoogleDriveCache, - googleDriveService, -} from './datasource/google_drive_service'; -import {useGoogleAuth} from './hooks/use_google_auth'; -import {Intro} from './intro'; -import {TopBar} from './menu/top_bar'; +import {IntroPage} from './pages/intro_page'; import {ViewPage} from './pages/view_page'; -import {getArguments, getStaticUrl} from './util/url_args'; +import {getStaticUrl} from './util/url_args'; const staticUrl = getStaticUrl(); /** * Root App component that orchestrates top-level routing, Google Drive "Open with" - * payload interception and redirection, and global layout rendering for the intro view. + * payload interception and redirection. */ export function App() { - /** Tracks whether the user has a valid cached Google Drive OAuth access token and provides a state setter. */ - const {hasGoogleToken, setHasGoogleToken} = useGoogleAuth(); - const navigate = useNavigate(); const location = useLocation(); - const args = useMemo(() => getArguments(location), [location]); - /** Whether the app is in standalone mode, i.e. showing 'open file' menus. */ - const standalone = args.standalone; - /** Prevents the Google Drive "Open with" state from being processed more than once. */ const stateProcessed = useRef(false); @@ -68,14 +55,6 @@ export function App() { } }, [navigate, location.search]); - async function onGoogleSignOut() { - await googleDriveService.signOut(); - setHasGoogleToken(false); - // Purge sessionStorage keys starting with "google-drive:" - clearGoogleDriveCache(); - navigate({pathname: '/'}, {replace: true}); - } - const isViewPage = location.pathname === '/view'; if (isViewPage) { @@ -87,25 +66,14 @@ export function App() { ); } - return ( - <> - setHasGoogleToken(true)} - /> - {staticUrl ? ( - - } /> - - ) : ( - - } /> - } /> - - )} - + return staticUrl ? ( + + } /> + + ) : ( + + } /> + } /> + ); } diff --git a/src/pages/intro_page.tsx b/src/pages/intro_page.tsx new file mode 100644 index 0000000..cd7ff43 --- /dev/null +++ b/src/pages/intro_page.tsx @@ -0,0 +1,43 @@ +import {useMemo} from 'react'; +import {useLocation, useNavigate} from 'react-router'; +import { + clearGoogleDriveCache, + googleDriveService, +} from '../datasource/google_drive_service'; +import {useGoogleAuth} from '../hooks/use_google_auth'; +import {Intro} from '../intro'; +import {TopBar} from '../menu/top_bar'; +import {getArguments} from '../util/url_args'; + +/** + * IntroPage component that represents the landing page of the application. + * It renders the intro text and lists examples alongside its own TopBar. + */ +export function IntroPage() { + const {hasGoogleToken, setHasGoogleToken} = useGoogleAuth(); + const navigate = useNavigate(); + const location = useLocation(); + + const args = useMemo(() => getArguments(location), [location]); + const standalone = args.standalone; + + async function onGoogleSignOut() { + await googleDriveService.signOut(); + setHasGoogleToken(false); + clearGoogleDriveCache(); + navigate({pathname: '/'}, {replace: true}); + } + + return ( + <> + setHasGoogleToken(true)} + /> + + + ); +}