Skip to content

Commit d180543

Browse files
committed
Polish ClawWatch preregistration flow
1 parent f5cf91e commit d180543

File tree

3 files changed

+137
-6
lines changed

3 files changed

+137
-6
lines changed

docs/app.js

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,24 @@ const signOutButton = document.getElementById('google-signout-button');
66
const statusText = document.getElementById('preregister-status');
77
const resultBanner = document.getElementById('preregister-result');
88
const preregisterSection = document.getElementById('preregister');
9+
const preregisterModal = document.getElementById('preregister-modal');
10+
const preregisterModalClose = document.getElementById('preregister-modal-close');
911

1012
let supabaseClient = null;
13+
let hasShownThankYou = false;
14+
15+
function showThankYouModal() {
16+
if (!preregisterModal || hasShownThankYou) return;
17+
hasShownThankYou = true;
18+
preregisterModal.hidden = false;
19+
document.body.style.overflow = 'hidden';
20+
}
21+
22+
function closeThankYouModal() {
23+
if (!preregisterModal) return;
24+
preregisterModal.hidden = true;
25+
document.body.style.overflow = '';
26+
}
1127

1228
function track(eventName, params = {}) {
1329
if (typeof window.gtag === 'function') {
@@ -69,13 +85,16 @@ function renderRegistered(user) {
6985
if (signOutButton) {
7086
signOutButton.hidden = false;
7187
}
72-
setBanner('Interest saved. We will contact you when the install-ready release opens.', 'success');
88+
setBanner("Thank you for your interest! We'll be back when the easy-to-install ClawWatch is here.", 'success');
7389
}
7490

75-
async function ensureInterest(user) {
91+
async function ensureInterest(user, { showModal = false } = {}) {
7692
const metadata = user.user_metadata || {};
7793
if (metadata.clawwatch_interest_at) {
7894
renderRegistered(user);
95+
if (showModal) {
96+
showThankYouModal();
97+
}
7998
return;
8099
}
81100

@@ -104,6 +123,9 @@ async function ensureInterest(user) {
104123

105124
track('clawwatch_preregister_complete');
106125
renderRegistered(data.user || user);
126+
if (showModal) {
127+
showThankYouModal();
128+
}
107129
if (window.location.hash === '#preregister') {
108130
preregisterSection?.scrollIntoView({ behavior: 'smooth', block: 'start' });
109131
}
@@ -114,10 +136,11 @@ async function startGoogleSignIn() {
114136
renderBusy('Redirecting to Google sign-in…');
115137
track('clawwatch_preregister_start');
116138

117-
const redirectTo = `${window.location.origin}${window.location.pathname}#preregister`;
118-
const { error } = await supabaseClient.auth.signInWithOAuth({
139+
const redirectTo = `${window.location.origin}${window.location.pathname}?preregister=complete#preregister`;
140+
const { data, error } = await supabaseClient.auth.signInWithOAuth({
119141
provider: 'google',
120142
options: {
143+
skipBrowserRedirect: true,
121144
redirectTo,
122145
queryParams: {
123146
prompt: 'select_account',
@@ -129,7 +152,34 @@ async function startGoogleSignIn() {
129152
if (error) {
130153
renderSignedOut();
131154
setBanner(error.message, 'error');
155+
return;
156+
}
157+
158+
const authUrl = data?.url;
159+
if (!authUrl) {
160+
renderSignedOut();
161+
setBanner('Google sign-in could not be started.', 'error');
162+
return;
163+
}
164+
165+
const popup = window.open(authUrl, 'clawwatch-preregister', 'popup=yes,width=520,height=720');
166+
if (!popup) {
167+
renderSignedOut();
168+
setBanner('Please allow popups for ClawWatch sign-in.', 'error');
169+
return;
132170
}
171+
172+
const poll = window.setInterval(async () => {
173+
if (!popup || popup.closed) {
174+
window.clearInterval(poll);
175+
const { data: sessionData } = await supabaseClient.auth.getSession();
176+
if (sessionData.session?.user) {
177+
await ensureInterest(sessionData.session.user, { showModal: true });
178+
} else {
179+
renderSignedOut();
180+
}
181+
}
182+
}, 500);
133183
}
134184

135185
async function signOut() {
@@ -154,22 +204,51 @@ async function init() {
154204

155205
preregisterButton?.addEventListener('click', startGoogleSignIn);
156206
signOutButton?.addEventListener('click', signOut);
207+
preregisterModalClose?.addEventListener('click', closeThankYouModal);
208+
preregisterModal?.addEventListener('click', (event) => {
209+
if (event.target?.dataset?.closeModal === 'true') {
210+
closeThankYouModal();
211+
}
212+
});
213+
214+
window.addEventListener('message', async (event) => {
215+
if (event.origin !== window.location.origin) return;
216+
if (event.data?.type !== 'clawwatch-preregister-complete') return;
217+
const { data: sessionData } = await supabaseClient.auth.getSession();
218+
if (sessionData.session?.user) {
219+
await ensureInterest(sessionData.session.user, { showModal: true });
220+
}
221+
});
157222

158223
const { data, error } = await supabaseClient.auth.getSession();
159224
if (error) {
160225
setBanner(error.message, 'error');
161226
return;
162227
}
163228

229+
const preregComplete = new URLSearchParams(window.location.search).get('preregister') === 'complete';
230+
231+
if (preregComplete && window.opener) {
232+
window.opener.postMessage({ type: 'clawwatch-preregister-complete' }, window.location.origin);
233+
}
234+
164235
if (data.session?.user) {
165-
await ensureInterest(data.session.user);
236+
await ensureInterest(data.session.user, { showModal: preregComplete });
166237
} else {
167238
renderSignedOut();
168239
}
169240

241+
if (preregComplete) {
242+
const cleanUrl = `${window.location.origin}${window.location.pathname}${window.location.hash || ''}`;
243+
window.history.replaceState({}, document.title, cleanUrl);
244+
if (window.opener) {
245+
window.setTimeout(() => window.close(), 120);
246+
}
247+
}
248+
170249
supabaseClient.auth.onAuthStateChange(async (event, session) => {
171250
if (event === 'SIGNED_IN' && session?.user) {
172-
await ensureInterest(session.user);
251+
await ensureInterest(session.user, { showModal: true });
173252
return;
174253
}
175254

docs/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ <h2>ClawWatch.app is a ThinkOff.io service</h2>
289289
<p>© 2026 ThinkOff Ecosystem</p>
290290
</footer>
291291
</div>
292+
<div id="preregister-modal" class="modal-shell" hidden>
293+
<div class="modal-backdrop" data-close-modal="true"></div>
294+
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="preregister-modal-title">
295+
<h2 id="preregister-modal-title">Thank you for your interest!</h2>
296+
<p>We'll be back when the easy-to-install ClawWatch is here.</p>
297+
<button id="preregister-modal-close" class="cta-primary modal-close-button" type="button">Close</button>
298+
</div>
299+
</div>
292300
<script type="module" src="/app.js"></script>
293301
</body>
294302
</html>

docs/styles.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,50 @@ body.clawwatch-body {
594594
font-size: 0.95rem;
595595
}
596596

597+
.modal-shell {
598+
position: fixed;
599+
inset: 0;
600+
z-index: 40;
601+
display: grid;
602+
place-items: center;
603+
}
604+
605+
.modal-backdrop {
606+
position: absolute;
607+
inset: 0;
608+
background: rgba(4, 7, 15, 0.72);
609+
backdrop-filter: blur(6px);
610+
}
611+
612+
.modal-card {
613+
position: relative;
614+
z-index: 1;
615+
width: min(92vw, 460px);
616+
padding: 28px;
617+
border-radius: 24px;
618+
background: linear-gradient(180deg, rgba(14, 18, 30, 0.98), rgba(10, 12, 22, 0.98));
619+
border: 1px solid rgba(255,255,255,0.12);
620+
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.42);
621+
text-align: center;
622+
}
623+
624+
.modal-card h2 {
625+
margin: 0 0 10px;
626+
font-size: 1.8rem;
627+
letter-spacing: -0.03em;
628+
}
629+
630+
.modal-card p {
631+
margin: 0;
632+
color: var(--muted);
633+
line-height: 1.7;
634+
}
635+
636+
.modal-close-button {
637+
margin-top: 20px;
638+
width: 100%;
639+
}
640+
597641
@keyframes heroBreathe {
598642
0%, 100% { transform: translateY(0) scale(1); }
599643
50% { transform: translateY(-4px) scale(1.024); }

0 commit comments

Comments
 (0)