feat: Implement SigSocket request queuing and approval system, Enhance Settings UI
This commit is contained in:
@@ -25,6 +25,10 @@ class SigSocketService {
|
||||
|
||||
// UI communication
|
||||
this.popupPort = null;
|
||||
|
||||
// Status monitoring
|
||||
this.statusMonitorInterval = null;
|
||||
this.lastKnownConnectionState = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,72 +48,221 @@ class SigSocketService {
|
||||
console.warn('Failed to load SigSocket URL from storage:', error);
|
||||
}
|
||||
|
||||
// Restore any persisted pending requests
|
||||
await this.restorePendingRequests();
|
||||
|
||||
console.log('🔌 SigSocket service initialized with WASM APIs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore pending requests from persistent storage
|
||||
* Only restore requests that match the current workspace
|
||||
*/
|
||||
async restorePendingRequests() {
|
||||
try {
|
||||
const result = await chrome.storage.local.get(['sigSocketPendingRequests']);
|
||||
if (result.sigSocketPendingRequests && Array.isArray(result.sigSocketPendingRequests)) {
|
||||
console.log(`🔄 Found ${result.sigSocketPendingRequests.length} stored requests`);
|
||||
|
||||
// Filter requests for current workspace only
|
||||
const currentWorkspaceRequests = result.sigSocketPendingRequests.filter(request =>
|
||||
request.target_public_key === this.connectedPublicKey
|
||||
);
|
||||
|
||||
console.log(`🔄 Restoring ${currentWorkspaceRequests.length} requests for current workspace`);
|
||||
|
||||
// Add each workspace-specific request back to WASM storage
|
||||
for (const request of currentWorkspaceRequests) {
|
||||
try {
|
||||
await this.wasmModule.SigSocketManager.add_pending_request(JSON.stringify(request.request || request));
|
||||
console.log(`✅ Restored request: ${request.id || request.request?.id}`);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to restore request ${request.id || request.request?.id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Update badge after restoration
|
||||
this.updateBadge();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to restore pending requests:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist pending requests to storage with workspace isolation
|
||||
*/
|
||||
async persistPendingRequests() {
|
||||
try {
|
||||
const requests = await this.getFilteredRequests();
|
||||
|
||||
// Get existing storage to merge with other workspaces
|
||||
const result = await chrome.storage.local.get(['sigSocketPendingRequests']);
|
||||
const existingRequests = result.sigSocketPendingRequests || [];
|
||||
|
||||
// Remove old requests for current workspace
|
||||
const otherWorkspaceRequests = existingRequests.filter(request =>
|
||||
request.target_public_key !== this.connectedPublicKey
|
||||
);
|
||||
|
||||
// Combine with current workspace requests
|
||||
const allRequests = [...otherWorkspaceRequests, ...requests];
|
||||
|
||||
await chrome.storage.local.set({ sigSocketPendingRequests: allRequests });
|
||||
console.log(`💾 Persisted ${requests.length} requests for current workspace (${allRequests.length} total)`);
|
||||
} catch (error) {
|
||||
console.warn('Failed to persist pending requests:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to SigSocket server using WASM APIs
|
||||
* WASM handles all connection logic (reuse, switching, etc.)
|
||||
* @param {string} workspaceId - The workspace/keyspace identifier
|
||||
* @param {number} retryCount - Number of retry attempts (default: 3)
|
||||
* @returns {Promise<boolean>} - True if connected successfully
|
||||
*/
|
||||
async connectToServer(workspaceId) {
|
||||
try {
|
||||
if (!this.wasmModule?.SigSocketManager) {
|
||||
throw new Error('WASM SigSocketManager not available');
|
||||
async connectToServer(workspaceId, retryCount = 3) {
|
||||
for (let attempt = 1; attempt <= retryCount; attempt++) {
|
||||
try {
|
||||
if (!this.wasmModule?.SigSocketManager) {
|
||||
throw new Error('WASM SigSocketManager not available');
|
||||
}
|
||||
|
||||
console.log(`🔗 Requesting SigSocket connection for workspace: ${workspaceId} (attempt ${attempt}/${retryCount})`);
|
||||
|
||||
// Clean workspace switching
|
||||
if (this.currentWorkspace && this.currentWorkspace !== workspaceId) {
|
||||
console.log(`🔄 Clean workspace switch: ${this.currentWorkspace} -> ${workspaceId}`);
|
||||
await this.cleanWorkspaceSwitch(workspaceId);
|
||||
// Small delay to ensure clean state transition
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
}
|
||||
|
||||
// Let WASM handle all connection logic (reuse, switching, etc.)
|
||||
const connectionInfo = await this.wasmModule.SigSocketManager.connect_workspace_with_events(
|
||||
workspaceId,
|
||||
this.defaultServerUrl,
|
||||
(event) => this.handleSigSocketEvent(event)
|
||||
);
|
||||
|
||||
// Parse connection info
|
||||
const info = JSON.parse(connectionInfo);
|
||||
this.currentWorkspace = workspaceId; // Use the parameter we passed, not WASM response
|
||||
this.connectedPublicKey = info.public_key;
|
||||
this.isConnected = info.is_connected;
|
||||
|
||||
console.log(`✅ SigSocket connection result:`, {
|
||||
workspace: this.currentWorkspace,
|
||||
publicKey: this.connectedPublicKey?.substring(0, 16) + '...',
|
||||
connected: this.isConnected,
|
||||
serverUrl: this.defaultServerUrl
|
||||
});
|
||||
|
||||
// Validate that we have a public key if connected
|
||||
if (this.isConnected && !this.connectedPublicKey) {
|
||||
console.warn('⚠️ Connected but no public key received - this may cause request issues');
|
||||
}
|
||||
|
||||
// Update badge to show current state
|
||||
this.updateBadge();
|
||||
|
||||
if (this.isConnected) {
|
||||
// Clean flow: Connect -> Restore workspace requests -> Update UI
|
||||
console.log(`🔗 Connected to workspace: ${workspaceId}, restoring pending requests...`);
|
||||
|
||||
// 1. Restore requests for this specific workspace
|
||||
await this.restorePendingRequests();
|
||||
|
||||
// 2. Update badge with current count
|
||||
this.updateBadge();
|
||||
|
||||
console.log(`✅ Workspace ${workspaceId} ready with restored requests`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not connected but no error, try again
|
||||
if (attempt < retryCount) {
|
||||
console.log(`⏳ Connection not established, retrying in 1 second...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Check if this is an expected "no workspace" error during startup
|
||||
const isExpectedStartupError = error.message &&
|
||||
(error.message.includes('Workspace not found') ||
|
||||
error.message.includes('no keypairs available'));
|
||||
|
||||
if (isExpectedStartupError && attempt === 1) {
|
||||
console.log(`⏳ SigSocket connection attempt ${attempt}: No active workspace (expected after extension reload)`);
|
||||
}
|
||||
|
||||
// Check if this is a public key related error
|
||||
if (error.message && error.message.includes('public key')) {
|
||||
console.error(`🔑 Public key error detected: ${error.message}`);
|
||||
// For public key errors, don't retry immediately - might need workspace change
|
||||
if (attempt === 1) {
|
||||
console.log(`🔄 Public key error on first attempt, trying to disconnect and reconnect...`);
|
||||
await this.disconnect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
if (attempt < retryCount) {
|
||||
if (!isExpectedStartupError) {
|
||||
console.log(`⏳ Retrying connection in 2 seconds...`);
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
} else {
|
||||
// Final attempt failed
|
||||
this.isConnected = false;
|
||||
this.currentWorkspace = null;
|
||||
this.connectedPublicKey = null;
|
||||
|
||||
if (isExpectedStartupError) {
|
||||
console.log(`ℹ️ SigSocket connection failed: No active workspace. Will connect when user logs in.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`🔗 Requesting SigSocket connection for workspace: ${workspaceId}`);
|
||||
|
||||
// Let WASM handle all connection logic (reuse, switching, etc.)
|
||||
const connectionInfo = await this.wasmModule.SigSocketManager.connect_workspace_with_events(
|
||||
workspaceId,
|
||||
this.defaultServerUrl,
|
||||
(event) => this.handleSigSocketEvent(event)
|
||||
);
|
||||
|
||||
// Parse connection info
|
||||
const info = JSON.parse(connectionInfo);
|
||||
this.currentWorkspace = info.workspace;
|
||||
this.connectedPublicKey = info.public_key;
|
||||
this.isConnected = info.is_connected;
|
||||
|
||||
console.log(`✅ SigSocket connection result:`, {
|
||||
workspace: this.currentWorkspace,
|
||||
publicKey: this.connectedPublicKey?.substring(0, 16) + '...',
|
||||
connected: this.isConnected
|
||||
});
|
||||
|
||||
// Update badge to show current state
|
||||
this.updateBadge();
|
||||
|
||||
return this.isConnected;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ SigSocket connection failed:', error);
|
||||
this.isConnected = false;
|
||||
this.currentWorkspace = null;
|
||||
this.connectedPublicKey = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handle events from the WASM SigSocket client
|
||||
* This is called automatically when requests arrive
|
||||
* @param {Object} event - Event from WASM layer
|
||||
*/
|
||||
handleSigSocketEvent(event) {
|
||||
async handleSigSocketEvent(event) {
|
||||
console.log('📨 Received SigSocket event:', event);
|
||||
|
||||
if (event.type === 'sign_request') {
|
||||
console.log(`🔐 New sign request: ${event.request_id}`);
|
||||
console.log(`🔐 New sign request: ${event.request_id} for workspace: ${this.currentWorkspace}`);
|
||||
|
||||
// The request is automatically stored by WASM
|
||||
// We just handle UI updates
|
||||
this.showSignRequestNotification();
|
||||
this.updateBadge();
|
||||
this.notifyPopupOfNewRequest();
|
||||
// Clean flow: Request arrives -> Store -> Persist -> Update UI
|
||||
try {
|
||||
// 1. Request is automatically stored in WASM (already done by WASM layer)
|
||||
|
||||
// 2. Persist to storage with workspace isolation
|
||||
await this.persistPendingRequests();
|
||||
|
||||
// 3. Update badge count
|
||||
this.updateBadge();
|
||||
|
||||
// 4. Show notification
|
||||
this.showSignRequestNotification();
|
||||
|
||||
// 5. Notify popup if connected
|
||||
this.notifyPopupOfNewRequest();
|
||||
|
||||
console.log(`✅ Request ${event.request_id} processed and stored for workspace: ${this.currentWorkspace}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to process request ${event.request_id}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +277,19 @@ class SigSocketService {
|
||||
throw new Error('WASM SigSocketManager not available');
|
||||
}
|
||||
|
||||
// Check if we're connected before attempting approval
|
||||
if (!this.isConnected) {
|
||||
console.warn(`⚠️ Not connected to SigSocket server, cannot approve request: ${requestId}`);
|
||||
throw new Error('Not connected to SigSocket server');
|
||||
}
|
||||
|
||||
// Verify we can approve this request
|
||||
const canApprove = await this.canApproveRequest(requestId);
|
||||
if (!canApprove) {
|
||||
console.warn(`⚠️ Cannot approve request ${requestId} - keyspace may be locked or request not found`);
|
||||
throw new Error('Cannot approve request - keyspace may be locked or request not found');
|
||||
}
|
||||
|
||||
console.log(`✅ Approving request: ${requestId}`);
|
||||
|
||||
// WASM handles all validation, signing, and server communication
|
||||
@@ -131,14 +297,37 @@ class SigSocketService {
|
||||
|
||||
console.log(`🎉 Request approved successfully: ${requestId}`);
|
||||
|
||||
// Update UI
|
||||
// Clean flow: Approve -> Remove from storage -> Update UI
|
||||
// 1. Remove from persistent storage (WASM already removed it)
|
||||
await this.persistPendingRequests();
|
||||
|
||||
// 2. Update badge count
|
||||
this.updateBadge();
|
||||
|
||||
// 3. Notify popup of updated state
|
||||
this.notifyPopupOfRequestUpdate();
|
||||
|
||||
console.log(`✅ Request ${requestId} approved and cleaned up`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to approve request ${requestId}:`, error);
|
||||
|
||||
// Check if this is a connection-related error
|
||||
if (error.message && (error.message.includes('Connection not found') || error.message.includes('public key'))) {
|
||||
console.error(`🔑 Connection/public key error during approval. Current state:`, {
|
||||
connected: this.isConnected,
|
||||
workspace: this.currentWorkspace,
|
||||
publicKey: this.connectedPublicKey?.substring(0, 16) + '...'
|
||||
});
|
||||
|
||||
// Try to reconnect for next time
|
||||
if (this.currentWorkspace) {
|
||||
console.log(`🔄 Attempting to reconnect to workspace: ${this.currentWorkspace}`);
|
||||
setTimeout(() => this.connectToServer(this.currentWorkspace), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -162,10 +351,17 @@ class SigSocketService {
|
||||
|
||||
console.log(`✅ Request rejected successfully: ${requestId}`);
|
||||
|
||||
// Update UI
|
||||
// Clean flow: Reject -> Remove from storage -> Update UI
|
||||
// 1. Remove from persistent storage (WASM already removed it)
|
||||
await this.persistPendingRequests();
|
||||
|
||||
// 2. Update badge count
|
||||
this.updateBadge();
|
||||
|
||||
// 3. Notify popup of updated state
|
||||
this.notifyPopupOfRequestUpdate();
|
||||
|
||||
console.log(`✅ Request ${requestId} rejected and cleaned up`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
@@ -224,18 +420,54 @@ class SigSocketService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification for new sign request
|
||||
* Show clickable notification for new sign request
|
||||
* Call this AFTER the request has been stored and persisted
|
||||
*/
|
||||
showSignRequestNotification() {
|
||||
async showSignRequestNotification() {
|
||||
try {
|
||||
if (chrome.notifications && chrome.notifications.create) {
|
||||
chrome.notifications.create({
|
||||
// Small delay to ensure request is fully stored
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
console.log(`📢 Preparing notification for new signature request`);
|
||||
|
||||
// Check if keyspace is currently unlocked to customize message
|
||||
let message = 'New signature request received. Click to review and approve.';
|
||||
let title = 'SigSocket Sign Request';
|
||||
|
||||
// Try to determine if keyspace is locked
|
||||
try {
|
||||
const requests = await this.getPendingRequests();
|
||||
const canApprove = requests.length > 0 ? await this.canApproveRequest(requests[0].id) : false;
|
||||
if (!canApprove) {
|
||||
message = 'New signature request received. Click to unlock keyspace and approve.';
|
||||
title = 'SigSocket Request';
|
||||
}
|
||||
} catch (error) {
|
||||
// If we can't check, use generic message
|
||||
message = 'New signature request received. Click to open extension.';
|
||||
}
|
||||
|
||||
// Create clickable notification with unique ID
|
||||
const notificationId = `sigsocket-request-${Date.now()}`;
|
||||
|
||||
const notificationOptions = {
|
||||
type: 'basic',
|
||||
iconUrl: 'icons/icon48.png',
|
||||
title: 'SigSocket Sign Request',
|
||||
message: 'New signature request received. Click to review.'
|
||||
title: title,
|
||||
message: message,
|
||||
requireInteraction: true // Keep notification visible until user interacts
|
||||
};
|
||||
|
||||
console.log(`📢 Creating notification: ${notificationId}`, notificationOptions);
|
||||
|
||||
chrome.notifications.create(notificationId, notificationOptions, (createdId) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error('❌ Failed to create notification:', chrome.runtime.lastError);
|
||||
} else {
|
||||
console.log(`✅ Notification created successfully: ${createdId}`);
|
||||
}
|
||||
});
|
||||
console.log('📢 Notification shown for sign request');
|
||||
} else {
|
||||
console.log('📢 Notifications not available, skipping notification');
|
||||
}
|
||||
@@ -322,6 +554,10 @@ class SigSocketService {
|
||||
this.isConnected = false;
|
||||
this.currentWorkspace = null;
|
||||
this.connectedPublicKey = null;
|
||||
this.lastKnownConnectionState = false;
|
||||
|
||||
// Stop status monitoring
|
||||
this.stopStatusMonitoring();
|
||||
|
||||
this.updateBadge();
|
||||
|
||||
@@ -333,7 +569,50 @@ class SigSocketService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection status from WASM
|
||||
* Clear persisted pending requests from storage
|
||||
*/
|
||||
async clearPersistedRequests() {
|
||||
try {
|
||||
await chrome.storage.local.remove(['sigSocketPendingRequests']);
|
||||
console.log('🗑️ Cleared persisted pending requests from storage');
|
||||
} catch (error) {
|
||||
console.warn('Failed to clear persisted requests:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean workspace switch - clear current workspace requests only
|
||||
*/
|
||||
async cleanWorkspaceSwitch(newWorkspace) {
|
||||
try {
|
||||
console.log(`🔄 Clean workspace switch: ${this.currentWorkspace} -> ${newWorkspace}`);
|
||||
|
||||
// 1. Persist current workspace requests before switching
|
||||
if (this.currentWorkspace && this.isConnected) {
|
||||
await this.persistPendingRequests();
|
||||
console.log(`💾 Saved requests for workspace: ${this.currentWorkspace}`);
|
||||
}
|
||||
|
||||
// 2. Clear WASM state (will be restored for new workspace)
|
||||
if (this.wasmModule?.SigSocketManager) {
|
||||
await this.wasmModule.SigSocketManager.clear_pending_requests();
|
||||
console.log('🧹 Cleared WASM request state');
|
||||
}
|
||||
|
||||
// 3. Reset local state
|
||||
this.currentWorkspace = null;
|
||||
this.connectedPublicKey = null;
|
||||
this.isConnected = false;
|
||||
|
||||
console.log('✅ Workspace switch cleanup completed');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to clean workspace switch:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection status with real connection verification
|
||||
* @returns {Promise<Object>} - Connection status information
|
||||
*/
|
||||
async getStatus() {
|
||||
@@ -348,21 +627,63 @@ class SigSocketService {
|
||||
};
|
||||
}
|
||||
|
||||
// Let WASM provide the authoritative status
|
||||
// Get WASM status first
|
||||
const statusJson = await this.wasmModule.SigSocketManager.get_connection_status();
|
||||
const status = JSON.parse(statusJson);
|
||||
const requests = await this.getPendingRequests();
|
||||
|
||||
return {
|
||||
isConnected: status.is_connected,
|
||||
workspace: status.workspace,
|
||||
publicKey: status.public_key,
|
||||
// Verify connection by trying to get requests (this will fail if not connected)
|
||||
let actuallyConnected = false;
|
||||
let requests = [];
|
||||
|
||||
try {
|
||||
requests = await this.getPendingRequests();
|
||||
// If we can get requests and WASM says connected, we're probably connected
|
||||
actuallyConnected = status.is_connected && Array.isArray(requests);
|
||||
} catch (error) {
|
||||
// If getting requests fails, we're definitely not connected
|
||||
console.warn('Connection verification failed:', error);
|
||||
actuallyConnected = false;
|
||||
}
|
||||
|
||||
// Update our internal state
|
||||
this.isConnected = actuallyConnected;
|
||||
|
||||
if (status.connected_public_key && actuallyConnected) {
|
||||
this.connectedPublicKey = status.connected_public_key;
|
||||
} else {
|
||||
this.connectedPublicKey = null;
|
||||
}
|
||||
|
||||
// If we're disconnected, clear our workspace
|
||||
if (!actuallyConnected) {
|
||||
this.currentWorkspace = null;
|
||||
}
|
||||
|
||||
const statusResult = {
|
||||
isConnected: actuallyConnected,
|
||||
workspace: this.currentWorkspace,
|
||||
publicKey: status.connected_public_key,
|
||||
pendingRequestCount: requests.length,
|
||||
serverUrl: this.defaultServerUrl
|
||||
serverUrl: this.defaultServerUrl,
|
||||
// Clean flow status indicators
|
||||
cleanFlowReady: actuallyConnected && this.currentWorkspace && status.connected_public_key
|
||||
};
|
||||
|
||||
console.log('📊 Clean flow status:', {
|
||||
connected: statusResult.isConnected,
|
||||
workspace: statusResult.workspace,
|
||||
requestCount: statusResult.pendingRequestCount,
|
||||
flowReady: statusResult.cleanFlowReady
|
||||
});
|
||||
|
||||
return statusResult;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to get status:', error);
|
||||
// Clear state on error
|
||||
this.isConnected = false;
|
||||
this.currentWorkspace = null;
|
||||
this.connectedPublicKey = null;
|
||||
return {
|
||||
isConnected: false,
|
||||
workspace: null,
|
||||
@@ -375,33 +696,178 @@ class SigSocketService {
|
||||
|
||||
/**
|
||||
* Set the popup port for communication
|
||||
* @param {chrome.runtime.Port} port - The popup port
|
||||
* @param {chrome.runtime.Port|null} port - The popup port or null to disconnect
|
||||
*/
|
||||
setPopupPort(port) {
|
||||
this.popupPort = port;
|
||||
console.log('📱 Popup connected to SigSocket service');
|
||||
|
||||
if (port) {
|
||||
console.log('📱 Popup connected to SigSocket service');
|
||||
|
||||
// Immediately check connection status when popup opens
|
||||
this.checkConnectionStatusNow();
|
||||
|
||||
// Start monitoring connection status when popup connects
|
||||
this.startStatusMonitoring();
|
||||
} else {
|
||||
console.log('📱 Popup disconnected from SigSocket service');
|
||||
// Stop monitoring when popup disconnects
|
||||
this.stopStatusMonitoring();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when keyspace is unlocked - notify popup of current state
|
||||
* Immediately check and update connection status
|
||||
*/
|
||||
async checkConnectionStatusNow() {
|
||||
try {
|
||||
// Force a fresh connection check
|
||||
const currentStatus = await this.getStatusWithConnectionTest();
|
||||
this.lastKnownConnectionState = currentStatus.isConnected;
|
||||
|
||||
// Notify popup of current status
|
||||
this.notifyPopupOfStatusChange(currentStatus);
|
||||
|
||||
console.log(`🔍 Immediate status check: ${currentStatus.isConnected ? 'Connected' : 'Disconnected'}`);
|
||||
} catch (error) {
|
||||
console.warn('Failed to check connection status immediately:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status with additional connection testing
|
||||
*/
|
||||
async getStatusWithConnectionTest() {
|
||||
const status = await this.getStatus();
|
||||
|
||||
// If WASM claims we're connected, do an additional verification
|
||||
if (status.isConnected) {
|
||||
try {
|
||||
// Try to get connection status again - if this fails, we're not really connected
|
||||
const verifyJson = await this.wasmModule.SigSocketManager.get_connection_status();
|
||||
const verifyStatus = JSON.parse(verifyJson);
|
||||
|
||||
if (!verifyStatus.is_connected) {
|
||||
console.log('🔍 Connection verification failed - marking as disconnected');
|
||||
status.isConnected = false;
|
||||
this.isConnected = false;
|
||||
this.currentWorkspace = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('🔍 Connection test failed - marking as disconnected:', error.message);
|
||||
status.isConnected = false;
|
||||
this.isConnected = false;
|
||||
this.currentWorkspace = null;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start periodic status monitoring to detect connection changes
|
||||
*/
|
||||
startStatusMonitoring() {
|
||||
// Clear any existing monitoring
|
||||
if (this.statusMonitorInterval) {
|
||||
clearInterval(this.statusMonitorInterval);
|
||||
}
|
||||
|
||||
// Check status every 2 seconds when popup is open (more responsive)
|
||||
this.statusMonitorInterval = setInterval(async () => {
|
||||
if (this.popupPort) {
|
||||
try {
|
||||
const currentStatus = await this.getStatusWithConnectionTest();
|
||||
|
||||
// Check if connection status changed
|
||||
if (currentStatus.isConnected !== this.lastKnownConnectionState) {
|
||||
console.log(`🔄 Connection state changed: ${this.lastKnownConnectionState} -> ${currentStatus.isConnected}`);
|
||||
this.lastKnownConnectionState = currentStatus.isConnected;
|
||||
|
||||
// Notify popup of status change
|
||||
this.notifyPopupOfStatusChange(currentStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Status monitoring error:', error);
|
||||
// On error, assume disconnected
|
||||
if (this.lastKnownConnectionState !== false) {
|
||||
console.log('🔄 Status monitoring error - marking as disconnected');
|
||||
this.lastKnownConnectionState = false;
|
||||
this.notifyPopupOfStatusChange({
|
||||
isConnected: false,
|
||||
workspace: null,
|
||||
publicKey: null,
|
||||
pendingRequestCount: 0,
|
||||
serverUrl: this.defaultServerUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Stop monitoring when popup is closed
|
||||
this.stopStatusMonitoring();
|
||||
}
|
||||
}, 2000); // 2 seconds for better responsiveness
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop status monitoring
|
||||
*/
|
||||
stopStatusMonitoring() {
|
||||
if (this.statusMonitorInterval) {
|
||||
clearInterval(this.statusMonitorInterval);
|
||||
this.statusMonitorInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify popup of connection status change
|
||||
* @param {Object} status - Current connection status
|
||||
*/
|
||||
notifyPopupOfStatusChange(status) {
|
||||
if (this.popupPort) {
|
||||
this.popupPort.postMessage({
|
||||
type: 'CONNECTION_STATUS_CHANGED',
|
||||
status: status
|
||||
});
|
||||
console.log(`📡 Notified popup of connection status change: ${status.isConnected ? 'Connected' : 'Disconnected'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when keyspace is unlocked - clean approach to show pending requests
|
||||
*/
|
||||
async onKeypaceUnlocked() {
|
||||
if (!this.popupPort) return;
|
||||
|
||||
try {
|
||||
console.log('🔓 Keyspace unlocked - preparing to show pending requests');
|
||||
|
||||
// 1. Restore any persisted requests for this workspace
|
||||
await this.restorePendingRequests();
|
||||
|
||||
// 2. Get current requests (includes restored + any new ones)
|
||||
const requests = await this.getPendingRequests();
|
||||
const canApprove = requests.length > 0 ? await this.canApproveRequest(requests[0].id) : false;
|
||||
|
||||
this.popupPort.postMessage({
|
||||
type: 'KEYSPACE_UNLOCKED',
|
||||
canApprove,
|
||||
pendingRequests: requests
|
||||
});
|
||||
// 3. Check if we can approve requests (keyspace should be unlocked now)
|
||||
const canApprove = requests.length > 0 ? await this.canApproveRequest(requests[0].id) : true;
|
||||
|
||||
console.log(`🔓 Keyspace unlocked notification sent: ${requests.length} requests, canApprove: ${canApprove}`);
|
||||
// 4. Update badge with current count
|
||||
this.updateBadge();
|
||||
|
||||
// 5. Notify popup if connected
|
||||
if (this.popupPort) {
|
||||
this.popupPort.postMessage({
|
||||
type: 'KEYSPACE_UNLOCKED',
|
||||
canApprove,
|
||||
pendingRequests: requests
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🔓 Keyspace unlocked: ${requests.length} requests ready, canApprove: ${canApprove}`);
|
||||
|
||||
return requests;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to handle keyspace unlock:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user