diff --git a/shared/components/rstack-identity.ts b/shared/components/rstack-identity.ts index c04ec68..4862a1d 100644 --- a/shared/components/rstack-identity.ts +++ b/shared/components/rstack-identity.ts @@ -369,6 +369,9 @@ export class RStackIdentity extends HTMLElement { // Validate session with server — detects logout from another browser session this.#validateSessionWithServer(); + // Nudge users without a second device to link one + this.#checkDeviceNudge(); + // Propagate login/logout across tabs via storage events window.addEventListener("storage", this.#onStorageChange); } @@ -396,6 +399,86 @@ export class RStackIdentity extends HTMLElement { } catch { /* network error — let token expire naturally */ } } + async #checkDeviceNudge() { + const session = getSession(); + if (!session?.accessToken) return; + + // Don't nag if dismissed within the last 7 days + const NUDGE_KEY = "eid_device_nudge_dismissed"; + const dismissed = localStorage.getItem(NUDGE_KEY); + if (dismissed && Date.now() - parseInt(dismissed, 10) < 7 * 24 * 60 * 60 * 1000) return; + + // Wait a moment so it doesn't compete with page load + await new Promise(r => setTimeout(r, 3000)); + + // Fetch account status + try { + const res = await fetch(`${ENCRYPTID_URL}/api/account/status`, { + headers: { Authorization: `Bearer ${getAccessToken()}` }, + }); + if (!res.ok) return; + const status = await res.json(); + if (status.multiDevice) return; // already has 2+ devices + + // Show a toast nudge + const toast = document.createElement("div"); + toast.className = "eid-device-nudge"; + toast.innerHTML = ` + +