Start.tsx raw

   1  import React from "react";
   2  import { toast } from "sonner";
   3  import Container from "src/components/Container";
   4  import PasswordInput from "src/components/password/PasswordInput";
   5  import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
   6  import { LoadingButton } from "src/components/ui/custom/loading-button";
   7  import { Label } from "src/components/ui/label";
   8  
   9  import { useInfo } from "src/hooks/useInfo";
  10  import { saveAuthToken } from "src/lib/auth";
  11  import { AuthTokenResponse } from "src/types";
  12  import { handleRequestError } from "src/utils/handleRequestError";
  13  import { request } from "src/utils/request";
  14  
  15  export default function Start() {
  16    const [unlockPassword, setUnlockPassword] = React.useState("");
  17    const [loading, setLoading] = React.useState(false);
  18    const [buttonText, setButtonText] = React.useState("Start");
  19  
  20    const { data: info } = useInfo(true); // poll the info endpoint to auto-redirect when app is running
  21  
  22    const startupState = info?.startupState;
  23    const startupError = info?.startupError;
  24    const startupErrorTime = info?.startupErrorTime;
  25  
  26    React.useEffect(() => {
  27      if (startupState) {
  28        setButtonText(startupState);
  29      }
  30    }, [startupState]);
  31  
  32    React.useEffect(() => {
  33      if (startupError && startupErrorTime) {
  34        toast.error("Failed to start", {
  35          description: startupError,
  36        });
  37        setLoading(false);
  38        setButtonText("Start");
  39        setUnlockPassword("");
  40      }
  41    }, [startupError, startupErrorTime]);
  42  
  43    async function onSubmit(e: React.FormEvent) {
  44      e.preventDefault();
  45      try {
  46        setLoading(true);
  47        setButtonText("Please wait...");
  48  
  49        const authTokenResponse = await request<AuthTokenResponse>("/api/start", {
  50          method: "POST",
  51          headers: {
  52            "Content-Type": "application/json",
  53          },
  54          body: JSON.stringify({
  55            unlockPassword,
  56            permission: "full",
  57          }),
  58        });
  59        if (authTokenResponse) {
  60          saveAuthToken(authTokenResponse.token);
  61        }
  62      } catch (error) {
  63        handleRequestError("Failed to connect", error);
  64        setLoading(false);
  65        setButtonText("Start");
  66        setUnlockPassword("");
  67      }
  68    }
  69  
  70    return (
  71      <Container>
  72        <div className="mx-auto grid gap-5">
  73          <TwoColumnLayoutHeader
  74            title="Start"
  75            pageTitle="Start"
  76            description="Enter your password to unlock and start Alby Hub."
  77          />
  78          <form onSubmit={onSubmit}>
  79            <div className="grid gap-4">
  80              <div className="grid gap-1.5">
  81                <Label htmlFor="password">Password</Label>
  82                <PasswordInput
  83                  id="password"
  84                  onChange={setUnlockPassword}
  85                  autoFocus
  86                  value={unlockPassword}
  87                />
  88              </div>
  89              <LoadingButton
  90                type="submit"
  91                loading={loading}
  92                disabled={!unlockPassword}
  93              >
  94                {buttonText}
  95              </LoadingButton>
  96              {loading && (
  97                <p className="text-muted-foreground text-xs text-center">
  98                  Starting Alby Hub may take a few minutes.
  99                </p>
 100              )}
 101            </div>
 102          </form>
 103        </div>
 104      </Container>
 105    );
 106  }
 107