session bug fix
This commit is contained in:
514
src/components/ui/BluetoothKeyTransfer.jsx
Normal file
514
src/components/ui/BluetoothKeyTransfer.jsx
Normal file
@@ -0,0 +1,514 @@
|
||||
/**
|
||||
* Bluetooth Key Transfer UI Component
|
||||
*
|
||||
* Provides user interface for Bluetooth key exchange:
|
||||
* - Device discovery and connection
|
||||
* - Key transmission status
|
||||
* - Error handling and fallbacks
|
||||
* - Integration with existing QR/manual methods
|
||||
*/
|
||||
|
||||
const BluetoothKeyTransfer = ({
|
||||
webrtcManager,
|
||||
onKeyReceived,
|
||||
onStatusChange,
|
||||
onAutoConnection,
|
||||
isVisible = false,
|
||||
onClose
|
||||
}) => {
|
||||
const [bluetoothManager, setBluetoothManager] = React.useState(null);
|
||||
const [isSupported, setIsSupported] = React.useState(false);
|
||||
const [isAvailable, setIsAvailable] = React.useState(false);
|
||||
const [isScanning, setIsScanning] = React.useState(false);
|
||||
const [isAdvertising, setIsAdvertising] = React.useState(false);
|
||||
const [connectedDevices, setConnectedDevices] = React.useState([]);
|
||||
const [status, setStatus] = React.useState('idle');
|
||||
const [error, setError] = React.useState(null);
|
||||
const [logs, setLogs] = React.useState([]);
|
||||
|
||||
// Initialize Bluetooth manager
|
||||
React.useEffect(() => {
|
||||
if (isVisible && !bluetoothManager) {
|
||||
initializeBluetooth();
|
||||
}
|
||||
}, [isVisible]);
|
||||
|
||||
// Cleanup on unmount
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (bluetoothManager) {
|
||||
bluetoothManager.cleanup();
|
||||
}
|
||||
};
|
||||
}, [bluetoothManager]);
|
||||
|
||||
const initializeBluetooth = async () => {
|
||||
try {
|
||||
const manager = new window.BluetoothKeyTransfer(
|
||||
webrtcManager,
|
||||
handleStatusChange,
|
||||
handleKeyReceived,
|
||||
handleError,
|
||||
handleAutoConnection
|
||||
);
|
||||
|
||||
setBluetoothManager(manager);
|
||||
|
||||
// Check support after initialization
|
||||
setTimeout(() => {
|
||||
setIsSupported(manager.isSupported);
|
||||
setIsAvailable(manager.isAvailable);
|
||||
}, 100);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize Bluetooth manager:', error);
|
||||
setError('Failed to initialize Bluetooth: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStatusChange = (statusType, data) => {
|
||||
setStatus(statusType);
|
||||
addLog(`Status: ${statusType}`, data);
|
||||
|
||||
// Update UI state based on status
|
||||
switch (statusType) {
|
||||
case 'bluetooth_ready':
|
||||
setIsSupported(data.supported);
|
||||
setIsAvailable(data.available);
|
||||
break;
|
||||
case 'scanning_active':
|
||||
setIsScanning(true);
|
||||
break;
|
||||
case 'scanning_stopped':
|
||||
setIsScanning(false);
|
||||
break;
|
||||
case 'advertising_active':
|
||||
setIsAdvertising(true);
|
||||
break;
|
||||
case 'advertising_stopped':
|
||||
setIsAdvertising(false);
|
||||
break;
|
||||
case 'connected':
|
||||
setConnectedDevices(prev => [...prev, {
|
||||
id: data.deviceId,
|
||||
name: data.deviceName,
|
||||
connected: true
|
||||
}]);
|
||||
break;
|
||||
}
|
||||
|
||||
onStatusChange?.(statusType, data);
|
||||
};
|
||||
|
||||
const handleKeyReceived = (keyData, deviceId) => {
|
||||
addLog('Key received from device', { deviceId });
|
||||
onKeyReceived?.(keyData, deviceId);
|
||||
};
|
||||
|
||||
const handleError = (error) => {
|
||||
console.error('Bluetooth error:', error);
|
||||
setError(error.message);
|
||||
addLog('Error', error.message);
|
||||
};
|
||||
|
||||
const handleAutoConnection = (connectionData) => {
|
||||
console.log('Auto connection completed:', connectionData);
|
||||
addLog('Auto Connection Completed', connectionData);
|
||||
onAutoConnection?.(connectionData);
|
||||
};
|
||||
|
||||
const addLog = (message, data = null) => {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const logEntry = {
|
||||
timestamp,
|
||||
message,
|
||||
data: data ? JSON.stringify(data, null, 2) : null
|
||||
};
|
||||
setLogs(prev => [...prev.slice(-9), logEntry]); // Keep last 10 logs
|
||||
};
|
||||
|
||||
const startScanning = async () => {
|
||||
try {
|
||||
setError(null);
|
||||
await bluetoothManager.startScanning();
|
||||
} catch (error) {
|
||||
setError('Failed to start scanning: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const stopScanning = async () => {
|
||||
try {
|
||||
await bluetoothManager.stopScanning();
|
||||
} catch (error) {
|
||||
setError('Failed to stop scanning: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const startAdvertising = async () => {
|
||||
try {
|
||||
setError(null);
|
||||
if (!webrtcManager || !webrtcManager.ecdhKeyPair) {
|
||||
throw new Error('No public key available for advertising');
|
||||
}
|
||||
|
||||
await bluetoothManager.startAdvertising(
|
||||
webrtcManager.ecdhKeyPair.publicKey,
|
||||
'SecureBit Device'
|
||||
);
|
||||
} catch (error) {
|
||||
setError('Failed to start advertising: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const stopAdvertising = async () => {
|
||||
try {
|
||||
await bluetoothManager.stopAdvertising();
|
||||
} catch (error) {
|
||||
setError('Failed to stop advertising: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const sendPublicKey = async (deviceId) => {
|
||||
try {
|
||||
setError(null);
|
||||
if (!webrtcManager || !webrtcManager.ecdhKeyPair) {
|
||||
throw new Error('No public key available for sending');
|
||||
}
|
||||
|
||||
await bluetoothManager.sendPublicKey(
|
||||
webrtcManager.ecdhKeyPair.publicKey,
|
||||
deviceId
|
||||
);
|
||||
} catch (error) {
|
||||
setError('Failed to send public key: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const clearLogs = () => {
|
||||
setLogs([]);
|
||||
};
|
||||
|
||||
const startAutoConnection = async (deviceId) => {
|
||||
try {
|
||||
setError(null);
|
||||
await bluetoothManager.startAutoConnection(deviceId);
|
||||
} catch (error) {
|
||||
setError('Failed to start auto connection: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const startAutoConnectionAsResponder = async (deviceId) => {
|
||||
try {
|
||||
setError(null);
|
||||
await bluetoothManager.startAutoConnectionAsResponder(deviceId);
|
||||
} catch (error) {
|
||||
setError('Failed to start auto connection as responder: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return React.createElement('div', {
|
||||
className: 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'modal',
|
||||
className: 'bg-gray-900 rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden'
|
||||
}, [
|
||||
// Header
|
||||
React.createElement('div', {
|
||||
key: 'header',
|
||||
className: 'flex items-center justify-between p-6 border-b border-gray-700'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'title',
|
||||
className: 'flex items-center space-x-3'
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: 'fas fa-bluetooth text-blue-400 text-xl'
|
||||
}),
|
||||
React.createElement('h2', {
|
||||
key: 'text',
|
||||
className: 'text-xl font-semibold text-white'
|
||||
}, 'Bluetooth Key Transfer')
|
||||
]),
|
||||
React.createElement('button', {
|
||||
key: 'close',
|
||||
onClick: onClose,
|
||||
className: 'text-gray-400 hover:text-white transition-colors'
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
className: 'fas fa-times text-xl'
|
||||
})
|
||||
])
|
||||
]),
|
||||
|
||||
// Content
|
||||
React.createElement('div', {
|
||||
key: 'content',
|
||||
className: 'p-6 space-y-6 overflow-y-auto max-h-[calc(90vh-200px)]'
|
||||
}, [
|
||||
// Status Section
|
||||
React.createElement('div', {
|
||||
key: 'status',
|
||||
className: 'space-y-4'
|
||||
}, [
|
||||
React.createElement('h3', {
|
||||
key: 'title',
|
||||
className: 'text-lg font-medium text-white'
|
||||
}, 'Bluetooth Status'),
|
||||
|
||||
React.createElement('div', {
|
||||
key: 'indicators',
|
||||
className: 'grid grid-cols-2 gap-4'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'support',
|
||||
className: 'flex items-center space-x-2'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
className: `w-3 h-3 rounded-full ${isSupported ? 'bg-green-500' : 'bg-red-500'}`
|
||||
}),
|
||||
React.createElement('span', {
|
||||
className: 'text-sm text-gray-300'
|
||||
}, 'Bluetooth Supported')
|
||||
]),
|
||||
React.createElement('div', {
|
||||
key: 'availability',
|
||||
className: 'flex items-center space-x-2'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
className: `w-3 h-3 rounded-full ${isAvailable ? 'bg-green-500' : 'bg-red-500'}`
|
||||
}),
|
||||
React.createElement('span', {
|
||||
className: 'text-sm text-gray-300'
|
||||
}, 'Bluetooth Available')
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Controls Section
|
||||
React.createElement('div', {
|
||||
key: 'controls',
|
||||
className: 'space-y-4'
|
||||
}, [
|
||||
React.createElement('h3', {
|
||||
key: 'title',
|
||||
className: 'text-lg font-medium text-white'
|
||||
}, 'Key Exchange'),
|
||||
|
||||
React.createElement('div', {
|
||||
key: 'buttons',
|
||||
className: 'grid grid-cols-1 sm:grid-cols-2 gap-4'
|
||||
}, [
|
||||
// Scanning Controls
|
||||
React.createElement('div', {
|
||||
key: 'scanning',
|
||||
className: 'space-y-2'
|
||||
}, [
|
||||
React.createElement('h4', {
|
||||
key: 'title',
|
||||
className: 'text-sm font-medium text-gray-300'
|
||||
}, 'Discover Devices'),
|
||||
React.createElement('button', {
|
||||
key: 'scan',
|
||||
onClick: isScanning ? stopScanning : startScanning,
|
||||
disabled: !isSupported || !isAvailable,
|
||||
className: `w-full px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
isScanning
|
||||
? 'bg-red-600 hover:bg-red-700 text-white'
|
||||
: 'bg-blue-600 hover:bg-blue-700 text-white disabled:bg-gray-600 disabled:cursor-not-allowed'
|
||||
}`
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: `fas ${isScanning ? 'fa-stop' : 'fa-search'} mr-2`
|
||||
}),
|
||||
isScanning ? 'Stop Scanning' : 'Start Scanning'
|
||||
])
|
||||
]),
|
||||
|
||||
// Advertising Controls
|
||||
React.createElement('div', {
|
||||
key: 'advertising',
|
||||
className: 'space-y-2'
|
||||
}, [
|
||||
React.createElement('h4', {
|
||||
key: 'title',
|
||||
className: 'text-sm font-medium text-gray-300'
|
||||
}, 'Share Your Key'),
|
||||
React.createElement('button', {
|
||||
key: 'advertise',
|
||||
onClick: isAdvertising ? stopAdvertising : startAdvertising,
|
||||
disabled: !isSupported || !isAvailable,
|
||||
className: `w-full px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
isAdvertising
|
||||
? 'bg-red-600 hover:bg-red-700 text-white'
|
||||
: 'bg-green-600 hover:bg-green-700 text-white disabled:bg-gray-600 disabled:cursor-not-allowed'
|
||||
}`
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: `fas ${isAdvertising ? 'fa-stop' : 'fa-broadcast-tower'} mr-2`
|
||||
}),
|
||||
isAdvertising ? 'Stop Sharing' : 'Start Sharing'
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Connected Devices
|
||||
connectedDevices.length > 0 && React.createElement('div', {
|
||||
key: 'devices',
|
||||
className: 'space-y-4'
|
||||
}, [
|
||||
React.createElement('h3', {
|
||||
key: 'title',
|
||||
className: 'text-lg font-medium text-white'
|
||||
}, 'Connected Devices'),
|
||||
|
||||
React.createElement('div', {
|
||||
key: 'list',
|
||||
className: 'space-y-2'
|
||||
}, connectedDevices.map(device =>
|
||||
React.createElement('div', {
|
||||
key: device.id,
|
||||
className: 'flex items-center justify-between p-3 bg-gray-800 rounded-lg'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'info',
|
||||
className: 'flex items-center space-x-3'
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: 'fas fa-mobile-alt text-blue-400'
|
||||
}),
|
||||
React.createElement('span', {
|
||||
key: 'name',
|
||||
className: 'text-white'
|
||||
}, device.name)
|
||||
]),
|
||||
React.createElement('div', {
|
||||
key: 'buttons',
|
||||
className: 'flex space-x-2'
|
||||
}, [
|
||||
React.createElement('button', {
|
||||
key: 'auto-connect',
|
||||
onClick: () => startAutoConnection(device.id),
|
||||
className: 'px-3 py-1 bg-green-600 hover:bg-green-700 text-white text-sm rounded transition-colors'
|
||||
}, 'Auto Connect'),
|
||||
React.createElement('button', {
|
||||
key: 'auto-respond',
|
||||
onClick: () => startAutoConnectionAsResponder(device.id),
|
||||
className: 'px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded transition-colors'
|
||||
}, 'Auto Respond'),
|
||||
React.createElement('button', {
|
||||
key: 'send',
|
||||
onClick: () => sendPublicKey(device.id),
|
||||
className: 'px-3 py-1 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded transition-colors'
|
||||
}, 'Send Key')
|
||||
])
|
||||
])
|
||||
))
|
||||
]),
|
||||
|
||||
// Error Display
|
||||
error && React.createElement('div', {
|
||||
key: 'error',
|
||||
className: 'p-4 bg-red-900 border border-red-700 rounded-lg'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'header',
|
||||
className: 'flex items-center space-x-2 mb-2'
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
key: 'icon',
|
||||
className: 'fas fa-exclamation-triangle text-red-400'
|
||||
}),
|
||||
React.createElement('h4', {
|
||||
key: 'title',
|
||||
className: 'text-red-400 font-medium'
|
||||
}, 'Error')
|
||||
]),
|
||||
React.createElement('p', {
|
||||
key: 'message',
|
||||
className: 'text-red-300 text-sm'
|
||||
}, error)
|
||||
]),
|
||||
|
||||
// Logs Section
|
||||
React.createElement('div', {
|
||||
key: 'logs',
|
||||
className: 'space-y-4'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'header',
|
||||
className: 'flex items-center justify-between'
|
||||
}, [
|
||||
React.createElement('h3', {
|
||||
key: 'title',
|
||||
className: 'text-lg font-medium text-white'
|
||||
}, 'Activity Log'),
|
||||
React.createElement('button', {
|
||||
key: 'clear',
|
||||
onClick: clearLogs,
|
||||
className: 'text-sm text-gray-400 hover:text-white transition-colors'
|
||||
}, 'Clear')
|
||||
]),
|
||||
|
||||
React.createElement('div', {
|
||||
key: 'log-list',
|
||||
className: 'bg-gray-800 rounded-lg p-4 max-h-40 overflow-y-auto'
|
||||
}, logs.length === 0 ?
|
||||
React.createElement('p', {
|
||||
key: 'empty',
|
||||
className: 'text-gray-400 text-sm text-center'
|
||||
}, 'No activity yet') :
|
||||
logs.map((log, index) =>
|
||||
React.createElement('div', {
|
||||
key: index,
|
||||
className: 'text-xs text-gray-300 mb-1'
|
||||
}, [
|
||||
React.createElement('span', {
|
||||
key: 'time',
|
||||
className: 'text-gray-500'
|
||||
}, `[${log.timestamp}] `),
|
||||
React.createElement('span', {
|
||||
key: 'message',
|
||||
className: 'text-gray-300'
|
||||
}, log.message),
|
||||
log.data && React.createElement('pre', {
|
||||
key: 'data',
|
||||
className: 'text-gray-400 mt-1 ml-4'
|
||||
}, log.data)
|
||||
])
|
||||
)
|
||||
)
|
||||
])
|
||||
]),
|
||||
|
||||
// Footer
|
||||
React.createElement('div', {
|
||||
key: 'footer',
|
||||
className: 'flex items-center justify-between p-6 border-t border-gray-700'
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'info',
|
||||
className: 'text-sm text-gray-400'
|
||||
}, 'Bluetooth key exchange provides secure device-to-device communication'),
|
||||
|
||||
React.createElement('button', {
|
||||
key: 'close-footer',
|
||||
onClick: onClose,
|
||||
className: 'px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition-colors'
|
||||
}, 'Close')
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
// Export component
|
||||
if (typeof window !== 'undefined') {
|
||||
window.BluetoothKeyTransfer = BluetoothKeyTransfer;
|
||||
}
|
||||
@@ -9,6 +9,10 @@ const EnhancedMinimalHeader = ({
|
||||
}) => {
|
||||
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
|
||||
const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0);
|
||||
// Added local session state to remove references errors after session timer removal
|
||||
const [hasActiveSession, setHasActiveSession] = React.useState(false);
|
||||
const [currentTimeLeft, setCurrentTimeLeft] = React.useState(0);
|
||||
const [sessionType, setSessionType] = React.useState('unknown');
|
||||
|
||||
// ============================================
|
||||
// FIXED SECURITY UPDATE LOGIC
|
||||
@@ -154,7 +158,7 @@ const EnhancedMinimalHeader = ({
|
||||
setHasActiveSession(true);
|
||||
setCurrentTimeLeft(0);
|
||||
setSessionType('premium'); // All features enabled
|
||||
}, [sessionTimeLeft]);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleForceUpdate = (event) => {
|
||||
|
||||
Reference in New Issue
Block a user