diff --git a/src/app.tsx b/src/app.tsx index fcebd74..37e350b 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,6 +1,14 @@ import queryString from 'query-string'; import {useMemo} from 'react'; import {Navigate, Route, Routes, useLocation} from 'react-router'; +import { + GOOGLE_DRIVE_REDIRECT_KEYS, + handleGoogleDriveRedirect, +} from './datasource/google_drive'; +import { + handleWikiTreeRedirect, + WIKITREE_REDIRECT_KEYS, +} from './datasource/wikitree'; import {IntroPage} from './pages/intro_page'; import {ViewPage} from './pages/view_page'; import {getStaticUrl} from './util/url_args'; @@ -28,43 +36,31 @@ export function App() { let paramsModified = false; // 1. Handle Google Drive "Open with" action - const stateParam = mergedParams.state; - if (typeof stateParam === 'string') { - try { - const parsedState = JSON.parse(stateParam); - if ( - parsedState && - parsedState.action === 'open' && - Array.isArray(parsedState.ids) && - parsedState.ids.length > 0 - ) { - const fileId = parsedState.ids[0]; - redirectPath = '/view'; - mergedParams.source = 'google-drive'; - mergedParams.fileId = fileId; - delete mergedParams.state; - paramsModified = true; - } - } catch (err) { - // Silently catch JSON parsing errors for state parameters not meant for us - console.warn( - 'Google Drive state query parameter JSON parsing failed or action mismatch:', - err, - ); - } - } - - // 2. Handle WikiTree authcode presence - if (windowSearch.authcode) { - redirectPath = redirectPath || location.pathname; + const gdResult = handleGoogleDriveRedirect(mergedParams); + if (gdResult) { + redirectPath = gdResult.redirectPath; + mergedParams.source = 'google-drive'; + mergedParams.fileId = gdResult.fileId; + delete mergedParams.state; paramsModified = true; } - if (paramsModified || windowSearch.state || windowSearch.authcode) { + // 2. Handle WikiTree authcode presence + const wtResult = handleWikiTreeRedirect(windowSearch, location.pathname); + if (wtResult) { + redirectPath = redirectPath || wtResult.redirectPath; + paramsModified = paramsModified || wtResult.paramsModified; + } + + const hasRedirectKeys = + GOOGLE_DRIVE_REDIRECT_KEYS.some((k) => windowSearch[k] !== undefined) || + WIKITREE_REDIRECT_KEYS.some((k) => windowSearch[k] !== undefined); + + if (paramsModified || hasRedirectKeys) { // Strip external state / authcode parameters from window.location.search to prevent redirect loops. const cleanWindowSearch = {...windowSearch}; - delete cleanWindowSearch.state; - delete cleanWindowSearch.authcode; + GOOGLE_DRIVE_REDIRECT_KEYS.forEach((k) => delete cleanWindowSearch[k]); + WIKITREE_REDIRECT_KEYS.forEach((k) => delete cleanWindowSearch[k]); const cleanWindowSearchStr = queryString.stringify(cleanWindowSearch); const newUrl = diff --git a/src/datasource/google_drive.ts b/src/datasource/google_drive.ts index e5dca47..6483694 100644 --- a/src/datasource/google_drive.ts +++ b/src/datasource/google_drive.ts @@ -1,3 +1,4 @@ +import type {ParsedQuery} from 'query-string'; import {TopolaData} from '../util/gedcom_util'; import {DataSource, DataSourceEnum, SourceSelection} from './data_source'; import { @@ -92,3 +93,34 @@ export class GoogleDriveDataSource implements DataSource return loadAndPrepareFile(blob, cacheKey, onProgress); } } + +export const GOOGLE_DRIVE_REDIRECT_KEYS = ['state']; + +export function handleGoogleDriveRedirect( + mergedParams: ParsedQuery, +): {redirectPath: string; fileId: string} | null { + const stateParam = mergedParams.state; + if (typeof stateParam === 'string') { + try { + const parsedState = JSON.parse(stateParam); + if ( + parsedState && + parsedState.action === 'open' && + Array.isArray(parsedState.ids) && + parsedState.ids.length > 0 + ) { + return { + redirectPath: '/view', + fileId: parsedState.ids[0], + }; + } + } catch (err) { + // Silently catch JSON parsing errors for state parameters not meant for us + console.warn( + 'Google Drive state query parameter JSON parsing failed or action mismatch:', + err, + ); + } + } + return null; +} diff --git a/src/datasource/wikitree.ts b/src/datasource/wikitree.ts index d52574c..3025eec 100644 --- a/src/datasource/wikitree.ts +++ b/src/datasource/wikitree.ts @@ -1,3 +1,4 @@ +import type {ParsedQuery} from 'query-string'; import {IntlShape} from 'react-intl'; import {analyticsEvent} from '../util/analytics'; import {TopolaError} from '../util/error'; @@ -109,3 +110,18 @@ export class WikiTreeDataSource implements DataSource { } } } + +export const WIKITREE_REDIRECT_KEYS = ['authcode']; + +export function handleWikiTreeRedirect( + windowSearch: ParsedQuery, + currentPathname: string, +): {redirectPath: string; paramsModified: boolean} | null { + if (windowSearch.authcode) { + return { + redirectPath: currentPathname, + paramsModified: true, + }; + } + return null; +}