"use client"; import { useRef, useState } from "react"; import { Alert, Button, Group, Paper, Stack, Text, ThemeIcon, Title } from "@mantine/core"; import { IconArrowRight, IconDatabaseImport } from "@tabler/icons-react"; import { clientApi } from "@homarr/api/client"; import { revalidatePathActionAsync } from "@homarr/common/client"; import { showErrorNotification } from "@homarr/notifications"; import { useI18n } from "@homarr/translation/client"; import { OnboardingBackdrop } from "./onboarding-backdrop"; import { OnboardingWordmark } from "./onboarding-wordmark"; import type { OnboardingStudioProps } from "./types"; import { useOnboardingSounds, useWelcomeSound } from "./use-onboarding-sounds"; import classes from "./onboarding-studio.module.css"; export const Welcome = ({ environment, sqliteRestore }: OnboardingStudioProps) => { const t = useI18n("init.studio.welcome"); const claimT = useI18n("init.studio.claim"); const [showRestore, setShowRestore] = useState(false); const [claimPending, setClaimPending] = useState(false); const [claimError, setClaimError] = useState(null); const sounds = useOnboardingSounds(); const welcomeSound = useWelcomeSound(); const welcomeSoundPlayed = useRef(false); const start = clientApi.onboard.nextStep.useMutation({ async onSuccess() { sounds.swoosh(); await revalidatePathActionAsync("/init"); }, onError() { sounds.error(); showErrorNotification({ title: t("errorTitle"), message: t("errorDescription") }); }, }); const getClaimError = (code: unknown) => { switch (code) { case "finished": case "unavailable": case "cross_site": return claimT("errorDescription"); case "administrator_required": return claimT("signInDescription"); case "locked": return claimT("lockedDescription"); default: return t("errorDescription"); } }; const claimOnboardingAsync = async () => { setClaimPending(true); setClaimError(null); try { const response = await fetch("/api/onboarding/claim", { method: "POST" }); if (!response.ok) { const body = (await response.json().catch(() => null)) as { code?: unknown } | null; throw new Error(getClaimError(body?.code)); } return true; } catch (error) { sounds.error(); setClaimError(error instanceof Error ? error.message : t("errorTitle")); return false; } finally { setClaimPending(false); } }; const startOnboardingAsync = async () => { if (!welcomeSoundPlayed.current) { welcomeSoundPlayed.current = true; welcomeSound.notify(); } else sounds.click(); if (await claimOnboardingAsync()) await start.mutateAsync(); }; const toggleRestoreAsync = async () => { const playedWelcomeSound = !welcomeSoundPlayed.current; if (playedWelcomeSound) { welcomeSoundPlayed.current = true; welcomeSound.notify(); } if (showRestore) { if (!playedWelcomeSound) sounds.toggle(false); setShowRestore(false); return; } if (await claimOnboardingAsync()) { if (!playedWelcomeSound) sounds.toggle(true); setShowRestore(true); } }; return (
{t("title")} {t("description", { version: environment.version })} {environment.databaseDriver === "sqlite" && sqliteRestore ? ( ) : null} {showRestore && sqliteRestore ? ( {t("restoreTitle")} {t("restoreDescription")} {sqliteRestore} ) : null} {claimError || start.error ? ( {claimError ?? t("errorDescription")} ) : null}
); };