diff --git a/src/pages/home/home.html b/src/pages/home/home.html index 84676b6..842dd76 100755 --- a/src/pages/home/home.html +++ b/src/pages/home/home.html @@ -34,4 +34,4 @@
- + \ No newline at end of file diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index fb6c9a3..f2d9bad 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -55,7 +55,15 @@ async function onScanSuccess(decodedText: any, decodedResult: any) { html5QrcodeScanner.clear(); const service = await Services.getInstance(); // Call the sendPairingTx function with the extracted sp_address - await service.sendPairingTx(spAddress); + try { + const member = { + sp_addresses: [spAddress], + } + service.connectMember([member]); + // await service.sendPairingTx(spAddress); + } catch (e) { + console.error('Failed to pair:', e); + } } else { console.error('The scanned URL does not contain the sp_address parameter.'); alert('Invalid QR code: sp_address parameter missing.'); diff --git a/src/router.ts b/src/router.ts index 5f809e7..729fc1b 100755 --- a/src/router.ts +++ b/src/router.ts @@ -104,7 +104,6 @@ async function init(): Promise { await services.restoreDevice(device); } await services.restoreProcesses(); - await services.restoreMessages(); if (services.isPaired()) { await navigate('process'); @@ -113,7 +112,14 @@ async function init(): Promise { const urlParams = new URLSearchParams(queryString); const pairingAddress = urlParams.get('sp_address'); if (pairingAddress) { - setTimeout(async () => await services.sendPairingTx(pairingAddress), 2000); + setTimeout(async () => { + // await services.sendPairingTx(pairingAddress) + try { + services.connectMember([{sp_addresses: [pairingAddress]}]); + } catch (e) { + console.error('Failed to pair:', e); + } + }, 2000); } await navigate('home'); } diff --git a/src/service-workers/cache.worker.js b/src/service-workers/cache.worker.js new file mode 100644 index 0000000..42e720c --- /dev/null +++ b/src/service-workers/cache.worker.js @@ -0,0 +1,20 @@ +const addResourcesToCache = async (resources) => { + const cache = await caches.open("v1"); + await cache.addAll(resources); + }; + + self.addEventListener("install", (event) => { + event.waitUntil( + addResourcesToCache([ + "/", + "/index.html", + "/style.css", + "/app.js", + "/image-list.js", + "/star-wars-logo.jpg", + "/gallery/bountyHunters.jpg", + "/gallery/myLittleVader.jpg", + "/gallery/snowTroopers.jpg", + ]), + ); + }); \ No newline at end of file diff --git a/src/service-workers/database.worker.js b/src/service-workers/database.worker.js index fa75692..78cc01c 100755 --- a/src/service-workers/database.worker.js +++ b/src/service-workers/database.worker.js @@ -1,5 +1,3 @@ -import Database from '../services/database.service'; - self.addEventListener('install', (event) => { event.waitUntil(self.skipWaiting()); // Activate worker immediately }); @@ -11,7 +9,6 @@ self.addEventListener('activate', (event) => { // Event listener for messages from clients self.addEventListener('message', async (event) => { const data = event.data; - const db = await Database.getInstance(); if (data.type === 'ADD_OBJECT') { try { @@ -19,26 +16,18 @@ self.addEventListener('message', async (event) => { const db = await openDatabase(); const tx = db.transaction(storeName, 'readwrite'); const store = tx.objectStore(storeName); - await store.put(object); - event.ports[0].postMessage({ status: 'success', message: 'Object added or replaced successfully' }); + + if (key) { + await store.put(object, key); + } else { + await store.put(object); + } + + event.ports[0].postMessage({ status: 'success', message: '' }); } catch (error) { event.ports[0].postMessage({ status: 'error', message: error.message }); } } - - if (data.type === 'GET_OBJECT') { - const { storeName, key } = data.payload; - const db = await openDatabase(); - const tx = db.transaction(storeName, 'readonly'); - const store = tx.objectStore(storeName); - const result = await new Promise((resolve, reject) => { - const getRequest = store.get(key); - getRequest.onsuccess = (event) => resolve(getRequest.result); - getRequest.onerror = (event) => reject(getRequest.error); - }); - - event.ports[0].postMessage({ type: 'GET_OBJECT_RESULT', payload: result }); - } }); async function openDatabase() { diff --git a/src/services/database.service.ts b/src/services/database.service.ts index 5749806..53225b7 100755 --- a/src/services/database.service.ts +++ b/src/services/database.service.ts @@ -17,9 +17,14 @@ class Database { options: { keyPath: 'id' }, indices: [], }, - AnkMessages: { - name: 'messages', - options: { keyPath: 'id' }, + AnkSharedSecrets: { + name: 'shared_secrets', + options: { keyPath: 'key' }, + indices: [], + }, + AnkUnconfirmedSecrets: { + name: 'unconfirmed_secrets', + options: { autoIncrement: true }, indices: [], }, AnkProcessData: { @@ -139,31 +144,52 @@ class Database { console.log('Received response from service worker (GET_OBJECT):', event.data); }; - public addObject(payload: { storeName: string; object: any; key: any }) { - if (this.serviceWorkerRegistration?.active) { - const messageChannel = this.createMessageChannel(this.handleAddObjectResponse); - this.serviceWorkerRegistration.active.postMessage( - { - type: 'ADD_OBJECT', - payload, - }, - [messageChannel.port2], - ); - } + public addObject(payload: { storeName: string; object: any; key: any }): Promise { + return new Promise((resolve, reject) => { + // Check if the service worker is active + if (!this.serviceWorkerRegistration?.active) { + reject(new Error('Service worker is not active')); + return; + } + + // Create a message channel for communication + const messageChannel = new MessageChannel(); + + // Handle the response from the service worker + messageChannel.port1.onmessage = (event) => { + if (event.data.status === 'success') { + resolve(); + } else { + const error = event.data.message; + reject(new Error(error || 'Unknown error occurred while adding object')); + } + }; + + // Send the add object request to the service worker + try { + this.serviceWorkerRegistration.active.postMessage( + { + type: 'ADD_OBJECT', + payload, + }, + [messageChannel.port2], + ); + } catch (error) { + reject(new Error(`Failed to send message to service worker: ${error}`)); + } + }); } - public getObject(storeName: string, key: string) { - if (this.serviceWorkerRegistration?.active) { - const messageChannel = this.createMessageChannel(this.handleGetObjectResponse); - - this.serviceWorkerRegistration.active.postMessage( - { - type: 'GET_OBJECT', - payload: { storeName, key }, - }, - [messageChannel.port2], - ); - } + public async getObject(storeName: string, key: string): Promise { + const db = await this.getDb(); + const tx = db.transaction(storeName, 'readonly'); + const store = tx.objectStore(storeName); + const result = await new Promise((resolve, reject) => { + const getRequest = store.get(key); + getRequest.onsuccess = () => resolve(getRequest.result); + getRequest.onerror = () => reject(getRequest.error); + }); + return result } } diff --git a/src/services/service.ts b/src/services/service.ts index fbbb8db..706ef73 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -103,6 +103,50 @@ export default class Services { return apiReturn; } + public async connectMember(members: Member[]): Promise { + if (members.length === 0) { + throw new Error('Trying to connect to empty members list'); + } + + const members_str = members.map(member => JSON.stringify(member)); + + const waitForAmount = async (): Promise => { + let attempts = 3; + while (attempts > 0) { + const amount = this.getAmount(); + if (amount !== 0n) { + return amount; + } + attempts--; + if (attempts > 0) { + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + } + } + throw new Error('Amount is still 0 after 3 attempts'); + }; + + let availableAmt = this.getAmount(); + if (availableAmt === 0n) { + const faucetMsg = this.createFaucetMessage(); + this.sendFaucetMessage(faucetMsg); + + try { + availableAmt = await waitForAmount(); + } catch (e) { + console.error('Failed to retrieve amount:', e); + throw e; // Rethrow the error if needed + } + } + + try { + const apiReturn = this.sdkClient.create_connect_transaction(members_str, 1); + + await this.handleApiReturn(apiReturn); + } catch (e) { + console.error('Failed to connect:', e); + } + } + public async sendPairingTx(spAddress: string): Promise { const localAddress = this.sdkClient.get_address(); const emptyTxid = '0'.repeat(64); @@ -142,9 +186,10 @@ export default class Services { } } - async sendFaucetMessage(message: string): Promise { - await sendMessage('Faucet', message); + sendFaucetMessage(message: string): void { + sendMessage('Faucet', message); } + async parseCipher(message: string) { try { console.log('parsing new cipher'); @@ -159,7 +204,7 @@ export default class Services { async parseNewTx(tx: string) { try { - const parsedTx = await this.sdkClient.parse_new_tx(tx, 0, 0.0001); + const parsedTx = await this.sdkClient.parse_new_tx(tx, 0); if (parsedTx) { console.log('🚀 ~ Services ~ parseNewTx ~ parsedTx:', parsedTx); try { @@ -176,8 +221,33 @@ export default class Services { } private async handleApiReturn(apiReturn: ApiReturn) { - if (apiReturn.ciphers_to_send && apiReturn.ciphers_to_send.length) { - await this.sendCipherMessages(apiReturn.ciphers_to_send); + if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) { + await this.sendNewTxMessage(JSON.stringify(apiReturn.new_tx_to_send)); + } + + if (apiReturn.secrets) { + const unconfirmedSecrets = apiReturn.secrets.unconfirmed_secrets; + const confirmedSecrets = apiReturn.secrets.shared_secrets; + + console.log('confirmedSecrets:', confirmedSecrets); + const db = await Database.getInstance(); + for (const secret of unconfirmedSecrets) { + db.addObject({ + storeName: 'unconfirmed_secrets', + object: secret, + key: null, + }); + } + const entries = Object.entries(confirmedSecrets).map(([key, value]) => ({ key, value })); + for (const entry of entries) { + console.log('entry:', entry); + + db.addObject({ + storeName: 'shared_secrets', + object: entry, + key: null, + }); + } } setTimeout(async () => { @@ -207,27 +277,17 @@ export default class Services { } } - if (apiReturn.updated_cached_msg && apiReturn.updated_cached_msg.length) { - apiReturn.updated_cached_msg.forEach(async (msg, index) => { - // console.debug(`CachedMessage ${index}:`, msg); - // Save the message to local storage - localStorage.setItem(msg.id.toString(), JSON.stringify(msg)); - const db = await Database.getInstance(); - db.addObject({ - storeName: 'messages', - object: { id: msg.id.toString(), msg }, - key: msg.id.toString(), - }); - }); - } - if (apiReturn.commit_to_send) { const commit = apiReturn.commit_to_send; await this.sendCommitMessage(JSON.stringify(commit)); } - if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) { - await this.sendNewTxMessage(JSON.stringify(apiReturn.new_tx_to_send)); + if (apiReturn.decrypted_pcds && apiReturn.decrypted_pcds.length != 0) { + // TODO + } + + if (apiReturn.ciphers_to_send && apiReturn.ciphers_to_send.length != 0) { + await this.sendCipherMessages(apiReturn.ciphers_to_send); } }, 0); } @@ -281,8 +341,8 @@ export default class Services { } } - async getAmount(): Promise { - const amount = await this.sdkClient.get_available_amount(); + public getAmount(): BigInt { + const amount = this.sdkClient.get_available_amount(); return amount; } @@ -297,18 +357,30 @@ export default class Services { async saveDevice(device: any): Promise { const db = await Database.getInstance(); - db.addObject({ - storeName: 'wallet', - object: { pre_id: '1', device }, - key: '1', - }); - localStorage.setItem('wallet', device); + try { + await db.addObject({ + storeName: 'wallet', + object: { pre_id: '1', device }, + key: null, + }); + } catch (e) { + console.error(e); + } } async getDevice(): Promise { const db = await Database.getInstance(); - db.getObject('wallet', '1'); - return localStorage.getItem('wallet'); + try { + const dbRes = await db.getObject('wallet', '1'); + if (dbRes) { + const wallet = dbRes['device']; + return wallet; + } else { + return null; + } + } catch (e) { + throw new Error(`Failed to retrieve device from db: ${e}`); + } } async dumpWallet() { @@ -317,8 +389,8 @@ export default class Services { return wallet; } - async createFaucetMessage() { - const message = await this.sdkClient.create_faucet_msg(); + public createFaucetMessage() { + const message = this.sdkClient.create_faucet_msg(); console.log('🚀 ~ Services ~ createFaucetMessage ~ message:', message); return message; } @@ -447,38 +519,6 @@ export default class Services { return hexObjects; } - private getCachedMessages(): string[] { - const u32KeyRegex = /^\d+$/; - const U32_MAX = 4294967295; - const messages: string[] = []; - - for (let i = 0; i < localStorage.length; i++) { - const key = localStorage.key(i); - - if (!key) { - return messages; - } - - if (u32KeyRegex.test(key)) { - const num = parseInt(key, 10); - if (num < 0 || num > U32_MAX) { - console.warn(`Key ${key} is outside the u32 range and will be ignored.`); - continue; - } - - const value = localStorage.getItem(key); - if (!value) { - console.warn(`No value found for key: ${key}`); - continue; - } - - messages.push(value); - } - } - - return messages; - } - public async restoreProcesses() { const processesCache = this.getProcessesCache(); console.log('🚀 ~ Services ~ restoreProcesses ~ processesCache:', processesCache); @@ -495,19 +535,6 @@ export default class Services { } } - public async restoreMessages() { - const cachedMessages = this.getCachedMessages(); - console.log('🚀 ~ Services ~ restoreMessages ~ chachedMessages:', cachedMessages); - - if (cachedMessages && cachedMessages.length != 0) { - try { - await this.sdkClient.set_message_cache(cachedMessages); - } catch (e) { - console.error('Services ~ restoreMessages ~ Error:', e); - } - } - } - getNotifications(): INotification[] { return [ { diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index e05471a..93b5b4d 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -148,7 +148,15 @@ export function initAddressInput() { async function onOkButtonClick() { const service = await Services.getInstance(); const addressInput = (document.getElementById('addressInput') as HTMLInputElement).value; - await service.sendPairingTx(addressInput); + const member = { + sp_addresses: [addressInput], + } + try { + service.connectMember([member]); + // await service.sendPairingTx(addressInput); + } catch (e) { + console.error('onOkButtonClick error:', e); + } } export async function generateQRCode(spAddress: string) { diff --git a/src/websockets.ts b/src/websockets.ts index db5460c..175ffca 100755 --- a/src/websockets.ts +++ b/src/websockets.ts @@ -3,21 +3,13 @@ import Services from './services/service'; let ws: WebSocket; let messageQueue: string[] = []; -let services: Services; export async function initWebsocket(url: string) { ws = new WebSocket(url); - services = await Services.getInstance(); if (ws !== null) { ws.onopen = async (event) => { console.log('WebSocket connection established'); - const amount = await services.getAmount(); - - if (amount === 0n) { - const faucetMsg = await services.createFaucetMessage(); - await services.sendFaucetMessage(faucetMsg); - } while (messageQueue.length > 0) { const message = messageQueue.shift(); if (message) { @@ -36,6 +28,7 @@ export async function initWebsocket(url: string) { try { const feeRate = 0.0001; const parsedMessage = JSON.parse(msgData); + const services = await Services.getInstance(); switch (parsedMessage.flag) { case 'NewTx': await services.parseNewTx(parsedMessage.content); @@ -66,7 +59,7 @@ export async function initWebsocket(url: string) { } // Method to send messages -export async function sendMessage(flag: AnkFlag, message: string): Promise { +export function sendMessage(flag: AnkFlag, message: string): void { if (ws.readyState === WebSocket.OPEN) { const networkMessage = { flag: flag,