import { EventSource } from 'eventsource' import { QRCode } from "../ui/custom-qr"; import { styled } from "@macaron-css/solid"; import { theme } from "@nestri/www/ui/theme"; import { useNavigate } from "@solidjs/router"; import { keyframes } from "@macaron-css/core"; import { useOpenAuth } from "@openauthjs/solid"; import { useAccount } from "../providers/account"; import { createEffect, createSignal, onCleanup } from "solid-js"; import { Container, Screen as FullScreen } from "@nestri/www/ui/layout"; const Card = styled("div", { base: { padding: `10px 20px`, maxWidth: 360, width: "100%", position: "relative", display: "flex", gap: 20, flexDirection: "column", justifyContent: "center", } }) const LogoFooter = styled("section", { base: { position: "fixed", bottom: -1, fontSize: "100%", maxWidth: 1440, width: "100%", pointerEvents: "none", display: "flex", alignItems: "center", justifyContent: "center", padding: "0 8px", zIndex: 10, overflow: "hidden", }, }) const Logo = styled("svg", { base: { width: "100%", height: "100%", transform: "translateY(40%)", opacity: "70%", } }) const Title = styled("h2", { base: { fontSize: theme.font.size["2xl"], fontWeight: theme.font.weight.semibold, fontFamily: theme.font.family.heading } }) const Subtitle = styled("h2", { base: { fontSize: theme.font.size["base"], fontWeight: theme.font.weight.regular, color: theme.color.gray.d900, } }) const Button = styled("button", { base: { display: "flex", justifyContent: "space-between", alignItems: "center", cursor: "not-allowed", padding: "10px 20px", gap: theme.space["2"], borderRadius: theme.space["2"], backgroundColor: theme.color.background.d100, border: `1px solid ${theme.color.gray.d400}` } }) const ButtonText = styled("span", { base: { fontSize: theme.font.size["lg"], fontWeight: theme.font.weight.medium, fontFamily: theme.font.family.heading, position: "relative", display: "flex", alignItems: "center" } }) const ButtonIcon = styled("svg", { base: { height: 28, width: 28, } }) const ButtonContainer = styled("div", { base: { flexDirection: "column", display: "flex", gap: 10, position: "relative" } }) const bgRotate = keyframes({ 'to': { transform: 'rotate(1turn)' }, }); const shake = keyframes({ "0%": { transform: "translateX(0)", }, "50%": { transform: "translateX(10px)", }, "100%": { transform: "translateX(0)", }, }); const opacity = keyframes({ "0%": { opacity: 1 }, "100%": { opacity: 0 } }) const QRContainer = styled("div", { base: { position: "relative", display: "flex", overflow: "hidden", justifyContent: "center", alignItems: "center", borderRadius: 30, padding: 9, isolation: "isolate", ":after": { content: "", zIndex: -1, inset: 10, backgroundColor: theme.color.background.d100, borderRadius: 30, position: "absolute" } }, variants: { login: { true: { ":before": { content: "", backgroundImage: `conic-gradient(from 0deg,transparent 0,${theme.color.blue.d700} 10%,${theme.color.blue.d700} 25%,transparent 35%)`, animation: `${bgRotate} 2.25s linear infinite`, width: "200%", height: "200%", zIndex: -2, top: "-50%", left: "-50%", position: "absolute" }, } }, error: { true: { animation: `${shake} 100ms ease 3`, ":before": { content: "", inset: 1, background: theme.color.red.d700, opacity: 0, position: "absolute", animation: `${opacity} 3s ease`, width: "200%", height: "200%", } } }, success: { true: { animation: `${shake} 100ms ease 3`, // ":before": { // content: "", // backgroundImage: `conic-gradient(from 0deg,transparent 0,${theme.color.green.d700} 10%,${theme.color.green.d700} 25%,transparent 35%)`, // animation: `${bgRotate} 2.25s linear infinite`, // width: "200%", // height: "200%", // zIndex: -2, // top: "-50%", // left: "-50%", // position: "absolute" // }, ":before": { content: "", inset: 1, background: theme.color.teal.d700, opacity: 0, position: "absolute", animation: `${opacity} 1.1s ease infinite`, width: "200%", height: "200%", } } } } }) const QRBg = styled("div", { base: { backgroundColor: theme.color.background.d200, position: "absolute", inset: 0, margin: 5, borderRadius: 27 } }) const QRWrapper = styled("div", { base: { height: "max-content", width: "max-content", backgroundColor: theme.color.d1000.gray, position: "relative", textWrap: "balance", display: "flex", justifyContent: "center", alignItems: "center", overflow: "hidden", borderRadius: 22, padding: 20, }, variants: { error: { true: { filter: "blur(3px)", } } } }) const QRReloadBtn = styled("button", { base: { background: "none", border: "none", width: 50, height: 50, position: "absolute", borderRadius: 25, zIndex: 5, right: 2, bottom: 2, cursor: "pointer", color: theme.color.blue.d700, transition: "color 200ms", overflow: "hidden", display: "flex", justifyContent: "center", alignItems: "center", ":before": { zIndex: 3, content: "", position: "absolute", inset: 0, opacity: 0, transition: "opacity 200ms", background: "#FFF" } } }) const QRRealoadContainer = styled("div", { base: { position: "absolute", inset: 0, isolation: "isolate", ":before": { background: `conic-gradient( from 90deg, currentColor 10%, #FFF 80% )`, inset: 3, borderRadius: 16, position: "absolute", content: "", zIndex: 1 } } }) const QRReloadSvg = styled("svg", { base: { zIndex: 2, width: "100%", height: "100%", position: "relative", display: "block" } }) const LogoContainer = styled("div", { base: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", color: theme.color.gray.d100 } }) const LogoIcon = styled("svg", { base: { zIndex: 6, position: "absolute", left: "50%", top: "50%", transform: "translate(-50%,-50%)", overflow: "hidden", borderRadius: 17, } }) const Divider = styled("hr", { base: { height: "100%", backgroundColor: theme.color.gray.d400, width: 2, border: "none", margin: "0 20px", padding: 0, } }) const CardWrapper = styled("div", { base: { width: "100%", position: "relative", height: "max-content", flexDirection: "row", display: "flex", alignItems: "start", justifyContent: "start", top: "25vh" } }) const Footer = styled("div", { base: { flexDirection: "column", display: "flex", gap: 10 } }) const Soon = styled("div", { base: { borderRadius: ".375rem", padding: "2px 4px", fontWeight: theme.font.weight.semibold, fontFamily: theme.font.family.heading, fontSize: ".625rem", color: theme.color.blue.d900, backgroundColor: theme.color.blue.d400, textTransform: "uppercase", marginLeft: 5 } }) const Link = styled("a", { base: { fontSize: theme.font.size["base"], fontWeight: theme.font.weight.regular, color: theme.color.gray.d900, textDecoration: "underline", textUnderlineOffset: 2 } }) export function CreateTeamComponent() { const nav = useNavigate(); const auth = useOpenAuth(); const account = useAccount(); const [challengeUrl, setChallengeUrl] = createSignal(null); const [timedOut, setTimedOut] = createSignal(false); const [errorMsg, setErrorMsg] = createSignal(""); const [loginSuccess, setLoginSuccess] = createSignal(false); // bump this to reconnect const [retryCount, setRetryCount] = createSignal(0); let currentStream: EventSource | null = null; const connectStream = async () => { // clear previous state setChallengeUrl(null); setTimedOut(false); setErrorMsg(null); if (currentStream) { currentStream.close(); } const token = await auth.access(); const stream = new EventSource( `${import.meta.env.VITE_API_URL}/steam/login`, { fetch: (input, init) => fetch(input, { ...init, headers: { ...init?.headers, Authorization: `Bearer ${token}`, }, }), } ); currentStream = stream; // status // stream.addEventListener("status", (e) => { // // setStatus(JSON.parse(e.data).message); // }); // challenge URL stream.addEventListener("challenge_url", (e) => { setChallengeUrl(JSON.parse(e.data).url); }); // success stream.addEventListener("login_success", (e) => { setLoginSuccess(true); }); // timed out stream.addEventListener("timed_out", (e) => { setTimedOut(true); }); // server-side error stream.addEventListener("error", (e: any) => { // Network‐level errors also fire here try { const err = JSON.parse(e.data).message setErrorMsg(err); } catch { setErrorMsg("Connection error"); } //Event source has inbuilt retry method,this is to prevent it from firing stream.close() }); // team slug stream.addEventListener("team_slug", async (e) => { await account.refresh(account.current.email) {/**FIXME: Somehow this does not work when the user is in the "/new" page */ } nav(`/${JSON.parse(e.data).username}`) }); }; // kick it off on mount _and_ whenever retryCount changes createEffect(() => { // read retryCount so effect re-runs retryCount(); connectStream(); // ensure cleanup if component unmounts onCleanup(() => currentStream?.close()); }); return ( Connect your game library to get started.
Help I can't connect my account
{(challengeUrl() && !timedOut() && !loginSuccess() && !errorMsg()) ? () : ()} {(!loginSuccess() && timedOut() || errorMsg()) && ( setRetryCount((c) => c + 1)}> )} {loginSuccess() ? "Login successful" : (timedOut() || !!errorMsg()) ? "Login timed out" : "Scan to connect Steam" } { loginSuccess() ? "Just a minute while we create your team" : (timedOut() || !!errorMsg()) ? "Failed to connect Steam. Please try again." : "On your mobile phone, open the Stream App to scan this code"}
) }