···
@state() passkeySupported = false;
@state() needsEmailVerification = false;
@state() verificationCode = "";
35
+
@state() resendCodeTimer = 0;
36
+
@state() resendingCode = false;
37
+
private resendInterval: number | null = null;
38
+
private codeSentAt: number | null = null; // Unix timestamp in seconds when code was sent
static override styles = css`
···
font-family: 'Monaco', 'Courier New', monospace;
291
+
text-align: center;
293
+
font-size: 0.875rem;
294
+
color: var(--text);
300
+
color: var(--primary);
302
+
text-decoration: underline;
303
+
font-size: 0.875rem;
305
+
font-family: inherit;
308
+
.resend-button:hover:not(:disabled) {
309
+
color: var(--accent);
312
+
.resend-button:disabled {
313
+
color: var(--secondary);
314
+
cursor: not-allowed;
315
+
text-decoration: none;
···
this.needsEmailVerification = true;
448
+
this.startResendTimer(data.verification_code_sent_at);
···
this.needsEmailVerification = true;
486
+
this.startResendTimer(data.verification_code_sent_at);
···
608
+
private startResendTimer(sentAtTimestamp: number) {
609
+
// Use provided timestamp
610
+
this.codeSentAt = sentAtTimestamp;
612
+
// Clear existing interval if any
613
+
if (this.resendInterval !== null) {
614
+
clearInterval(this.resendInterval);
617
+
// Update timer based on elapsed time
618
+
const updateTimer = () => {
619
+
if (this.codeSentAt === null) return;
621
+
const now = Math.floor(Date.now() / 1000);
622
+
const elapsed = now - this.codeSentAt;
623
+
const remaining = Math.max(0, (5 * 60) - elapsed);
624
+
this.resendCodeTimer = remaining;
626
+
if (remaining <= 0) {
627
+
if (this.resendInterval !== null) {
628
+
clearInterval(this.resendInterval);
629
+
this.resendInterval = null;
634
+
// Update immediately
637
+
// Then update every second
638
+
this.resendInterval = window.setInterval(updateTimer, 1000);
641
+
private async handleResendCode() {
643
+
this.resendingCode = true;
646
+
const response = await fetch("/api/auth/resend-verification-code", {
649
+
"Content-Type": "application/json",
651
+
body: JSON.stringify({
656
+
if (!response.ok) {
657
+
const data = await response.json();
658
+
this.error = data.error || "Failed to resend code";
662
+
// Start the 5-minute timer
663
+
this.startResendTimer(data.verification_code_sent_at);
665
+
this.error = error instanceof Error ? error.message : "An error occurred";
667
+
this.resendingCode = false;
671
+
private formatTimer(seconds: number): string {
672
+
const mins = Math.floor(seconds / 60);
673
+
const secs = seconds % 60;
674
+
return `${mins}:${secs.toString().padStart(2, '0')}`;
677
+
override disconnectedCallback() {
678
+
super.disconnectedCallback();
679
+
// Clean up timer when component is removed
680
+
if (this.resendInterval !== null) {
681
+
clearInterval(this.resendInterval);
682
+
this.resendInterval = null;
return html`<div class="loading">Loading...</div>`;
···
? html`<div class="error-message">${this.error}</div>`
775
+
<div class="resend-link">
777
+
this.resendCodeTimer > 0
778
+
? html`Resend code in ${this.formatTimer(this.resendCodeTimer)}`
782
+
class="resend-button"
783
+
@click=${this.handleResendCode}
784
+
?disabled=${this.resendingCode}
786
+
${this.resendingCode ? "Sending..." : "Resend code"}
<div class="modal-actions">