diff --git a/package.json b/package.json index 96704e4..1e657c8 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,9 @@ "scripts": { "dev": "vite", "dev:app": "vite --port 5173 --strictPort", - "build": "tsc -b && vite build && npm run auth:build", - "auth:build": "vite build --config vite.auth.config.ts", - "auth:dev": "vite --config vite.auth.config.ts --port 5175 --strictPort", - "prebuild:all": "npm run auth:build && node scripts/ensure-playwright-runner.js && node scripts/ensure-personal-server.js", + "build": "tsc -b && vite build", + "prebuild:all": "node scripts/ensure-playwright-runner.js && node scripts/ensure-personal-server.js", "pretauri:dev": "npm run prebuild:all", - "pretauri:build": "npm run auth:build", "lint": "eslint .", "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,scss,md}\"", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,css,scss,md}\"", @@ -33,8 +30,6 @@ }, "dependencies": { "@base-ui/react": "^1.1.0", - "@privy-io/js-sdk-core": "^0.58.7", - "@privy-io/react-auth": "^2.4.0", "@reduxjs/toolkit": "^2.11.2", "@tabler/icons-react": "^3.36.1", "@tauri-apps/api": "~2.10.1", @@ -54,6 +49,7 @@ "shadcn": "^3.7.0", "tailwind-merge": "^3.4.0", "tw-animate-css": "^1.4.0", + "viem": "^2.45.1", "zod": "^3.25.76" }, "devDependencies": { diff --git a/src-tauri/src/commands/auth.rs b/src-tauri/src/commands/auth.rs deleted file mode 100644 index c39685d..0000000 --- a/src-tauri/src/commands/auth.rs +++ /dev/null @@ -1,500 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::io::{BufRead, BufReader, Read, Write}; -use std::net::TcpListener; -use std::path::PathBuf; -use std::sync::mpsc; -use std::thread; -use tauri::path::BaseDirectory; -use tauri::{AppHandle, Emitter, Manager}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AuthResult { - pub success: bool, - pub user: Option, - #[serde(rename = "walletAddress")] - pub wallet_address: Option, - #[serde(rename = "authToken")] - pub auth_token: Option, - #[serde(rename = "masterKeySignature")] - pub master_key_signature: Option, - pub error: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AuthUser { - pub id: String, - pub email: Option, -} - -/// Start the external browser auth flow -#[tauri::command] -pub async fn start_browser_auth( - app: AppHandle, - privy_app_id: String, - privy_client_id: Option, -) -> Result { - log::info!("Starting browser auth flow with Privy app ID: {}", privy_app_id); - - // Try to bind to fixed port 3083 (whitelisted in Privy dashboard) - // Fall back to 5173 or random port if unavailable - let (listener, callback_port) = if let Ok(l) = TcpListener::bind("127.0.0.1:3083") { - log::info!("Bound to preferred port 3083"); - (l, 3083u16) - } else if let Ok(l) = TcpListener::bind("127.0.0.1:5173") { - log::info!("Port 3083 unavailable, using fallback port 5173"); - (l, 5173u16) - } else { - let l = TcpListener::bind("127.0.0.1:0") - .map_err(|e| format!("Failed to bind to any port: {}", e))?; - let port = l.local_addr() - .map_err(|e| format!("Failed to get local address: {}", e))? - .port(); - log::warn!("Using random port {} - auth may fail if not whitelisted in Privy", port); - (l, port) - }; - - log::info!("Auth callback server starting on port {}", callback_port); - - // Channel to receive auth result - let (tx, _rx) = mpsc::channel::(); - - let app_handle = app.clone(); - let privy_app_id_clone = privy_app_id.clone(); - let privy_client_id_clone = privy_client_id.unwrap_or_default(); - - // Spawn callback server thread - thread::spawn(move || { - log::info!("Auth callback server thread started"); - - let resource_dir = app_handle - .path() - .resolve("auth-page", BaseDirectory::Resource) - .ok() - .filter(|path| path.exists()); - - let auth_dir = resource_dir.unwrap_or_else(|| { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("auth-page") - }); - - log::info!("Auth page directory: {}", auth_dir.display()); - - for stream in listener.incoming() { - match stream { - Ok(mut stream) => { - let mut reader = BufReader::new(stream.try_clone().unwrap()); - let mut request_line = String::new(); - - if reader.read_line(&mut request_line).is_err() { - continue; - } - - log::debug!("Auth callback server received: {}", request_line.trim()); - - // Parse request - let parts: Vec<&str> = request_line.split_whitespace().collect(); - if parts.len() < 2 { - continue; - } - - let method = parts[0]; - let path = parts[1]; - - let request_path = path.split('?').next().unwrap_or(path); - - match method { - "GET" if path == "/" || path.starts_with("/?") => { - let index_path = auth_dir.join("index.html"); - match std::fs::read_to_string(&index_path) { - Ok(html) => { - let resolved_html = html - .replace("%PRIVY_APP_ID%", &privy_app_id_clone) - .replace("%PRIVY_CLIENT_ID%", &privy_client_id_clone); - let response = format!( - "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nCache-Control: no-store\r\nContent-Length: {}\r\n\r\n{}", - resolved_html.len(), - resolved_html - ); - let _ = stream.write_all(response.as_bytes()); - } - Err(err) => { - log::error!( - "Failed to read auth page index.html: {}", - err - ); - let response = "HTTP/1.1 500 Internal Server Error\r\n\r\n"; - let _ = stream.write_all(response.as_bytes()); - } - } - } - "GET" - if request_path.starts_with("/assets/") - || request_path.starts_with("/fonts/") - || request_path == "/favicon.ico" => - { - let asset_path = auth_dir.join(request_path.trim_start_matches('/')); - match std::fs::read(&asset_path) { - Ok(bytes) => { - let mime = - mime_guess::from_path(&asset_path).first_or_octet_stream(); - let header = format!( - "HTTP/1.1 200 OK\r\nContent-Type: {}\r\nCache-Control: no-store\r\nContent-Length: {}\r\n\r\n", - mime, - bytes.len() - ); - let _ = stream.write_all(header.as_bytes()); - let _ = stream.write_all(&bytes); - } - Err(_) => { - let response = "HTTP/1.1 404 Not Found\r\n\r\n"; - let _ = stream.write_all(response.as_bytes()); - } - } - } - "POST" if path == "/auth-callback" || path == "/" => { - // Read headers to get content length - let mut content_length: usize = 0; - let mut line = String::new(); - loop { - line.clear(); - if reader.read_line(&mut line).is_err() || line == "\r\n" { - break; - } - if line.to_lowercase().starts_with("content-length:") { - if let Some(len_str) = line.split(':').nth(1) { - content_length = len_str.trim().parse().unwrap_or(0); - } - } - } - - // Read body - let mut body = vec![0u8; content_length]; - if reader.read_exact(&mut body).is_ok() { - if let Ok(body_str) = String::from_utf8(body) { - log::info!("Auth callback received: {}", body_str); - - // Parse auth result - if let Ok(auth_result) = serde_json::from_str::(&body_str) { - // Send to channel - let _ = tx.send(auth_result.clone()); - - // Emit event to frontend - let _ = app_handle.emit("auth-complete", auth_result); - - // Focus the app window - if let Some(window) = app_handle.get_webview_window("main") { - let _ = window.set_focus(); - } - } - } - } - - // Send response with CORS headers - let response = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: POST, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type\r\n\r\n{\"ok\":true}"; - let _ = stream.write_all(response.as_bytes()); - - log::info!("Auth complete, waiting for close-tab request"); - } - "GET" if path == "/server-identity" => { - // Proxy the personal server health check to avoid CORS - log::info!("Server identity request received"); - let cors = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET, POST, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type"; - let port_opt = super::server::PERSONAL_SERVER_PORT.lock().ok().and_then(|g| *g); - log::info!("Personal server port: {:?}", port_opt); - if let Some(port) = port_opt { - match reqwest::blocking::get(format!("http://localhost:{}/health", port)) { - Ok(resp) if resp.status().is_success() => { - let body = resp.text().unwrap_or_default(); - let resp_str = format!( - "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - cors, body.len(), body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - _ => { - let resp_str = format!("HTTP/1.1 503 Service Unavailable\r\n{}\r\n\r\n", cors); - let _ = stream.write_all(resp_str.as_bytes()); - } - } - } else { - let resp_str = format!("HTTP/1.1 503 Service Unavailable\r\n{}\r\n\r\n", cors); - let _ = stream.write_all(resp_str.as_bytes()); - } - } - "POST" if path == "/register-server" => { - // Read POST body - let mut content_length: usize = 0; - let mut line = String::new(); - loop { - line.clear(); - if reader.read_line(&mut line).is_err() || line == "\r\n" { - break; - } - if line.to_lowercase().starts_with("content-length:") { - if let Some(len_str) = line.split(':').nth(1) { - content_length = len_str.trim().parse().unwrap_or(0); - } - } - } - - let cors = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET, POST, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type"; - let mut body = vec![0u8; content_length]; - let gateway_url = "https://data-gateway-env-dev-opendatalabs.vercel.app"; - - if reader.read_exact(&mut body).is_ok() { - if let Ok(body_str) = String::from_utf8(body) { - log::info!("Register server request: {}", body_str); - - // Parse { signature, message } - if let Ok(reg) = serde_json::from_str::(&body_str) { - let signature = reg["signature"].as_str().unwrap_or_default(); - let message = ®["message"]; - - // POST to gateway - let client = reqwest::blocking::Client::new(); - match client - .post(format!("{}/v1/servers", gateway_url)) - .header("Content-Type", "application/json") - .header("Authorization", format!("Web3Signed {}", signature)) - .json(message) - .send() - { - Ok(resp) => { - let status = resp.status().as_u16(); - let resp_body = resp.text().unwrap_or_default(); - log::info!("Gateway register response: {} {}", status, resp_body); - - if status == 200 || status == 201 || status == 409 { - // Extract serverId from response and include in event - let server_id = serde_json::from_str::(&resp_body) - .ok() - .and_then(|v| v["serverId"].as_str().map(|s| s.to_string())); - let _ = app_handle.emit("server-registered", serde_json::json!({ - "status": status, - "serverId": server_id - })); - } - - let resp_str = format!( - "HTTP/1.1 {} OK\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - status, cors, resp_body.len(), resp_body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - Err(e) => { - let err_body = format!("{{\"error\":\"{}\"}}", e); - let resp_str = format!( - "HTTP/1.1 502 Bad Gateway\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - cors, err_body.len(), err_body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - } - } - } - } - } - "GET" if request_path == "/check-server-url" => { - // Proxy GET /v1/servers/{address} to gateway - let cors = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET, POST, DELETE, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type, Authorization"; - let gateway_url = "https://data-gateway-env-dev-opendatalabs.vercel.app"; - let address = path.split("address=").nth(1).unwrap_or("").split('&').next().unwrap_or(""); - - if address.is_empty() { - let resp_str = format!( - "HTTP/1.1 400 Bad Request\r\nContent-Type: application/json\r\n{}\r\n\r\n{{\"error\":\"Missing address parameter\"}}", - cors - ); - let _ = stream.write_all(resp_str.as_bytes()); - } else { - let client = reqwest::blocking::Client::new(); - match client - .get(format!("{}/v1/servers/{}", gateway_url, address)) - .send() - { - Ok(resp) => { - let status = resp.status().as_u16(); - let resp_body = resp.text().unwrap_or_default(); - log::info!("Gateway check-server-url response: {} {}", status, &resp_body[..resp_body.len().min(200)]); - let resp_str = format!( - "HTTP/1.1 {} OK\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - status, cors, resp_body.len(), resp_body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - Err(e) => { - let err_body = format!("{{\"error\":\"{}\"}}", e); - let resp_str = format!( - "HTTP/1.1 502 Bad Gateway\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - cors, err_body.len(), err_body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - } - } - } - "POST" if path == "/deregister-server" => { - // Proxy DELETE /v1/servers/{serverAddress} to gateway - let mut content_length: usize = 0; - let mut line = String::new(); - loop { - line.clear(); - if reader.read_line(&mut line).is_err() || line == "\r\n" { - break; - } - if line.to_lowercase().starts_with("content-length:") { - if let Some(len_str) = line.split(':').nth(1) { - content_length = len_str.trim().parse().unwrap_or(0); - } - } - } - - let cors = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET, POST, DELETE, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type, Authorization"; - let gateway_url = "https://data-gateway-env-dev-opendatalabs.vercel.app"; - let mut body = vec![0u8; content_length]; - - if reader.read_exact(&mut body).is_ok() { - if let Ok(body_str) = String::from_utf8(body) { - log::info!("Deregister server request: {}", body_str); - - if let Ok(dereg) = serde_json::from_str::(&body_str) { - let server_address = dereg["serverAddress"].as_str().unwrap_or_default(); - let signature = dereg["signature"].as_str().unwrap_or_default(); - let owner_address = dereg["ownerAddress"].as_str().unwrap_or_default(); - let deadline = dereg["deadline"].as_u64().unwrap_or(0); - - let delete_body = serde_json::json!({ - "ownerAddress": owner_address, - "deadline": deadline, - }); - - let client = reqwest::blocking::Client::new(); - match client - .delete(format!("{}/v1/servers/{}", gateway_url, server_address)) - .header("Content-Type", "application/json") - .header("Authorization", format!("Web3Signed {}", signature)) - .json(&delete_body) - .send() - { - Ok(resp) => { - let status = resp.status().as_u16(); - let resp_body = resp.text().unwrap_or_default(); - log::info!("Gateway deregister response: {} {}", status, resp_body); - let resp_str = format!( - "HTTP/1.1 {} OK\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - status, cors, resp_body.len(), resp_body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - Err(e) => { - let err_body = format!("{{\"error\":\"{}\"}}", e); - let resp_str = format!( - "HTTP/1.1 502 Bad Gateway\r\nContent-Type: application/json\r\n{}\r\nContent-Length: {}\r\n\r\n{}", - cors, err_body.len(), err_body - ); - let _ = stream.write_all(resp_str.as_bytes()); - } - } - } - } - } - } - "GET" if path == "/close-tab" => { - // Browser tab navigates here after showing success. - // Serve a page with the success message, then close the tab - // from the native side via Cmd+W (macOS). - let close_html = r##" -Signed in
-
-

You're signed in!

-

Closing this tab...

-
"##; - let response = format!( - "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {}\r\n\r\n{}", - close_html.len(), - close_html - ); - let _ = stream.write_all(response.as_bytes()); - - #[cfg(target_os = "macos")] - { - std::thread::sleep(std::time::Duration::from_millis(800)); - let _ = std::process::Command::new("osascript") - .arg("-e") - .arg(r#"tell application "System Events" to keystroke "w" using command down"#) - .output(); - } - - log::info!("Close-tab done, shutting down auth server"); - break; - } - "OPTIONS" => { - // Handle CORS preflight - let response = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: POST, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type\r\n\r\n"; - let _ = stream.write_all(response.as_bytes()); - } - _ => { - let response = "HTTP/1.1 404 Not Found\r\n\r\n"; - let _ = stream.write_all(response.as_bytes()); - } - } - } - Err(e) => { - log::error!("Auth callback server connection error: {}", e); - } - } - } - - log::info!("Auth callback server thread ended"); - }); - - // Open browser to the self-contained auth page served by our localhost server - let auth_url = format!("http://localhost:{}", callback_port); - log::info!("Opening browser to: {}", auth_url); - - #[cfg(target_os = "macos")] - { - std::process::Command::new("open") - .arg(&auth_url) - .spawn() - .map_err(|e| format!("Failed to open browser: {}", e))?; - } - - #[cfg(target_os = "linux")] - { - std::process::Command::new("xdg-open") - .arg(&auth_url) - .spawn() - .map_err(|e| format!("Failed to open browser: {}", e))?; - } - - #[cfg(target_os = "windows")] - { - std::process::Command::new("cmd") - .args(["/C", "start", &auth_url]) - .spawn() - .map_err(|e| format!("Failed to open browser: {}", e))?; - } - - // Emit event that auth flow has started - app.emit( - "auth-started", - serde_json::json!({ "callbackPort": callback_port, "authUrl": auth_url }), - ) - .map_err(|e| format!("Failed to emit event: {}", e))?; - - Ok(auth_url) -} - -/// Cancel the auth flow -#[tauri::command] -pub fn cancel_browser_auth() -> Result<(), String> { - // The server will automatically close when the thread ends - // or when the auth completes - Ok(()) -} diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 6fe1ab1..c4c602a 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -1,11 +1,9 @@ -pub mod auth; pub mod connector; pub mod download; pub mod file_ops; pub mod server; pub mod updates; -pub use auth::*; pub use connector::*; pub use download::*; pub use file_ops::*; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fa6dbef..c18878e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,13 +2,13 @@ mod commands; mod processors; use commands::{ - cancel_browser_auth, check_browser_available, check_connected_platforms, check_connector_updates, + check_browser_available, check_connected_platforms, check_connector_updates, cleanup_personal_server, clear_browser_session, debug_connector_paths, download_browser, download_chromium_rust, download_connector, get_app_config, get_installed_connectors, get_personal_server_status, get_platforms, get_registry_url, get_run_files, get_user_data_path, handle_download, list_browser_sessions, load_latest_source_export_full, load_latest_source_export_preview, load_run_export_data, load_runs, mark_export_synced, - open_folder, open_platform_export_folder, set_app_config, start_browser_auth, + open_folder, open_platform_export_folder, set_app_config, start_personal_server, start_connector_run, stop_connector_run, stop_personal_server, test_nodejs, write_export_data, }; @@ -79,8 +79,6 @@ pub fn run() { get_installed_connectors, get_app_config, set_app_config, - start_browser_auth, - cancel_browser_auth, start_personal_server, stop_personal_server, get_personal_server_status, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6662745..6c0d080 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -6,7 +6,7 @@ "build": { "frontendDist": "../dist", "devUrl": "http://localhost:5173", - "beforeDevCommand": "npm run auth:build && npm run dev", + "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build" }, "app": { @@ -62,10 +62,7 @@ "../connectors/openai/**/*": "connectors/openai/", "../connectors/spotify/**/*": "connectors/spotify/", "../playwright-runner/dist/*": "playwright-runner/dist/", - "../personal-server/dist/personal-server*": "personal-server/dist/", - "auth-page/*": "auth-page/", - "auth-page/assets/*": "auth-page/assets/", - "auth-page/fonts/*": "auth-page/fonts/" + "../personal-server/dist/personal-server*": "personal-server/dist/" }, "windows": { "nsis": { diff --git a/src/App.tsx b/src/App.tsx index 5734f98..d0e16d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,10 @@ import { lazy, Suspense } from "react" -import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom" +import { BrowserRouter, Routes, Route } from "react-router-dom" import { Provider } from "react-redux" import { store } from "./state/store" import { useEvents } from "./hooks/useEvents" import { useInitialize } from "./hooks/useInitialize" import { TopNav } from "./components/top-nav" -import { PrivyProvider } from "./components/providers/PrivyProvider" -import { InlineLogin } from "./components/auth/InlineLogin" -import { BrowserLogin } from "./pages/browser-login" import { useDeepLink } from "./hooks/use-deep-link" import { usePersonalServer } from "./hooks/usePersonalServer" import { usePendingApprovalRetry } from "./hooks/usePendingApproval" @@ -84,7 +81,6 @@ function AppContent() { } /> } /> } /> - } /> @@ -93,47 +89,32 @@ function AppContent() { ) } -// Router wrapper that handles both app content and standalone browser login -function AppRouter() { - const location = useLocation() - - // Browser login page is standalone (for external browser auth flow) - if (location.pathname === ROUTES.browserLogin) { - return - } - - return -} - function App() { return ( - -
- - Loading...
}> - - } /> - } - /> - } - /> - {/* Demo flow (throwaway — for video recording) */} - } /> - } /> - } /> - } /> - } /> - } /> - - - - -
+
+ + Loading...
}> + + } + /> + } + /> + {/* Demo flow (throwaway — for video recording) */} + } /> + } /> + } /> + } /> + } /> + } /> + + + +
) } diff --git a/src/auth-page/App.tsx b/src/auth-page/App.tsx deleted file mode 100644 index 53e4865..0000000 --- a/src/auth-page/App.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import { ArrowRight, Loader2, Mail } from "lucide-react" -import { VanaLogotype } from "@/components/icons/vana-logotype" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Text } from "@/components/typography/text" -import { PlatformAppleIcon } from "@/components/icons/platform-apple" -import { useAuthPage } from "./auth" -import { cn } from "@/lib/classes" - -// View this page locally: -// - npx vite --config vite.auth.config.ts -// - open http://localhost:5173 -// (Optional) Full Tauri window: npm run tauri:dev - -const GoogleIcon = () => ( - -) - -export const App = () => { - const { - view, - loadingText, - error, - email, - code, - showCode, - isSendingEmail, - isVerifyingCode, - isGoogleLoading, - isAppleLoading, - walletIframeUrl, - walletIframeRef, - handleWalletIframeLoad, - handleEmailChange, - handleCodeChange, - handleEmailSubmit, - handleVerifyCode, - handleGoogleLogin, - handleAppleLogin, - } = useAuthPage() - - const isEmailDisabled = isSendingEmail || isGoogleLoading || isAppleLoading - const isVerifyDisabled = isVerifyingCode || isGoogleLoading || isAppleLoading - - return ( -
-
-
- {view === "loading" && ( -
- - - {loadingText} - -
- )} - - {view === "success" && ( -
-
- -
- - Signed in. - - - You may now close this tab. - -
- )} - - {view === "login" && ( -
-
- - - - Data Connect uses Vana Passport - -
- to bring your data everywhere -
- - Sign-in or create your Vana Passport to grant permissions. - -
- - {error && ( -
- {error} -
- )} - -
- {!showCode && ( -
- - handleEmailChange(event.target.value)} - onKeyDown={event => { - if (event.key === "Enter") { - handleEmailSubmit() - } - }} - placeholder="jane@example.com" - className="h-8 border-0 bg-transparent px-0 text-body focus-visible:border-transparent focus-visible:ring-0" - /> - -
- )} - - - - -
- - {showCode && ( -
- - Enter verification code - - handleCodeChange(event.target.value)} - onKeyDown={event => { - if (event.key === "Enter") { - handleVerifyCode() - } - }} - placeholder="------" - maxLength={6} - className="h-12 text-center text-xlarge tracking-[0.35em]" - /> - -
- )} - - - By creating an account, you agree to our{" "} - - Terms of Service - {" "} - and{" "} - - Privacy Policy - - . - -
- )} -
- - {walletIframeUrl && ( -