Skip to content

Commit 9733c55

Browse files
committed
wip
1 parent 24eade4 commit 9733c55

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

packages/clerk-js/sandbox/SignIn.ts

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// Demonstration of the new reactive useSignIn hook API
2+
// This shows how the new API would work once implemented
3+
4+
interface UseSignInHookDemo {
5+
status: 'needs_first_factor' | 'needs_second_factor' | 'complete' | null;
6+
error: {
7+
global: string | null;
8+
fields: Record<string, string>;
9+
};
10+
emailCode: (params: { email: string }) => Promise<{ error?: any }>;
11+
oauth: (params: { provider: string }) => Promise<{ error?: any }>;
12+
verify: (params: { code: string }) => Promise<{ error?: any }>;
13+
}
14+
15+
// Mock implementation for demonstration purposes
16+
function createMockSignIn(): UseSignInHookDemo {
17+
let currentStatus: UseSignInHookDemo['status'] = 'needs_first_factor';
18+
let currentError: UseSignInHookDemo['error'] = { global: null, fields: {} };
19+
20+
return {
21+
get status() {
22+
return currentStatus;
23+
},
24+
get error() {
25+
return currentError;
26+
},
27+
28+
async emailCode({ email }) {
29+
console.log('📧 Starting email code flow for:', email);
30+
currentError = { global: null, fields: {} };
31+
32+
// Simulate API call
33+
await new Promise(resolve => setTimeout(resolve, 1000));
34+
35+
currentStatus = 'needs_second_factor';
36+
return { error: undefined };
37+
},
38+
39+
async verify({ code }) {
40+
console.log('🔐 Verifying code:', code);
41+
currentError = { global: null, fields: {} };
42+
43+
// Simulate API call
44+
await new Promise(resolve => setTimeout(resolve, 800));
45+
46+
if (code === '123456') {
47+
currentStatus = 'complete';
48+
return { error: undefined };
49+
} else {
50+
currentError = { global: null, fields: { code: 'Invalid verification code' } };
51+
return { error: new Error('Invalid code') };
52+
}
53+
},
54+
55+
async oauth({ provider }) {
56+
console.log('🔗 Starting OAuth flow with:', provider);
57+
currentError = { global: null, fields: {} };
58+
59+
// Simulate OAuth redirect
60+
alert(`Would redirect to ${provider} OAuth (demo mode)`);
61+
return { error: undefined };
62+
},
63+
};
64+
}
65+
66+
export function mountSignInDemo(element: HTMLDivElement) {
67+
// Clear the element
68+
element.innerHTML = '';
69+
70+
// Create the signIn instance (this would be the useSignIn() hook in real usage)
71+
const signIn = createMockSignIn();
72+
73+
// Create UI elements
74+
const container = document.createElement('div');
75+
container.className = 'max-w-md mx-auto p-6 bg-white rounded-lg shadow-md';
76+
77+
const title = document.createElement('h2');
78+
title.className = 'text-2xl font-bold mb-6 text-center';
79+
title.textContent = 'New useSignIn Hook Demo';
80+
81+
const description = document.createElement('div');
82+
description.className = 'mb-4 p-3 bg-blue-50 rounded text-sm';
83+
description.innerHTML = `
84+
<strong>New API Benefits:</strong><br>
85+
• No isLoaded checks needed<br>
86+
• No setActive invocation needed<br>
87+
• Strategy-specific methods (emailCode, oauth, verify)<br>
88+
• Better error interface<br>
89+
• Reactive state updates
90+
`;
91+
92+
// Status display
93+
const statusDisplay = document.createElement('div');
94+
statusDisplay.className = 'mb-4 p-3 bg-gray-50 rounded';
95+
96+
// Error display
97+
const errorDisplay = document.createElement('div');
98+
errorDisplay.className = 'mb-4';
99+
100+
// Email input form
101+
const emailForm = document.createElement('form');
102+
emailForm.className = 'mb-4';
103+
104+
const emailInput = document.createElement('input');
105+
emailInput.type = 'email';
106+
emailInput.placeholder = 'Enter your email';
107+
emailInput.value = '[email protected]';
108+
emailInput.className = 'w-full p-2 border rounded mb-2';
109+
110+
const emailButton = document.createElement('button');
111+
emailButton.type = 'submit';
112+
emailButton.textContent = 'Sign in with Email Code';
113+
emailButton.className = 'w-full p-2 bg-blue-500 text-white rounded hover:bg-blue-600';
114+
115+
emailForm.appendChild(emailInput);
116+
emailForm.appendChild(emailButton);
117+
118+
// OAuth button
119+
const oauthButton = document.createElement('button');
120+
oauthButton.textContent = 'Sign in with Google';
121+
oauthButton.className = 'w-full p-2 bg-red-500 text-white rounded hover:bg-red-600 mb-4';
122+
123+
// Code verification form (initially hidden)
124+
const codeForm = document.createElement('form');
125+
codeForm.className = 'mb-4 hidden';
126+
codeForm.id = 'codeForm';
127+
128+
const codeInput = document.createElement('input');
129+
codeInput.type = 'text';
130+
codeInput.placeholder = 'Enter verification code (try: 123456)';
131+
codeInput.className = 'w-full p-2 border rounded mb-2';
132+
133+
const codeButton = document.createElement('button');
134+
codeButton.type = 'submit';
135+
codeButton.textContent = 'Verify Code';
136+
codeButton.className = 'w-full p-2 bg-green-500 text-white rounded hover:bg-green-600';
137+
138+
codeForm.appendChild(codeInput);
139+
codeForm.appendChild(codeButton);
140+
141+
// Success message
142+
const successMessage = document.createElement('div');
143+
successMessage.className = 'hidden p-4 bg-green-100 text-green-800 rounded';
144+
successMessage.textContent = '✅ Successfully signed in!';
145+
146+
// Update UI function
147+
function updateUI() {
148+
// Update status
149+
statusDisplay.innerHTML = `
150+
<strong>Status:</strong>
151+
<span class="px-2 py-1 rounded text-sm ${
152+
signIn.status === 'complete'
153+
? 'bg-green-100 text-green-700'
154+
: signIn.status === 'needs_second_factor'
155+
? 'bg-yellow-100 text-yellow-700'
156+
: 'bg-gray-100 text-gray-700'
157+
}">${signIn.status || 'Ready'}</span>
158+
`;
159+
160+
// Update errors
161+
if (signIn.error.global) {
162+
errorDisplay.innerHTML = `<div class="p-2 bg-red-100 text-red-700 rounded">${signIn.error.global}</div>`;
163+
} else if (signIn.error.fields.code) {
164+
errorDisplay.innerHTML = `<div class="p-2 bg-red-100 text-red-700 rounded">Code error: ${signIn.error.fields.code}</div>`;
165+
} else {
166+
errorDisplay.innerHTML = '';
167+
}
168+
169+
// Show/hide forms based on status
170+
if (signIn.status === 'needs_second_factor') {
171+
emailForm.style.display = 'none';
172+
oauthButton.style.display = 'none';
173+
codeForm.classList.remove('hidden');
174+
successMessage.classList.add('hidden');
175+
} else if (signIn.status === 'complete') {
176+
emailForm.style.display = 'none';
177+
oauthButton.style.display = 'none';
178+
codeForm.classList.add('hidden');
179+
successMessage.classList.remove('hidden');
180+
} else {
181+
emailForm.style.display = 'block';
182+
oauthButton.style.display = 'block';
183+
codeForm.classList.add('hidden');
184+
successMessage.classList.add('hidden');
185+
}
186+
}
187+
188+
// Event handlers
189+
emailForm.addEventListener('submit', async e => {
190+
e.preventDefault();
191+
emailButton.disabled = true;
192+
emailButton.textContent = 'Sending code...';
193+
194+
await signIn.emailCode({ email: emailInput.value });
195+
196+
emailButton.disabled = false;
197+
emailButton.textContent = 'Sign in with Email Code';
198+
updateUI();
199+
});
200+
201+
oauthButton.addEventListener('click', async () => {
202+
await signIn.oauth({ provider: 'google' });
203+
updateUI();
204+
});
205+
206+
codeForm.addEventListener('submit', async e => {
207+
e.preventDefault();
208+
codeButton.disabled = true;
209+
codeButton.textContent = 'Verifying...';
210+
211+
await signIn.verify({ code: codeInput.value });
212+
213+
codeButton.disabled = false;
214+
codeButton.textContent = 'Verify Code';
215+
updateUI();
216+
});
217+
218+
// Assemble the UI
219+
container.appendChild(title);
220+
container.appendChild(description);
221+
container.appendChild(statusDisplay);
222+
container.appendChild(errorDisplay);
223+
container.appendChild(emailForm);
224+
container.appendChild(oauthButton);
225+
container.appendChild(codeForm);
226+
container.appendChild(successMessage);
227+
228+
element.appendChild(container);
229+
230+
// Initial UI update
231+
updateUI();
232+
}

