mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
182 lines
5.0 KiB
TypeScript
182 lines
5.0 KiB
TypeScript
type SetupWindowLike = {
|
|
isDestroyed: () => boolean;
|
|
};
|
|
|
|
type OpenHandlerDecision = { action: 'deny' };
|
|
|
|
type FocusableWindowLike = {
|
|
focus: () => void;
|
|
};
|
|
|
|
export function createHandleManualAnilistSetupSubmissionHandler(deps: {
|
|
consumeCallbackUrl: (rawUrl: string) => boolean;
|
|
redirectUri: string;
|
|
logWarn: (message: string) => void;
|
|
}) {
|
|
return (rawUrl: string): boolean => {
|
|
if (!rawUrl.startsWith('subminer://anilist-setup')) {
|
|
return false;
|
|
}
|
|
try {
|
|
const parsed = new URL(rawUrl);
|
|
const accessToken = parsed.searchParams.get('access_token')?.trim() ?? '';
|
|
if (accessToken.length > 0) {
|
|
return deps.consumeCallbackUrl(
|
|
`${deps.redirectUri}#access_token=${encodeURIComponent(accessToken)}`,
|
|
);
|
|
}
|
|
deps.logWarn('AniList setup submission missing access token');
|
|
return true;
|
|
} catch {
|
|
deps.logWarn('AniList setup submission had invalid callback input');
|
|
return true;
|
|
}
|
|
};
|
|
}
|
|
|
|
export function createMaybeFocusExistingAnilistSetupWindowHandler(deps: {
|
|
getSetupWindow: () => FocusableWindowLike | null;
|
|
}) {
|
|
return (): boolean => {
|
|
const window = deps.getSetupWindow();
|
|
if (!window) {
|
|
return false;
|
|
}
|
|
window.focus();
|
|
return true;
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupWindowOpenHandler(deps: {
|
|
isAllowedExternalUrl: (url: string) => boolean;
|
|
openExternal: (url: string) => void;
|
|
logWarn: (message: string, details?: unknown) => void;
|
|
}) {
|
|
return ({ url }: { url: string }): OpenHandlerDecision => {
|
|
if (!deps.isAllowedExternalUrl(url)) {
|
|
deps.logWarn('Blocked unsafe AniList setup external URL', { url });
|
|
return { action: 'deny' };
|
|
}
|
|
deps.openExternal(url);
|
|
return { action: 'deny' };
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupWillNavigateHandler(deps: {
|
|
handleManualSubmission: (url: string) => boolean;
|
|
consumeCallbackUrl: (url: string) => boolean;
|
|
redirectUri: string;
|
|
isAllowedNavigationUrl: (url: string) => boolean;
|
|
logWarn: (message: string, details?: unknown) => void;
|
|
}) {
|
|
return (params: { url: string; preventDefault: () => void }): void => {
|
|
const { url, preventDefault } = params;
|
|
if (deps.handleManualSubmission(url)) {
|
|
preventDefault();
|
|
return;
|
|
}
|
|
if (deps.consumeCallbackUrl(url)) {
|
|
preventDefault();
|
|
return;
|
|
}
|
|
if (url.startsWith(deps.redirectUri)) {
|
|
preventDefault();
|
|
return;
|
|
}
|
|
if (url.startsWith(`${deps.redirectUri}#`)) {
|
|
preventDefault();
|
|
return;
|
|
}
|
|
if (deps.isAllowedNavigationUrl(url)) {
|
|
return;
|
|
}
|
|
preventDefault();
|
|
deps.logWarn('Blocked unsafe AniList setup navigation URL', { url });
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupWillRedirectHandler(deps: {
|
|
consumeCallbackUrl: (url: string) => boolean;
|
|
}) {
|
|
return (params: { url: string; preventDefault: () => void }): void => {
|
|
if (deps.consumeCallbackUrl(params.url)) {
|
|
params.preventDefault();
|
|
}
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupDidNavigateHandler(deps: {
|
|
consumeCallbackUrl: (url: string) => boolean;
|
|
}) {
|
|
return (url: string): void => {
|
|
deps.consumeCallbackUrl(url);
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupDidFailLoadHandler(deps: {
|
|
onLoadFailure: (details: { errorCode: number; errorDescription: string; validatedURL: string }) => void;
|
|
}) {
|
|
return (details: { errorCode: number; errorDescription: string; validatedURL: string }): void => {
|
|
deps.onLoadFailure(details);
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupDidFinishLoadHandler(deps: {
|
|
getLoadedUrl: () => string;
|
|
onBlankPageLoaded: () => void;
|
|
}) {
|
|
return (): void => {
|
|
const loadedUrl = deps.getLoadedUrl();
|
|
if (!loadedUrl || loadedUrl === 'about:blank') {
|
|
deps.onBlankPageLoaded();
|
|
}
|
|
};
|
|
}
|
|
|
|
export function createHandleAnilistSetupWindowClosedHandler(deps: {
|
|
clearSetupWindow: () => void;
|
|
setSetupPageOpened: (opened: boolean) => void;
|
|
}) {
|
|
return (): void => {
|
|
deps.clearSetupWindow();
|
|
deps.setSetupPageOpened(false);
|
|
};
|
|
}
|
|
|
|
export function createHandleAnilistSetupWindowOpenedHandler(deps: {
|
|
setSetupWindow: () => void;
|
|
setSetupPageOpened: (opened: boolean) => void;
|
|
}) {
|
|
return (): void => {
|
|
deps.setSetupWindow();
|
|
deps.setSetupPageOpened(true);
|
|
};
|
|
}
|
|
|
|
export function createAnilistSetupFallbackHandler(deps: {
|
|
authorizeUrl: string;
|
|
developerSettingsUrl: string;
|
|
setupWindow: SetupWindowLike;
|
|
openSetupInBrowser: () => void;
|
|
loadManualTokenEntry: () => void;
|
|
logError: (message: string, details: unknown) => void;
|
|
logWarn: (message: string) => void;
|
|
}) {
|
|
return {
|
|
onLoadFailure: (details: { errorCode: number; errorDescription: string; validatedURL: string }) => {
|
|
deps.logError('AniList setup window failed to load', details);
|
|
deps.openSetupInBrowser();
|
|
if (!deps.setupWindow.isDestroyed()) {
|
|
deps.loadManualTokenEntry();
|
|
}
|
|
},
|
|
onBlankPageLoaded: () => {
|
|
deps.logWarn('AniList setup loaded a blank page; using fallback');
|
|
deps.openSetupInBrowser();
|
|
if (!deps.setupWindow.isDestroyed()) {
|
|
deps.loadManualTokenEntry();
|
|
}
|
|
},
|
|
};
|
|
}
|