64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
import { useLocalSearchParams, useRouter } from "expo-router";
|
|
import React, { useEffect, useRef, useState } from "react";
|
|
import { Text, View } from "react-native";
|
|
import { clearPendingConsents, loadPendingConsents, useApp } from "../../src/app-context";
|
|
import { AuthLoadingView } from "../../src/components/AuthLoadingView";
|
|
import { ScreenShell } from "../../src/components/ScreenShell";
|
|
import { pendingDialogKey, restorePendingIntents } from "../../src/pending-intent";
|
|
import { spacing } from "../../src/theme";
|
|
import { Button, ErrorNotice, styles } from "../../src/ui";
|
|
|
|
export default function AuthCallbackScreen() {
|
|
const params = useLocalSearchParams<{ code?: string; state?: string; error?: string }>();
|
|
const app = useApp();
|
|
const router = useRouter();
|
|
const [error, setError] = useState<unknown>();
|
|
const completionStarted = useRef(false);
|
|
|
|
const complete = async () => {
|
|
if (!params.code || !params.state) return;
|
|
const consents = await loadPendingConsents();
|
|
if (!consents) throw new Error("Не найдены локально принятые согласия. Начните вход заново.");
|
|
setError(undefined);
|
|
await app.finishCallback(params.code, params.state, consents);
|
|
await clearPendingConsents();
|
|
await restorePendingIntents();
|
|
|
|
if (pendingDialogKey()) {
|
|
router.replace("/dialogs?pending=1");
|
|
return;
|
|
}
|
|
|
|
router.replace("/");
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (completionStarted.current) return;
|
|
if (params.error) {
|
|
completionStarted.current = true;
|
|
setError(new Error("Авторизация отменена или отклонена."));
|
|
return;
|
|
}
|
|
if (!params.code || !params.state) return;
|
|
completionStarted.current = true;
|
|
void complete().catch(setError);
|
|
}, [params.code, params.state, params.error]);
|
|
|
|
return (
|
|
<ScreenShell>
|
|
{!error ? (
|
|
<AuthLoadingView />
|
|
) : (
|
|
<View style={{ flex: 1, justifyContent: "center", padding: spacing.lg, gap: spacing.lg }}>
|
|
<Text accessibilityRole="header" style={styles.title}>Не удалось завершить вход</Text>
|
|
<>
|
|
<ErrorNotice error={error} />
|
|
<Button title="Повторить" onPress={() => void complete().catch(setError)} />
|
|
<Button title="На главную" secondary onPress={() => router.replace("/")} />
|
|
</>
|
|
</View>
|
|
)}
|
|
</ScreenShell>
|
|
);
|
|
}
|