Add Sign Up functionality to desktop app (#5244)

# Description of Changes
Adds Sign Up with email to desktop app. SSO sign up will come in a
future PR.
This commit is contained in:
James Brunton
2025-12-16 14:55:53 +00:00
committed by GitHub
parent d80e627899
commit 340006ceea
10 changed files with 397 additions and 4 deletions
@@ -2,16 +2,20 @@ import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DesktopAuthLayout } from '@app/components/SetupWizard/DesktopAuthLayout';
import { SaaSLoginScreen } from '@app/components/SetupWizard/SaaSLoginScreen';
import { SaaSSignupScreen } from '@app/components/SetupWizard/SaaSSignupScreen';
import { ServerSelectionScreen } from '@app/components/SetupWizard/ServerSelectionScreen';
import { SelfHostedLoginScreen } from '@app/components/SetupWizard/SelfHostedLoginScreen';
import { ServerConfig, connectionModeService } from '@app/services/connectionModeService';
import { authService, UserInfo } from '@app/services/authService';
import { tauriBackendService } from '@app/services/tauriBackendService';
import { STIRLING_SAAS_URL } from '@desktop/constants/connection';
import { listen } from '@tauri-apps/api/event';
import { useEffect } from 'react';
import '@app/routes/authShared/auth.css';
enum SetupStep {
SaaSLogin,
SaaSSignup,
ServerSelection,
SelfHostedLogin,
}
@@ -80,6 +84,16 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
setActiveStep(SetupStep.ServerSelection);
};
const handleSwitchToSignup = () => {
setError(null);
setActiveStep(SetupStep.SaaSSignup);
};
const handleSwitchToLogin = () => {
setError(null);
setActiveStep(SetupStep.SaaSLogin);
};
const handleServerSelection = (config: ServerConfig) => {
setServerConfig(config);
setError(null);
@@ -128,6 +142,48 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
}
};
useEffect(() => {
const unsubscribePromise = listen<string>('deep-link', async (event) => {
const url = event.payload;
if (!url) return;
try {
const parsed = new URL(url);
// Supabase sends tokens in the URL hash
const hash = parsed.hash.replace(/^#/, '');
const params = new URLSearchParams(hash);
const accessToken = params.get('access_token');
const type = params.get('type') || parsed.searchParams.get('type');
if (!type || (type !== 'signup' && type !== 'recovery' && type !== 'magiclink')) {
return;
}
if (!accessToken) {
console.error('[SetupWizard] Deep link missing access_token');
return;
}
setLoading(true);
setError(null);
await authService.completeSupabaseSession(accessToken, serverConfig?.url || STIRLING_SAAS_URL);
await connectionModeService.switchToSaaS(serverConfig?.url || STIRLING_SAAS_URL);
tauriBackendService.startBackend().catch(console.error);
onComplete();
} catch (err) {
console.error('[SetupWizard] Failed to handle deep link', err);
setError(err instanceof Error ? err.message : 'Failed to complete signup');
setLoading(false);
}
});
return () => {
void unsubscribePromise.then((unsub) => unsub());
};
}, [onComplete, serverConfig?.url]);
const handleBack = () => {
setError(null);
if (activeStep === SetupStep.SelfHostedLogin) {
@@ -135,6 +191,8 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
} else if (activeStep === SetupStep.ServerSelection) {
setActiveStep(SetupStep.SaaSLogin);
setServerConfig({ url: STIRLING_SAAS_URL });
} else if (activeStep === SetupStep.SaaSSignup) {
setActiveStep(SetupStep.SaaSLogin);
}
};
@@ -147,11 +205,21 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
onLogin={handleSaaSLogin}
onOAuthSuccess={handleSaaSLoginOAuth}
onSelfHostedClick={handleSelfHostedClick}
onSwitchToSignup={handleSwitchToSignup}
loading={loading}
error={error}
/>
)}
{activeStep === SetupStep.SaaSSignup && (
<SaaSSignupScreen
loading={loading}
error={error}
onLogin={handleSaaSLogin}
onSwitchToLogin={handleSwitchToLogin}
/>
)}
{activeStep === SetupStep.ServerSelection && (
<ServerSelectionScreen
onSelect={handleServerSelection}