Spaces:
Running
Running
mfn
Share: permanent links (?w=) that open as your own copy; sign in with Hugging Face to share
aab2748 Download src/components/SignInGate.tsx from mrfakename/flow: direct link, hf CLI and curl.
- Browser
- Download file 5.16 kB
-
https://huggingface.co/spaces/mrfakename/flow/resolve/main/src/components/SignInGate.tsx
- Command line
-
hf download hf://spaces/mrfakename/flow/src/components/SignInGate.tsx
-
curl -L -o SignInGate.tsx https://huggingface.co/spaces/mrfakename/flow/resolve/main/src/components/SignInGate.tsx
5.16 kB
| import { Button } from "@cloudflare/kumo"; | |
| import { ArrowSquareOutIcon, ArrowClockwiseIcon, SpinnerGapIcon } from "@phosphor-icons/react"; | |
| import flowMark from "../assets/flow-mark.svg"; | |
| import hfLogo from "../assets/hf-logo.svg"; | |
| import { useEffect, useState } from "react"; | |
| import { pendingShare } from "../lib/deepLink"; | |
| import { isEmbedded, newTabUrl } from "../lib/hfAuth"; | |
| import { fetchShare } from "../lib/share"; | |
| import { useAuthStore } from "../store/useAuthStore"; | |
| import { useSyncStore } from "../store/useSyncStore"; | |
| const LOGO = <img src={hfLogo} alt="" aria-hidden className="h-[18px] w-[18px]" draggable={false} />; | |
| /** Someone opened a share link while signed out: say what it is before asking them to sign in. */ | |
| function SharePreview() { | |
| const [info, setInfo] = useState<{ owner: string; title: string } | null>(null); | |
| useEffect(() => { | |
| const id = pendingShare(); | |
| if (!id) return; | |
| let live = true; | |
| void fetchShare(id) | |
| .then((s) => live && setInfo({ owner: s.owner, title: s.title })) | |
| .catch(() => undefined); | |
| return () => { | |
| live = false; | |
| }; | |
| }, []); | |
| if (!info) return null; | |
| return ( | |
| <p className="gate__msg gate__share"> | |
| <strong>@{info.owner}</strong> shared “{info.title}” with you. Sign in to open your own copy. | |
| </p> | |
| ); | |
| } | |
| /** | |
| * Everything before the editor: signed out, signing in (embedded: in a new tab), and loading the | |
| * account's storage. Flow keeps workflows only in the user's Hugging Face bucket, so there is no | |
| * signed-out editor. | |
| */ | |
| export function SignInGate() { | |
| const user = useAuthStore((s) => s.user); | |
| const busy = useAuthStore((s) => s.busy); | |
| const configured = useAuthStore((s) => s.configured); | |
| const pending = useAuthStore((s) => s.pending); | |
| const error = useAuthStore((s) => s.error); | |
| const bootError = useSyncStore((s) => s.bootError); | |
| const auth = useAuthStore.getState(); | |
| let body: React.ReactNode; | |
| if (user && bootError) { | |
| body = ( | |
| <> | |
| <p className="gate__msg" role="alert"> | |
| {bootError} | |
| </p> | |
| <div className="gate__actions"> | |
| <Button variant="primary" icon={ArrowClockwiseIcon} onClick={() => void useSyncStore.getState().boot()}> | |
| Retry | |
| </Button> | |
| <Button variant="secondary" onClick={() => auth.login()}> | |
| Sign in again | |
| </Button> | |
| </div> | |
| <button type="button" className="gate__link" onClick={() => auth.logout()}> | |
| Sign out | |
| </button> | |
| </> | |
| ); | |
| } else if (user || busy) { | |
| body = ( | |
| <p className="gate__msg gate__loading" aria-live="polite"> | |
| <SpinnerGapIcon size={16} className="animate-spin" /> | |
| {user ? "Loading your workflows…" : "Signing in…"} | |
| </p> | |
| ); | |
| } else if (!configured) { | |
| body = <p className="gate__msg">Sign-in isn't configured for this deployment (no OAuth client id).</p>; | |
| } else if (pending === "tab") { | |
| body = ( | |
| <> | |
| <p className="gate__msg"> | |
| <strong>Signed in in the new tab?</strong> Continue there: it's the full app, and your work saves to your account either way. | |
| </p> | |
| <div className="gate__actions"> | |
| <Button variant="primary" icon={ArrowClockwiseIcon} onClick={() => void auth.finishLogin()}> | |
| Use it here too | |
| </Button> | |
| <Button variant="secondary" icon={ArrowSquareOutIcon} onClick={() => auth.openApp()}> | |
| Open Flow in a new tab | |
| </Button> | |
| </div> | |
| {error && ( | |
| <p className="gate__err" role="alert"> | |
| {error} | |
| </p> | |
| )} | |
| <p className="gate__fine"> | |
| Didn't see a tab?{" "} | |
| <a className="gate__link" href={newTabUrl()} target="_blank" rel="noopener"> | |
| Open the sign-in tab | |
| </a>{" "} | |
| ·{" "} | |
| <button type="button" className="gate__link" onClick={() => auth.cancelLogin()}> | |
| Cancel | |
| </button> | |
| </p> | |
| </> | |
| ); | |
| } else { | |
| body = ( | |
| <> | |
| <Button variant="primary" className="gate__cta" icon={LOGO} onClick={() => auth.login()}> | |
| Sign in with Hugging Face | |
| </Button> | |
| {error && ( | |
| <p className="gate__err" role="alert"> | |
| {error} | |
| </p> | |
| )} | |
| <p className="gate__fine"> | |
| {isEmbedded ? "Opens Flow in a new tab to sign in. " : ""}Flow is still in beta and hasn't launched yet. It's an independent tool, not made by Hugging Face. Runs use your own quota; workflows save to a private bucket on your account. | |
| </p> | |
| </> | |
| ); | |
| } | |
| return ( | |
| <div className="gate"> | |
| <div className="gate__card"> | |
| <div className="gate__brand"> | |
| <img src={flowMark} alt="" aria-hidden className="h-7 w-7" draggable={false} /> | |
| <h1 className="gate__title">Flow</h1> | |
| <span className="beta-tag">Beta</span> | |
| </div> | |
| <p className="gate__lede">A visual editor for chaining Hugging Face Spaces into workflows and running them from one canvas.</p> | |
| {!user && <SharePreview />} | |
| {body} | |
| </div> | |
| </div> | |
| ); | |
| } | |