mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-07-17 17:21:48 +00:00
Extract IntroPage from app.tsx
This commit is contained in:
@@ -102,7 +102,7 @@ We will gradually eliminate React state variables in `src/app.tsx` and derive th
|
|||||||
#### Phase 4: Structural Page Nesting
|
#### 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.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 `<GoogleAuthModal>` error fallback state and rendering.
|
* [x] **Step 4.2:** Create `src/pages/view_page.tsx` containing layout rendering (`renderMainArea`), asynchronous data loaders, view state management, `revokeObjectUrls` cleanups, and `<GoogleAuthModal>` 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 `<IntroPage />` or `<ViewPage />`. In `App`, handle Google Drive redirection synchronously during the render pass by returning a `<Navigate replace />` 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 `<ViewPage />`. Run final verification scripts (`npm run check:all`).
|
* [ ] **Step 4.4:** Refactor `src/app.tsx` to serve as a pure routing switch routing to `<IntroPage />` or `<ViewPage />`. In `App`, handle Google Drive redirection synchronously during the render pass by returning a `<Navigate replace />` 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 `<ViewPage />`. Run final verification scripts (`npm run check:all`).
|
||||||
|
|
||||||
## Future Considerations
|
## Future Considerations
|
||||||
|
|||||||
58
src/app.tsx
58
src/app.tsx
@@ -1,33 +1,20 @@
|
|||||||
import queryString from 'query-string';
|
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 {Navigate, Route, Routes, useLocation, useNavigate} from 'react-router';
|
||||||
import {
|
import {IntroPage} from './pages/intro_page';
|
||||||
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 {ViewPage} from './pages/view_page';
|
import {ViewPage} from './pages/view_page';
|
||||||
import {getArguments, getStaticUrl} from './util/url_args';
|
import {getStaticUrl} from './util/url_args';
|
||||||
|
|
||||||
const staticUrl = getStaticUrl();
|
const staticUrl = getStaticUrl();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root App component that orchestrates top-level routing, Google Drive "Open with"
|
* 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() {
|
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 navigate = useNavigate();
|
||||||
const location = useLocation();
|
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. */
|
/** Prevents the Google Drive "Open with" state from being processed more than once. */
|
||||||
const stateProcessed = useRef(false);
|
const stateProcessed = useRef(false);
|
||||||
|
|
||||||
@@ -68,14 +55,6 @@ export function App() {
|
|||||||
}
|
}
|
||||||
}, [navigate, location.search]);
|
}, [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';
|
const isViewPage = location.pathname === '/view';
|
||||||
|
|
||||||
if (isViewPage) {
|
if (isViewPage) {
|
||||||
@@ -87,25 +66,14 @@ export function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return staticUrl ? (
|
||||||
<>
|
<Routes>
|
||||||
<TopBar
|
<Route path="*" element={<Navigate to="/view" replace />} />
|
||||||
showingChart={false}
|
</Routes>
|
||||||
standalone={standalone}
|
) : (
|
||||||
hasGoogleToken={hasGoogleToken}
|
<Routes>
|
||||||
onGoogleSignOut={onGoogleSignOut}
|
<Route path="/" element={<IntroPage />} />
|
||||||
onGoogleTokenAcquired={() => setHasGoogleToken(true)}
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
/>
|
</Routes>
|
||||||
{staticUrl ? (
|
|
||||||
<Routes>
|
|
||||||
<Route path="*" element={<Navigate to="/view" replace />} />
|
|
||||||
</Routes>
|
|
||||||
) : (
|
|
||||||
<Routes>
|
|
||||||
<Route path="/" element={<Intro />} />
|
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
|
||||||
</Routes>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/pages/intro_page.tsx
Normal file
43
src/pages/intro_page.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<TopBar
|
||||||
|
showingChart={false}
|
||||||
|
standalone={standalone}
|
||||||
|
hasGoogleToken={hasGoogleToken}
|
||||||
|
onGoogleSignOut={onGoogleSignOut}
|
||||||
|
onGoogleTokenAcquired={() => setHasGoogleToken(true)}
|
||||||
|
/>
|
||||||
|
<Intro />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user