import React, { useEffect } from "react"; import { useNavigate } from "react-router"; import { toast } from "sonner"; import Container from "src/components/Container"; import LottieLoading from "src/components/LottieLoading"; import { Button } from "src/components/ui/button"; import { useInfo } from "src/hooks/useInfo"; import { saveAuthToken } from "src/lib/auth"; import useSetupStore from "src/state/SetupStore"; import { AuthTokenResponse, SetupNodeInfo } from "src/types"; import { handleRequestError } from "src/utils/handleRequestError"; import { request } from "src/utils/request"; let lastStartupErrorTime: string; export function SetupFinish() { const navigate = useNavigate(); const { data: info } = useInfo(true); // poll the info endpoint to auto-redirect when app is running const [loading, setLoading] = React.useState(false); const [connectionError, setConnectionError] = React.useState(false); const hasFetchedRef = React.useRef(false); const startupError = info?.startupError; const startupErrorTime = info?.startupErrorTime; React.useEffect(() => { // lastStartupErrorTime check is required because user may leave page and come back // after re-configuring settings if ( startupError && startupErrorTime && startupErrorTime !== lastStartupErrorTime ) { lastStartupErrorTime = startupErrorTime; toast.error("Failed to start", { description: startupError, }); setLoading(false); setConnectionError(true); } }, [startupError, startupErrorTime]); useEffect(() => { if (!loading) { return; } const timer = setTimeout(() => { // SetupRedirect takes care of redirection once info.running is true // if it still didn't redirect after 30 seconds, we show an error // Typically initial startup should complete in less than 10 seconds. setLoading(false); setConnectionError(true); }, 30000); return () => { clearTimeout(timer); }; }, [loading]); useEffect(() => { if (!info) { return; } // ensure setup call is only called once if (hasFetchedRef.current) { return; } hasFetchedRef.current = true; (async () => { setLoading(true); const succeeded = await finishSetup( useSetupStore.getState().nodeInfo, useSetupStore.getState().unlockPassword ); // only setup call is successful as start is async if (!succeeded) { setLoading(false); setConnectionError(true); } })(); }, [navigate, info]); if (connectionError) { return ( <> Connection Failed · Alby Hub

Connection Failed

Please check your node configuration and try again.

); } return ( <> Setting up... · Alby Hub

Setting up your Hub...

); } const finishSetup = async ( nodeInfo: SetupNodeInfo, unlockPassword: string ): Promise => { try { await request("/api/setup", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ ...nodeInfo, unlockPassword, }), }); const authTokenResponse = await request("/api/start", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ unlockPassword, }), }); if (authTokenResponse) { saveAuthToken(authTokenResponse.token); } return true; } catch (error) { handleRequestError("Failed to connect", error); return false; } };