onairos
Version:
The Onairos Library is a collection of functions that enable Applications to connect and communicate data with Onairos Identities via User Authorization. Integration for developers is seamless, simple and effective for all applications. LLM SDK capabiliti
324 lines (289 loc) • 10.2 kB
JSX
import React, { useEffect, useState } from 'react';
import EmailAuth from './components/EmailAuth.js';
import UniversalOnboarding from './components/UniversalOnboarding.jsx';
import PinSetup from './components/PinSetup.js';
import DataRequest from './components/DataRequest.js';
import TrainingComponent from './components/TrainingComponent.jsx';
export function OnairosButton({
requestData,
webpageName,
inferenceData = null,
onComplete = null,
autoFetch = false,
testMode = false,
proofMode = false,
textLayout = 'below',
textColor = 'white',
login = false,
buttonType = 'pill',
loginReturn = null,
loginType = 'signIn',
visualType = 'full',
appIcon = null,
enableTraining = true
}) {
const [showOverlay, setShowOverlay] = useState(false);
const [currentFlow, setCurrentFlow] = useState('email'); // 'email' | 'onboarding' | 'pin' | 'dataRequest' (training is within onboarding)
const [userData, setUserData] = useState(null);
const [error, setError] = useState(null);
// Check for existing user session
useEffect(() => {
const checkExistingSession = () => {
const savedUser = localStorage.getItem('onairosUser');
if (savedUser) {
try {
const user = JSON.parse(savedUser);
setUserData(user);
// If user has completed onboarding and PIN setup, go directly to data request
if (user.onboardingComplete && user.pinCreated) {
setCurrentFlow('dataRequest');
} else if (user.verified && !user.onboardingComplete) {
setCurrentFlow('onboarding');
} else if (user.onboardingComplete && !user.pinCreated) {
setCurrentFlow('pin');
}
} catch (error) {
console.error('Error parsing saved user data:', error);
localStorage.removeItem('onairosUser');
}
}
};
checkExistingSession();
}, []);
const openTerminal = async () => {
try {
console.log('🔥 openTerminal called');
setShowOverlay(true);
} catch (error) {
console.error('Error in openTerminal:', error);
}
};
const handleCloseOverlay = () => {
setShowOverlay(false);
setError(null);
};
// Handle clicks on the backdrop to close modal
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) {
handleCloseOverlay();
}
};
const handleEmailAuthSuccess = (authData) => {
console.log('🔥 Email auth successful:', authData);
console.log('🔧 User State:', {
isNewUser: authData.isNewUser,
userState: authData.userState,
flowType: authData.flowType,
existingUser: authData.existingUser
});
// Determine flow based on verification response
const isNewUser = authData.isNewUser === true || authData.flowType === 'onboarding' || authData.userState === 'new';
const newUserData = {
...authData,
verified: true,
onboardingComplete: !isNewUser, // New users need onboarding, returning users have completed it
pinCreated: !isNewUser // Assume returning users have PIN, new users need to create it
};
setUserData(newUserData);
localStorage.setItem('onairosUser', JSON.stringify(newUserData));
// Flow decision logic
if (isNewUser) {
console.log('🚀 New user detected → Starting onboarding flow (includes training)');
setCurrentFlow('onboarding');
} else {
console.log('👋 Existing user detected → Going directly to data request');
setCurrentFlow('dataRequest');
}
};
const handleOnboardingComplete = (onboardingData) => {
console.log('Onboarding completed:', onboardingData);
const updatedUserData = {
...userData,
onboardingComplete: true,
connectedAccounts: onboardingData.connectedAccounts || []
};
setUserData(updatedUserData);
localStorage.setItem('onairosUser', JSON.stringify(updatedUserData));
setCurrentFlow('pin');
};
const handlePinSetupComplete = (pinData) => {
console.log('PIN setup completed:', pinData);
const updatedUserData = {
...userData,
...pinData,
pinCreated: true
};
setUserData(updatedUserData);
localStorage.setItem('onairosUser', JSON.stringify(updatedUserData));
// Move to data request flow within the same overlay
setCurrentFlow('dataRequest');
};
const handleTrainingComplete = (trainingResult) => {
console.log('🎓 Training completed:', trainingResult);
const updatedUserData = {
...userData,
trainingCompleted: true,
...trainingResult
};
setUserData(updatedUserData);
localStorage.setItem('onairosUser', JSON.stringify(updatedUserData));
// Move to data request after training
setCurrentFlow('dataRequest');
};
const handleDataRequestComplete = (requestResult) => {
console.log('🔥 OnairosButton: Data request completed:', requestResult);
// Update user data with request result
const updatedUserData = {
...userData,
lastDataRequest: requestResult
};
setUserData(updatedUserData);
localStorage.setItem('onairosUser', JSON.stringify(updatedUserData));
// Close overlay
setShowOverlay(false);
// Call onComplete callback if provided
console.log('🔥 Calling onComplete callback with:', requestResult);
if (onComplete) {
try {
onComplete(requestResult);
console.log('🔥 onComplete callback executed successfully');
} catch (error) {
console.error('🔥 Error in onComplete callback:', error);
}
} else {
console.log('🔥 No onComplete callback provided');
}
};
const renderCurrentFlow = () => {
switch (currentFlow) {
case 'email':
return (
<EmailAuth
onSuccess={handleEmailAuthSuccess}
testMode={testMode} // Use the testMode prop from initialization
/>
);
case 'onboarding':
return (
<UniversalOnboarding
onComplete={handleOnboardingComplete}
appIcon={appIcon || "https://onairos.sirv.com/Images/OnairosBlack.png"}
appName={webpageName}
username={userData?.email || userData?.username}
/>
);
case 'pin':
return (
<PinSetup
onComplete={handlePinSetupComplete}
userEmail={userData?.email}
/>
);
case 'training':
return (
<TrainingComponent
onComplete={handleTrainingComplete}
userEmail={userData?.email}
appName={webpageName}
connectedAccounts={userData?.connectedAccounts || []}
/>
);
case 'dataRequest':
return (
<DataRequest
onComplete={handleDataRequestComplete}
userEmail={userData?.email}
requestData={requestData}
appName={webpageName}
autoFetch={autoFetch}
testMode={testMode}
appIcon={appIcon}
connectedAccounts={userData?.connectedAccounts || {}}
/>
);
default:
return (
<div className="flex flex-col items-center space-y-4 p-6">
<div className="animate-spin h-8 w-8 border-2 border-blue-600 rounded-full border-t-transparent"></div>
<p className="text-gray-600">Loading...</p>
</div>
);
}
};
// Styling and button class based on visual type
const buttonClass =
`flex items-center justify-center font-bold rounded cursor-pointer ${
buttonType === 'pill' ? 'px-4 py-2' : 'w-12 h-12'
} bg-transparent OnairosConnect`;
const buttonStyle = {
flexDirection: textLayout === 'below' ? 'column' : 'row',
backgroundColor: 'transparent',
color: textColor,
border: '1px solid transparent',
};
// Icon and text style based on the visualType
const logoStyle = {
width: '20px',
height: '20px',
marginRight: visualType === 'full' ? '12px' : '0',
};
const getText = () => {
switch (loginType) {
case 'signUp':
return 'Sign Up with Onairos';
case 'signOut':
return 'Sign Out of Onairos';
default:
return 'Sign In with Onairos';
}
};
return (
<>
<button
className={buttonClass}
onClick={openTerminal}
style={buttonStyle}
>
{(visualType === 'full' || visualType === 'icon') && (
<img
src={login ? "https://onairos.sirv.com/Images/OnairosWhite.png" : "https://onairos.sirv.com/Images/OnairosBlack.png"}
alt="Onairos Logo"
style={logoStyle}
/>
)}
{visualType !== 'icon' && (
<span className={`${textColor === 'black' ? 'text-black' : 'text-white'} ${visualType === 'icon' ? 'sr-only' : ''} ${textLayout === 'right' ? 'ml-2' : textLayout === 'left' ? 'mr-2' : ''}`}>
{getText()}
</span>
)}
</button>
{/* Full-Screen Overlay (Plaid/SendPay Style) */}
{showOverlay && (
<div
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
onClick={handleBackdropClick}
>
<div
className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-hidden relative"
onClick={(e) => e.stopPropagation()}
>
{/* Close button */}
<button
onClick={handleCloseOverlay}
className="absolute top-4 right-4 text-gray-400 hover:text-gray-600 z-10"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
{/* Content */}
<div className="overflow-y-auto max-h-[90vh]">
{renderCurrentFlow()}
</div>
</div>
</div>
)}
</>
);
}
export default OnairosButton;