diff --git a/modules/rinbox/components/folk-inbox-client.ts b/modules/rinbox/components/folk-inbox-client.ts index d3c2f0e..a08800a 100644 --- a/modules/rinbox/components/folk-inbox-client.ts +++ b/modules/rinbox/components/folk-inbox-client.ts @@ -39,7 +39,7 @@ class FolkInboxClient extends HTMLElement { private _fwdStatus: 'loading' | 'unavailable' | 'no-email' | 'ready' | 'enabled' | 'error' = 'loading'; private _fwdAddress = ''; private _fwdTarget = ''; - private _fwdDismissed = false; + private _fwdModalOpen = false; private _fwdBusy = false; private _fwdError = ''; private demoApprovals: any[] = [ @@ -295,11 +295,6 @@ class FolkInboxClient extends HTMLElement { } private async _checkForwardStatus() { - if (sessionStorage.getItem('rinbox_fwd_dismissed')) { - this._fwdDismissed = true; - this._fwdStatus = 'unavailable'; - return; - } const token = getAccessToken(); if (!token) { this._fwdStatus = 'unavailable'; return; } try { @@ -311,12 +306,12 @@ class FolkInboxClient extends HTMLElement { if (data.enabled) { this._fwdStatus = 'enabled'; this._fwdAddress = data.address || ''; - this._fwdTarget = data.target || ''; - } else if (data.available && data.target) { + this._fwdTarget = data.forwardsTo || ''; + } else if (data.available && data.forwardsTo) { this._fwdStatus = 'ready'; this._fwdAddress = data.address || ''; - this._fwdTarget = data.target || ''; - } else if (!data.target) { + this._fwdTarget = data.forwardsTo || ''; + } else if (!data.forwardsTo) { this._fwdStatus = 'no-email'; this._fwdAddress = data.address || ''; } else { @@ -372,77 +367,151 @@ class FolkInboxClient extends HTMLElement { this.render(); } - private _dismissForwardBanner() { - this._fwdDismissed = true; - sessionStorage.setItem('rinbox_fwd_dismissed', '1'); + private async _saveEmailAndEnable() { + if (this._fwdBusy) return; + const input = this.shadow.getElementById('fwd-email-input') as HTMLInputElement | null; + const email = input?.value.trim(); + if (!email) return; + this._fwdBusy = true; + this._fwdError = ''; + this.render(); + const token = getAccessToken(); + try { + // Save email to profile + const profileResp = await fetch('https://auth.rspace.online/api/user/profile', { + method: 'PUT', + headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ profileEmail: email }), + }); + if (!profileResp.ok) { + const err = await profileResp.json().catch(() => ({})); + this._fwdError = (err as any).error || 'Failed to save email'; + this._fwdStatus = 'error'; + this._fwdBusy = false; + this.render(); + return; + } + // Enable forwarding + const fwdResp = await fetch('https://auth.rspace.online/api/account/email-forward/enable', { + method: 'POST', + headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, + }); + if (fwdResp.ok) { + this._fwdStatus = 'enabled'; + this._fwdTarget = email; + } else { + const err = await fwdResp.json().catch(() => ({})); + this._fwdError = (err as any).error || 'Failed to enable forwarding'; + this._fwdStatus = 'error'; + } + } catch { + this._fwdError = 'Network error — try again'; + this._fwdStatus = 'error'; + } + this._fwdBusy = false; this.render(); } - private renderForwardBanner(): string { - if (this._fwdDismissed || this._fwdStatus === 'loading' || this._fwdStatus === 'unavailable') return ''; - - if (this._fwdStatus === 'ready') { - return ` -
-
-
Forward rInbox to your email
-
- ${this.escapeHtml(this._fwdAddress)} - - ${this.escapeHtml(this._fwdTarget)} -
-
-
- - -
-
`; - } + private renderForwardTrigger(): string { + if (this._fwdStatus === 'loading' || this._fwdStatus === 'unavailable') return ''; if (this._fwdStatus === 'enabled') { return ` -
-
-
Forwarding active
-
+
+ + Forwarding active: ${this.escapeHtml(this._fwdAddress)}${this.escapeHtml(this._fwdTarget)} + Manage +
`; + } + + return ` +
+ 📩 + Forward rInbox to your email → +
`; + } + + private renderForwardModal(): string { + if (!this._fwdModalOpen) return ''; + + let content = ''; + + if (this._fwdStatus === 'ready') { + content = ` +
+
+
${this.escapeHtml(this._fwdAddress)} ${this.escapeHtml(this._fwdTarget)}
-
- +

Mail sent to your rInbox address will be forwarded to your personal email.

+
+ 🔒 Your email is stored locally with your EncryptID keys — sovereign and encrypted on your device.
+
+
+ + +
`; + } else if (this._fwdStatus === 'enabled') { + content = ` +
+
+ + Forwarding Active +
+
+
+ ${this.escapeHtml(this._fwdAddress)} + + ${this.escapeHtml(this._fwdTarget)} +
+
+
+ 🔒 Your email is stored locally with your EncryptID keys — sovereign and encrypted on your device. +
+
+
+ + +
`; + } else if (this._fwdStatus === 'no-email') { + content = ` +
+

Connect an email address to receive forwarded mail from your rInbox.

+
+ 🔒 Your email is stored locally with your EncryptID keys — never on a central server. Only you control your contact info. +
+
+ + +
+
+
+ + +
`; + } else if (this._fwdStatus === 'error') { + content = ` +
+
${this.escapeHtml(this._fwdError)}
+
+
+ +
`; } - if (this._fwdStatus === 'no-email') { - return ` -
-
-
Forward rInbox to your email
-
Add a verified email to your profile to enable forwarding.
-
-
- Go to Profile - -
-
`; - } - - if (this._fwdStatus === 'error') { - return ` -
-
-
${this.escapeHtml(this._fwdError)}
-
-
- -
-
`; - } - - return ''; + return ` +
+
+ +
📩 Email Forwarding
+ ${content} +
+
`; } private timeAgo(dateStr: string): string { @@ -663,31 +732,40 @@ class FolkInboxClient extends HTMLElement { .sample-banner { padding: 8px 16px; background: rgba(99,102,241,0.12); border: 1px solid rgba(99,102,241,0.25); border-radius: 8px; color: #a5b4fc; font-size: 13px; text-align: center; margin-bottom: 12px; } - /* Forwarding banner */ - .fwd-banner { display: flex; align-items: center; gap: 1rem; padding: 0.75rem 1rem; border-radius: 10px; margin-bottom: 12px; border: 1px solid; } - .fwd-banner.fwd-ready { background: rgba(99,102,241,0.08); border-color: rgba(99,102,241,0.25); } - .fwd-banner.fwd-enabled { background: rgba(34,197,94,0.08); border-color: rgba(34,197,94,0.25); } - .fwd-banner.fwd-no-email { background: rgba(251,191,36,0.08); border-color: rgba(251,191,36,0.25); } - .fwd-banner.fwd-error { background: rgba(239,68,68,0.08); border-color: rgba(239,68,68,0.25); } - .fwd-banner-content { flex: 1; min-width: 0; } - .fwd-banner-title { font-size: 0.85rem; font-weight: 600; color: var(--rs-text-primary); margin-bottom: 2px; } - .fwd-banner-desc { font-size: 0.8rem; color: var(--rs-text-secondary); } - .fwd-banner-route { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; } - .fwd-banner-route code { font-size: 0.75rem; color: #818cf8; background: rgba(99,102,241,0.1); padding: 2px 6px; border-radius: 4px; } - .fwd-enabled .fwd-banner-route code { color: #4ade80; background: rgba(34,197,94,0.1); } - .fwd-arrow { color: var(--rs-text-muted); font-size: 0.8rem; } - .fwd-banner-actions { display: flex; gap: 0.5rem; align-items: center; flex-shrink: 0; } - .fwd-btn-enable { padding: 6px 14px; border-radius: 6px; border: none; background: linear-gradient(135deg, #6366f1, #0891b2); color: white; cursor: pointer; font-size: 0.8rem; font-weight: 600; transition: opacity 0.15s; } + /* Forwarding trigger */ + .fwd-trigger { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; margin-bottom: 12px; cursor: pointer; border-radius: 8px; font-size: 0.8rem; color: var(--rs-text-secondary); transition: background 0.15s; } + .fwd-trigger:hover { background: var(--rs-bg-hover); } + .fwd-trigger-enabled { color: #4ade80; } + .fwd-trigger-enabled code { font-size: 0.75rem; color: #4ade80; background: rgba(34,197,94,0.1); padding: 2px 6px; border-radius: 4px; } + .fwd-trigger-icon { font-size: 1rem; } + .fwd-trigger-link { color: #818cf8; font-weight: 500; } + .fwd-trigger-enabled .fwd-trigger-link { color: rgba(74,222,128,0.7); font-weight: 400; margin-left: auto; } + .fwd-trigger-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; } + .fwd-dot-active { background: #4ade80; box-shadow: 0 0 6px rgba(74,222,128,0.5); } + + /* Forwarding modal overlay */ + .fwd-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 10000; display: flex; align-items: center; justify-content: center; padding: 1rem; } + .fwd-modal { background: var(--rs-bg-surface-sunken); border: 1px solid var(--rs-border-strong); border-radius: 16px; max-width: 480px; width: 100%; padding: 2rem; position: relative; } + .fwd-modal-title { font-size: 1.25rem; font-weight: 700; margin-bottom: 1.25rem; color: var(--rs-text-primary); } + .fwd-modal-status { margin-bottom: 1.25rem; } + .fwd-modal-status-badge { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.75rem; border-radius: 6px; font-size: 0.85rem; font-weight: 600; margin-bottom: 0.75rem; } + .fwd-status-active { background: rgba(34,197,94,0.1); color: #4ade80; } + .fwd-modal-route { margin: 0.75rem 0; } + .fwd-modal-route-line { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; } + .fwd-modal-route-line code { font-size: 0.8rem; color: #818cf8; background: rgba(99,102,241,0.1); padding: 3px 8px; border-radius: 4px; } + .fwd-arrow { color: var(--rs-text-muted); font-size: 0.85rem; } + .fwd-modal-desc { font-size: 0.85rem; color: var(--rs-text-secondary); line-height: 1.5; margin: 0.5rem 0; } + .fwd-sovereignty { font-size: 0.8rem; color: var(--rs-text-muted); line-height: 1.5; padding: 0.75rem; background: rgba(99,102,241,0.06); border: 1px solid rgba(99,102,241,0.15); border-radius: 8px; margin-top: 0.75rem; } + .fwd-modal-error { font-size: 0.85rem; color: #f87171; padding: 0.75rem; background: rgba(239,68,68,0.08); border: 1px solid rgba(239,68,68,0.2); border-radius: 8px; } + .fwd-modal-actions { display: flex; gap: 0.5rem; justify-content: flex-end; } + .fwd-btn-enable { padding: 8px 18px; border-radius: 8px; border: none; background: linear-gradient(135deg, #6366f1, #0891b2); color: white; cursor: pointer; font-size: 0.85rem; font-weight: 600; transition: opacity 0.15s; } .fwd-btn-enable:hover { opacity: 0.9; } .fwd-btn-enable:disabled { opacity: 0.5; cursor: not-allowed; } - .fwd-btn-disable { padding: 6px 12px; border-radius: 6px; border: 1px solid rgba(34,197,94,0.3); background: transparent; color: #4ade80; cursor: pointer; font-size: 0.75rem; transition: all 0.15s; } - .fwd-btn-disable:hover { border-color: rgba(34,197,94,0.6); background: rgba(34,197,94,0.1); } + .fwd-btn-disable { padding: 8px 18px; border-radius: 8px; border: 1px solid rgba(239,68,68,0.3); background: transparent; color: #f87171; cursor: pointer; font-size: 0.85rem; font-weight: 500; transition: all 0.15s; } + .fwd-btn-disable:hover { border-color: rgba(239,68,68,0.5); background: rgba(239,68,68,0.08); } .fwd-btn-disable:disabled { opacity: 0.5; cursor: not-allowed; } - .fwd-btn-dismiss { padding: 6px 10px; border-radius: 6px; border: none; background: transparent; color: var(--rs-text-muted); cursor: pointer; font-size: 0.75rem; transition: color 0.15s; } - .fwd-btn-dismiss:hover { color: var(--rs-text-secondary); } - .fwd-btn-profile { padding: 6px 14px; border-radius: 6px; border: 1px solid rgba(251,191,36,0.3); background: rgba(251,191,36,0.1); color: #fbbf24; text-decoration: none; font-size: 0.8rem; font-weight: 500; transition: all 0.15s; } - .fwd-btn-profile:hover { border-color: rgba(251,191,36,0.5); background: rgba(251,191,36,0.15); } - .fwd-error .fwd-banner-title { color: #f87171; } + .fwd-btn-cancel { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--rs-border-strong); background: transparent; color: var(--rs-text-secondary); cursor: pointer; font-size: 0.85rem; transition: all 0.15s; } + .fwd-btn-cancel:hover { border-color: var(--rs-border-strong); color: var(--rs-text-primary); } @media (max-width: 768px) { .mailbox-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); } @@ -695,8 +773,7 @@ class FolkInboxClient extends HTMLElement { .thread-row { flex-wrap: wrap; gap: 4px; } } @media (max-width: 480px) { - .fwd-banner { flex-direction: column; align-items: stretch; gap: 0.5rem; } - .fwd-banner-actions { justify-content: flex-end; } + .fwd-modal { padding: 1.25rem; } .rapp-nav { gap: 0.25rem; } .nav-btn { padding: 4px 8px; font-size: 12px; } .inbox-header { padding: 0.625rem 0.75rem; gap: 0.5rem; } @@ -715,9 +792,10 @@ class FolkInboxClient extends HTMLElement {
${this.renderNav()} ${this.showingSampleData ? '
Showing sample data — create a mailbox to get started
' : ''} - ${this.view === 'mailboxes' ? this.renderForwardBanner() : ''} + ${this.view === 'mailboxes' ? this.renderForwardTrigger() : ''} ${this.renderView()} ${this.helpOpen ? this.renderHelp() : ""} + ${this.renderForwardModal()}
`; this.bindEvents(); @@ -1337,10 +1415,24 @@ class FolkInboxClient extends HTMLElement { helpClose.addEventListener("click", () => { this.helpOpen = false; this.render(); }); } - // Forwarding banner actions + // Forwarding trigger + modal actions + this.shadow.querySelector("[data-action='fwd-open']")?.addEventListener("click", () => { + this._fwdModalOpen = true; this.render(); + }); this.shadow.querySelector("[data-action='fwd-enable']")?.addEventListener("click", () => this._enableForwarding()); this.shadow.querySelector("[data-action='fwd-disable']")?.addEventListener("click", () => this._disableForwarding()); - this.shadow.querySelector("[data-action='fwd-dismiss']")?.addEventListener("click", () => this._dismissForwardBanner()); + this.shadow.querySelector("[data-action='fwd-save-enable']")?.addEventListener("click", () => this._saveEmailAndEnable()); + this.shadow.querySelectorAll("[data-action='fwd-close']").forEach(btn => { + btn.addEventListener("click", () => { this._fwdModalOpen = false; this.render(); }); + }); + const fwdOverlay = this.shadow.querySelector("[data-action='fwd-close-overlay']"); + if (fwdOverlay) { + fwdOverlay.addEventListener("click", (e) => { + if ((e.target as HTMLElement).dataset.action === "fwd-close-overlay") { + this._fwdModalOpen = false; this.render(); + } + }); + } // Mailbox click this.shadow.querySelectorAll("[data-mailbox]").forEach((card) => {