packages/clerk-js/sandbox/app.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { SignInResource } from '@clerk/types';
22

33
import * as l from '../../localizations';
44
import type { Clerk as ClerkType } from '../';
5+
import { mountSignInDemo } from './SignIn';
56

67
const AVAILABLE_LOCALES = Object.keys(l) as (keyof typeof l)[];
78

@@ -485,6 +486,19 @@ function mountSignInObservable(element: HTMLDivElement) {
485486
}
486487

487488
void (async () => {
489+
// Handle demo route first (doesn't need Clerk to be loaded)
490+
const currentPath = window.location.pathname;
491+
console.log('Current path:', currentPath);
492+
if (currentPath === '/sign-in-demo') {
493+
console.log('Mounting sign in demo...');
494+
addCurrentRouteIndicator(currentPath);
495+
mountSignInDemo(app);
496+
console.log('Demo mounted, returning early');
497+
return;
498+
}
499+
500+
console.log('Not demo route, continuing with Clerk initialization...');
501+
488502
assertClerkIsLoaded(Clerk);
489503
fillLocalizationSelect();
490504
const { updateVariables } = appearanceVariableOptions();

packages/clerk-js/sandbox/template.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,13 @@
195195
>Sign In Observable</a
196196
>
197197
</li>
198+
<li class="relative">
199+
<a
200+
class="relative isolate flex w-full rounded-md border border-white px-2 py-[0.4375rem] text-sm hover:bg-gray-50 aria-[current]:bg-gray-50"
201+
href="/sign-in-demo"
202+
>New useSignIn Hook Demo</a
203+
>
204+
</li>
198205
<li class="relative">
199206
<a
200207
class="relative isolate flex w-full rounded-md border border-white px-2 py-[0.4375rem] text-sm hover:bg-gray-50 aria-[current]:bg-gray-50"

0 commit comments

Comments
 (0)