import type { WidgetConfig } from '../types'; import { isDemo } from './config'; /** * The demo's email gate. * * Vela is our own demo store, so whoever tries it is a merchant looking at * aisthetix and we are the controller of the address they leave. The first * try-on is free; from the second we ask, and the ask carries its purpose in * one line and an explicit unticked consent box. * * The way out is that declining costs nothing: "not now" closes the modal and * the visitor keeps the try-on they made, the gallery, and the whole store. The * ask comes back the next time they want another render, because that is the * condition, but it is never pushed at someone who did not ask for anything. * That is what makes the consent freely given rather than extracted. */ const LEAD_KEY = 'aisthetix-demo-lead'; /** Deliberately loose: this rejects typos, not people with unusual addresses. */ const EMAIL_SHAPE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export function isValidEmail( email: string ): boolean { return EMAIL_SHAPE.test( email.trim() ); } export function hasLead(): boolean { try { return !! localStorage.getItem( LEAD_KEY ); } catch { return false; } } export function rememberLead( email: string ): void { try { localStorage.setItem( LEAD_KEY, email.trim() ); } catch { // A browser that refuses storage will be asked again next visit. That is // annoying, not broken, and it is not worth blocking the try-on over. } } /** * Whether the next try-on should stop at the gate. * * False on a merchant store, false without somewhere to send the lead, false * for the first try-on, and false once the address has been given. Each of * those is a reason not to interrupt. */ export function shouldAskForEmail( config: WidgetConfig, completedTryOns: number ): boolean { if ( ! isDemo( config ) ) { return false; } if ( ! config.demoLeadUrl ) { return false; } if ( completedTryOns < 1 ) { return false; } return ! hasLead(); } export interface LeadPayload { email: string; /** Always true: the form cannot be submitted without the box ticked. */ consent: true; productId?: string; locale?: string; } /** * Posts the lead. Resolves false on anything that went wrong, so the caller can * say so on screen instead of pretending the address was saved. */ export async function submitLead( url: string, payload: LeadPayload ): Promise { try { const response = await fetch( url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( payload ), } ); return response.ok; } catch ( error ) { console.warn( '[leadStore] Could not save the lead:', error ); return false; } }