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