this repo has no description
1<script lang="ts"> 2 import { loginWithOAuth, confirmSignup, resendVerification, getAuthState, switchAccount, forgetAccount } from '../lib/auth.svelte' 3 import { navigate } from '../lib/router.svelte' 4 let submitting = $state(false) 5 let pendingVerification = $state<{ did: string } | null>(null) 6 let verificationCode = $state('') 7 let resendingCode = $state(false) 8 let resendMessage = $state<string | null>(null) 9 let showNewLogin = $state(false) 10 const auth = getAuthState() 11 async function handleSwitchAccount(did: string) { 12 submitting = true 13 try { 14 await switchAccount(did) 15 navigate('/dashboard') 16 } catch { 17 submitting = false 18 } 19 } 20 function handleForgetAccount(did: string, e: Event) { 21 e.stopPropagation() 22 forgetAccount(did) 23 } 24 async function handleOAuthLogin() { 25 submitting = true 26 try { 27 await loginWithOAuth() 28 } catch { 29 submitting = false 30 } 31 } 32 async function handleVerification(e: Event) { 33 e.preventDefault() 34 if (!pendingVerification || !verificationCode.trim()) return 35 submitting = true 36 try { 37 await confirmSignup(pendingVerification.did, verificationCode.trim()) 38 navigate('/dashboard') 39 } catch { 40 submitting = false 41 } 42 } 43 async function handleResendCode() { 44 if (!pendingVerification || resendingCode) return 45 resendingCode = true 46 resendMessage = null 47 try { 48 await resendVerification(pendingVerification.did) 49 resendMessage = 'Verification code resent!' 50 } catch { 51 resendMessage = null 52 } finally { 53 resendingCode = false 54 } 55 } 56 function backToLogin() { 57 pendingVerification = null 58 verificationCode = '' 59 resendMessage = null 60 } 61</script> 62<div class="login-container"> 63 {#if auth.error} 64 <div class="error">{auth.error}</div> 65 {/if} 66 {#if pendingVerification} 67 <h1>Verify Your Account</h1> 68 <p class="subtitle"> 69 Your account needs verification. Enter the code sent to your verification method. 70 </p> 71 {#if resendMessage} 72 <div class="success">{resendMessage}</div> 73 {/if} 74 <form onsubmit={(e) => { e.preventDefault(); handleVerification(e); }}> 75 <div class="field"> 76 <label for="verification-code">Verification Code</label> 77 <input 78 id="verification-code" 79 type="text" 80 bind:value={verificationCode} 81 placeholder="Enter 6-digit code" 82 disabled={submitting} 83 required 84 maxlength="6" 85 pattern="[0-9]{6}" 86 autocomplete="one-time-code" 87 /> 88 </div> 89 <button type="submit" disabled={submitting || !verificationCode.trim()}> 90 {submitting ? 'Verifying...' : 'Verify Account'} 91 </button> 92 <button type="button" class="secondary" onclick={handleResendCode} disabled={resendingCode}> 93 {resendingCode ? 'Resending...' : 'Resend Code'} 94 </button> 95 <button type="button" class="tertiary" onclick={backToLogin}> 96 Back to Login 97 </button> 98 </form> 99 {:else if auth.savedAccounts.length > 0 && !showNewLogin} 100 <h1>Sign In</h1> 101 <p class="subtitle">Choose an account</p> 102 <div class="saved-accounts"> 103 {#each auth.savedAccounts as account} 104 <div 105 class="account-item" 106 class:disabled={submitting} 107 role="button" 108 tabindex="0" 109 onclick={() => !submitting && handleSwitchAccount(account.did)} 110 onkeydown={(e) => e.key === 'Enter' && !submitting && handleSwitchAccount(account.did)} 111 > 112 <div class="account-info"> 113 <span class="account-handle">@{account.handle}</span> 114 <span class="account-did">{account.did}</span> 115 </div> 116 <button 117 type="button" 118 class="forget-btn" 119 onclick={(e) => handleForgetAccount(account.did, e)} 120 title="Remove from saved accounts" 121 > 122 × 123 </button> 124 </div> 125 {/each} 126 </div> 127 <button type="button" class="secondary add-account" onclick={() => showNewLogin = true}> 128 Sign in to another account 129 </button> 130 <p class="register-link"> 131 Don't have an account? <a href="#/register">Create one</a> 132 </p> 133 {:else} 134 <h1>Sign In</h1> 135 <p class="subtitle">Sign in to manage your PDS account</p> 136 {#if auth.savedAccounts.length > 0} 137 <button type="button" class="tertiary back-btn" onclick={() => showNewLogin = false}> 138 ← Back to saved accounts 139 </button> 140 {/if} 141 <button type="button" class="oauth-btn" onclick={handleOAuthLogin} disabled={submitting || auth.loading}> 142 {submitting ? 'Redirecting...' : 'Sign In'} 143 </button> 144 <p class="forgot-link"> 145 <a href="#/reset-password">Forgot password?</a> &middot; <a href="#/request-passkey-recovery">Lost passkey?</a> 146 </p> 147 <p class="register-link"> 148 Don't have an account? <a href="#/register">Create one</a> 149 </p> 150 {/if} 151</div> 152<style> 153 .login-container { 154 max-width: 400px; 155 margin: 4rem auto; 156 padding: 2rem; 157 } 158 h1 { 159 margin: 0 0 0.5rem 0; 160 } 161 .subtitle { 162 color: var(--text-secondary); 163 margin: 0 0 2rem 0; 164 } 165 form { 166 display: flex; 167 flex-direction: column; 168 gap: 1rem; 169 } 170 .field { 171 display: flex; 172 flex-direction: column; 173 gap: 0.25rem; 174 } 175 label { 176 font-size: 0.875rem; 177 font-weight: 500; 178 } 179 input { 180 padding: 0.75rem; 181 border: 1px solid var(--border-color-light); 182 border-radius: 4px; 183 font-size: 1rem; 184 background: var(--bg-input); 185 color: var(--text-primary); 186 } 187 input:focus { 188 outline: none; 189 border-color: var(--accent); 190 } 191 button { 192 padding: 0.75rem; 193 background: var(--accent); 194 color: white; 195 border: none; 196 border-radius: 4px; 197 font-size: 1rem; 198 cursor: pointer; 199 margin-top: 0.5rem; 200 } 201 button:hover:not(:disabled) { 202 background: var(--accent-hover); 203 } 204 button:disabled { 205 opacity: 0.6; 206 cursor: not-allowed; 207 } 208 button.secondary { 209 background: transparent; 210 color: var(--accent); 211 border: 1px solid var(--accent); 212 } 213 button.secondary:hover:not(:disabled) { 214 background: var(--accent); 215 color: white; 216 } 217 button.tertiary { 218 background: transparent; 219 color: var(--text-secondary); 220 border: none; 221 } 222 button.tertiary:hover:not(:disabled) { 223 color: var(--text-primary); 224 } 225 .oauth-btn { 226 width: 100%; 227 padding: 1rem; 228 font-size: 1.125rem; 229 font-weight: 500; 230 } 231 .error { 232 padding: 0.75rem; 233 background: var(--error-bg); 234 border: 1px solid var(--error-border); 235 border-radius: 4px; 236 color: var(--error-text); 237 } 238 .success { 239 padding: 0.75rem; 240 background: var(--success-bg); 241 border: 1px solid var(--success-border); 242 border-radius: 4px; 243 color: var(--success-text); 244 } 245 .forgot-link { 246 text-align: center; 247 margin-top: 1rem; 248 margin-bottom: 0; 249 color: var(--text-secondary); 250 } 251 .forgot-link a { 252 color: var(--accent); 253 } 254 .register-link { 255 text-align: center; 256 margin-top: 0.5rem; 257 color: var(--text-secondary); 258 } 259 .register-link a { 260 color: var(--accent); 261 } 262 .saved-accounts { 263 display: flex; 264 flex-direction: column; 265 gap: 0.5rem; 266 margin-bottom: 1rem; 267 } 268 .account-item { 269 display: flex; 270 align-items: center; 271 justify-content: space-between; 272 padding: 1rem; 273 background: var(--bg-card); 274 border: 1px solid var(--border-color); 275 border-radius: 8px; 276 cursor: pointer; 277 text-align: left; 278 width: 100%; 279 transition: border-color 0.15s, box-shadow 0.15s; 280 } 281 .account-item:hover:not(.disabled) { 282 border-color: var(--accent); 283 box-shadow: 0 2px 8px rgba(77, 166, 255, 0.15); 284 } 285 .account-item.disabled { 286 opacity: 0.6; 287 cursor: not-allowed; 288 } 289 .account-info { 290 display: flex; 291 flex-direction: column; 292 gap: 0.25rem; 293 } 294 .account-handle { 295 font-weight: 500; 296 color: var(--text-primary); 297 } 298 .account-did { 299 font-size: 0.75rem; 300 color: var(--text-muted); 301 font-family: monospace; 302 overflow: hidden; 303 text-overflow: ellipsis; 304 max-width: 250px; 305 } 306 .forget-btn { 307 padding: 0.25rem 0.5rem; 308 background: transparent; 309 border: none; 310 color: var(--text-muted); 311 cursor: pointer; 312 font-size: 1.25rem; 313 line-height: 1; 314 border-radius: 4px; 315 margin: 0; 316 } 317 .forget-btn:hover { 318 background: var(--error-bg); 319 color: var(--error-text); 320 } 321 .add-account { 322 width: 100%; 323 margin-bottom: 1rem; 324 } 325 .back-btn { 326 margin-bottom: 1rem; 327 padding: 0; 328 } 329</style>