diff --git a/front/apps/mobile/app/_layout.tsx b/front/apps/mobile/app/_layout.tsx index e7774e7b..8a05aad7 100644 --- a/front/apps/mobile/app/_layout.tsx +++ b/front/apps/mobile/app/_layout.tsx @@ -22,7 +22,6 @@ import { PortalProvider } from "@gorhom/portal"; import { ThemeSelector } from "@kyoo/primitives"; import { NavbarRight, NavbarTitle } from "@kyoo/ui"; import { createQueryClient } from "@kyoo/models"; -import { getSecureItem } from "@kyoo/models/src/secure-store"; import { QueryClientProvider } from "@tanstack/react-query"; import i18next from "i18next"; import { Stack } from "expo-router"; @@ -39,7 +38,7 @@ import { useColorScheme } from "react-native"; import { initReactI18next } from "react-i18next"; import { useTheme } from "yoshiki/native"; import "intl-pluralrules"; -import { ApiUrlContext } from "./index"; +import { AccountContext, useAccounts } from "./index"; // TODO: use a backend to load jsons. import en from "../../../translations/en.json"; @@ -77,29 +76,16 @@ const ThemedStack = ({ onLayout }: { onLayout?: () => void }) => { ); }; -const useApiUrl = (): string | null | undefined => { - const [apiUrl, setApiUrl] = useState(undefined); - - useEffect(() => { - async function run() { - const apiUrl = await getSecureItem("apiUrl"); - setApiUrl(apiUrl); - } - run(); - }, []); - - return apiUrl; -} export default function Root() { const [queryClient] = useState(() => createQueryClient()); const theme = useColorScheme(); const [fontsLoaded] = useFonts({ Poppins_300Light, Poppins_400Regular, Poppins_900Black }); - const apiUrl = useApiUrl(); + const info = useAccounts(); - if (!fontsLoaded || apiUrl === undefined) return ; + if (!fontsLoaded || info.type === "loading") return ; return ( - + - + ); } diff --git a/front/apps/mobile/app/index.tsx b/front/apps/mobile/app/index.tsx index d587ed43..495ed6a9 100644 --- a/front/apps/mobile/app/index.tsx +++ b/front/apps/mobile/app/index.tsx @@ -18,15 +18,85 @@ * along with Kyoo. If not, see . */ +import { Account, loginFunc } from "@kyoo/models"; +import { getSecureItem, setSecureItem } from "@kyoo/models/src/secure-store"; +import { Button, CircularProgress, H1, P } from "@kyoo/primitives"; import { Redirect } from "expo-router"; -import { createContext, useContext } from "react"; +import { createContext, useContext, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { View } from "react-native"; +import { useYoshiki } from "yoshiki/native"; +import { useRouter } from "solito/router"; -export const ApiUrlContext = createContext(null); +export const useAccounts = () => { + const [accounts, setAccounts] = useState(null); + const [verified, setVerified] = useState<{ + status: "ok" | "error" | "loading" | "unverified"; + error?: string; + }>({ status: "loading" }); + // TODO: Remember the last selected account. + const selected = accounts?.length ? 0 : null; + + useEffect(() => { + async function run() { + const accounts = await getSecureItem("accounts"); + setAccounts(accounts ? JSON.parse(accounts) : []); + } + run(); + }, []); + + useEffect(() => { + async function check() { + const selAcc = accounts![selected!]; + await setSecureItem("apiUrl", selAcc.apiUrl); + const verif = await loginFunc("refresh", selAcc.refresh_token); + setVerified(verif.ok ? { status: "ok" } : { status: "error", error: verif.error }); + } + + if (accounts && selected !== null) check(); + else setVerified({status: "unverified"}); + }, [accounts, selected, verified.status]); + + if (accounts === null || verified.status === "loading") return { type: "loading" } as const; + if (verified.status === "error") { + return { + type: "error", + error: verified.error, + retry: () => setVerified({ status: "loading" }), + }; + } + return { type: "ok", accounts, selected } as const; +}; + +export const ConnectionError = ({ error, retry }: { error?: string; retry: () => void }) => { + const { css } = useYoshiki(); + const { t } = useTranslation(); + const router = useRouter(); + + return ( + theme.colors.red })}> +

{t("error.connection")}

+

{error ?? t("error.unknown")}

+

{t("error.connection-tips")}

+