AuthCodeForm.tsx raw

   1  import { RefreshCwIcon } from "lucide-react";
   2  import React, { useState } from "react";
   3  import { useNavigate } from "react-router";
   4  import Container from "src/components/Container";
   5  import PasswordInput from "src/components/password/PasswordInput";
   6  import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
   7  import { Button } from "src/components/ui/button";
   8  import { LoadingButton } from "src/components/ui/custom/loading-button";
   9  import { Label } from "src/components/ui/label";
  10  import { UnlinkAlbyAccount } from "src/components/UnlinkAlbyAccount";
  11  
  12  import { useInfo } from "src/hooks/useInfo";
  13  import { handleRequestError } from "src/utils/handleRequestError";
  14  import { openLink } from "src/utils/openLink";
  15  import { request } from "src/utils/request"; // build the project for this to appear
  16  
  17  type AuthCodeFormProps = {
  18    url: string;
  19  };
  20  
  21  function AuthCodeForm({ url }: AuthCodeFormProps) {
  22    const [authCode, setAuthCode] = useState("");
  23    const navigate = useNavigate();
  24  
  25    const { data: info, mutate: refetchInfo } = useInfo();
  26  
  27    const [hasRequestedCode, setRequestedCode] = React.useState(false);
  28    const [isLoading, setLoading] = React.useState(false);
  29  
  30    async function requestAuthCode() {
  31      setRequestedCode((hasRequestedCode) => {
  32        if (!url) {
  33          return false;
  34        }
  35        if (!hasRequestedCode) {
  36          openLink(url);
  37        }
  38        return true;
  39      });
  40    }
  41  
  42    async function onSubmit(e: React.FormEvent) {
  43      e.preventDefault();
  44      setLoading(true);
  45      try {
  46        await request(`/api/alby/callback?code=${authCode}`, {
  47          headers: {
  48            "Content-Type": "application/json",
  49          },
  50        });
  51        await refetchInfo();
  52        navigate("/");
  53      } catch (error) {
  54        handleRequestError("Failed to connect", error);
  55      }
  56      setLoading(false);
  57    }
  58  
  59    return (
  60      <Container>
  61        <form onSubmit={onSubmit} className="flex flex-col items-center w-full">
  62          <div className="grid gap-5">
  63            <TwoColumnLayoutHeader
  64              title="Connect your Alby Account"
  65              pageTitle="Connect your Alby Account"
  66              description="A new window will open. Sign in with your Alby Account, copy the Authorization Code, and paste it here."
  67            />
  68            {!hasRequestedCode && (
  69              <>
  70                <Button onClick={requestAuthCode}>
  71                  Request Authorization Code
  72                </Button>
  73              </>
  74            )}
  75            {hasRequestedCode && (
  76              <>
  77                <div className="grid gap-4 w-full">
  78                  <div className="grid gap-1.5">
  79                    <Label htmlFor="authorization-code">Authorization Code</Label>
  80                    <PasswordInput
  81                      autoFocus
  82                      id="authorization-code"
  83                      placeholder="Enter code you see in the browser"
  84                      onChange={setAuthCode}
  85                      value={authCode}
  86                    />
  87                  </div>
  88                </div>
  89                <div className="flex gap-4">
  90                  <LoadingButton loading={isLoading} className="flex-1">
  91                    Submit
  92                  </LoadingButton>
  93                  <Button
  94                    type="button"
  95                    size="icon"
  96                    variant="outline"
  97                    onClick={() => url && openLink(url)}
  98                  >
  99                    <RefreshCwIcon className="size-4" />
 100                  </Button>
 101                </div>
 102              </>
 103            )}
 104            {info?.albyUserIdentifier && (
 105              <div className="flex flex-col justify-center items-center mt-15 gap-5">
 106                <p className="text-muted-foreground">or</p>
 107                <UnlinkAlbyAccount>
 108                  <Button variant="outline" size="sm">
 109                    Disconnect Alby Account
 110                  </Button>
 111                </UnlinkAlbyAccount>
 112              </div>
 113            )}
 114          </div>
 115        </form>
 116      </Container>
 117    );
 118  }
 119  
 120  export default AuthCodeForm;
 121