pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
at main 161 lines 5.2 kB view raw
1import { useState } from "react"; 2import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3"; 3import { useTranslation } from "react-i18next"; 4import { useNavigate } from "react-router-dom"; 5 6import { MetaResponse } from "@/backend/accounts/meta"; 7import { Button } from "@/components/buttons/Button"; 8import { BackendSelector } from "@/components/form/BackendSelector"; 9import { 10 LargeCard, 11 LargeCardButtons, 12 LargeCardText, 13} from "@/components/layout/LargeCard"; 14import { SubPageLayout } from "@/pages/layouts/SubPageLayout"; 15import { 16 AccountCreatePart, 17 AccountProfile, 18} from "@/pages/parts/auth/AccountCreatePart"; 19import { PassphraseGeneratePart } from "@/pages/parts/auth/PassphraseGeneratePart"; 20import { TrustBackendPart } from "@/pages/parts/auth/TrustBackendPart"; 21import { VerifyPassphrase } from "@/pages/parts/auth/VerifyPassphrasePart"; 22import { PageTitle } from "@/pages/parts/util/PageTitle"; 23import { conf } from "@/setup/config"; 24import { useAuthStore } from "@/stores/auth"; 25 26function CaptchaProvider(props: { 27 siteKey: string | null; 28 children: JSX.Element; 29}) { 30 if (!props.siteKey) return props.children; 31 return ( 32 <GoogleReCaptchaProvider reCaptchaKey={props.siteKey}> 33 {props.children} 34 </GoogleReCaptchaProvider> 35 ); 36} 37 38export function RegisterPage() { 39 const navigate = useNavigate(); 40 const { t } = useTranslation(); 41 const setBackendUrl = useAuthStore((s) => s.setBackendUrl); 42 const currentBackendUrl = useAuthStore((s) => s.backendUrl); 43 const config = conf(); 44 const availableBackends = 45 config.BACKEND_URLS.length > 0 46 ? config.BACKEND_URLS 47 : config.BACKEND_URL 48 ? [config.BACKEND_URL] 49 : []; 50 51 // If there's only one backend and user hasn't selected a custom one, auto-select it 52 const defaultBackend = 53 currentBackendUrl ?? 54 (availableBackends.length === 1 ? availableBackends[0] : null); 55 56 const [step, setStep] = useState( 57 availableBackends.length > 1 || !defaultBackend ? -1 : 0, 58 ); 59 const [mnemonic, setMnemonic] = useState<null | string>(null); 60 const [credentialId, setCredentialId] = useState<null | string>(null); 61 const [authMethod, setAuthMethod] = useState<"mnemonic" | "passkey">( 62 "mnemonic", 63 ); 64 const [account, setAccount] = useState<null | AccountProfile>(null); 65 const [siteKey, setSiteKey] = useState<string | null>(null); 66 const [selectedBackendUrl, setSelectedBackendUrl] = useState<string | null>( 67 currentBackendUrl ?? defaultBackend ?? null, 68 ); 69 70 const handleBackendSelect = (url: string | null) => { 71 setSelectedBackendUrl(url); 72 if (url) { 73 setBackendUrl(url); 74 } 75 }; 76 77 return ( 78 <CaptchaProvider siteKey={siteKey}> 79 <SubPageLayout> 80 <PageTitle subpage k="global.pages.register" /> 81 {step === -1 && (availableBackends.length > 1 || !defaultBackend) ? ( 82 <LargeCard> 83 <LargeCardText title={t("auth.backendSelection.title")}> 84 {t("auth.backendSelection.description")} 85 </LargeCardText> 86 <BackendSelector 87 selectedUrl={selectedBackendUrl} 88 onSelect={handleBackendSelect} 89 availableUrls={availableBackends} 90 showCustom 91 /> 92 <LargeCardButtons> 93 <span className="text-type-danger font-medium text-center"> 94 {t("settings.connections.server.notice")} 95 </span> 96 <Button 97 theme="purple" 98 onClick={() => { 99 if (selectedBackendUrl) { 100 setStep(0); 101 } 102 }} 103 disabled={!selectedBackendUrl} 104 > 105 {t("auth.register.information.next")} 106 </Button> 107 </LargeCardButtons> 108 </LargeCard> 109 ) : null} 110 {step === 0 ? ( 111 <TrustBackendPart 112 backendUrl={selectedBackendUrl} 113 onNext={(meta: MetaResponse) => { 114 setSiteKey( 115 meta.hasCaptcha && meta.captchaClientKey 116 ? meta.captchaClientKey 117 : null, 118 ); 119 setStep(1); 120 }} 121 /> 122 ) : null} 123 {step === 1 ? ( 124 <PassphraseGeneratePart 125 onNext={(m) => { 126 setMnemonic(m); 127 setAuthMethod("mnemonic"); 128 setStep(2); 129 }} 130 onPasskeyNext={(credId) => { 131 setCredentialId(credId); 132 setAuthMethod("passkey"); 133 setStep(2); 134 }} 135 /> 136 ) : null} 137 {step === 2 ? ( 138 <AccountCreatePart 139 onNext={(a) => { 140 setAccount(a); 141 setStep(3); 142 }} 143 /> 144 ) : null} 145 {step === 3 ? ( 146 <VerifyPassphrase 147 hasCaptcha={!!siteKey} 148 mnemonic={mnemonic} 149 credentialId={credentialId} 150 authMethod={authMethod} 151 userData={account} 152 backendUrl={selectedBackendUrl} 153 onNext={() => { 154 navigate("/"); 155 }} 156 /> 157 ) : null} 158 </SubPageLayout> 159 </CaptchaProvider> 160 ); 161}