import Services from "./services"; import { AnkFlag, AnkNetworkMsg, CachedMessage } from "../dist/pkg/sdk_client"; class WebSocketClient { private ws: WebSocket; private messageQueue: string[] = []; constructor(url: string, private services: Services) { this.ws = new WebSocket(url); this.ws.addEventListener('open', (event) => { console.log('WebSocket connection established'); // Once the connection is open, send all messages in the queue while (this.messageQueue.length > 0) { const message = this.messageQueue.shift(); if (message) { this.ws.send(message); } } }); // Listen for messages this.ws.addEventListener('message', (event) => { const msgData = event.data; (async () => { if (typeof(msgData) === 'string') { console.log("Received text message: "+msgData); try { const feeRate = 1; // By parsing the message, we can link it with existing cached message and return the updated version of the message let res: CachedMessage = await services.parseNetworkMessage(msgData, feeRate); console.debug(res); if (res.status === 'FaucetComplete') { // we received a faucet tx, there's nothing else to do window.alert(`New faucet output\n${res.commited_in}`); await services.updateMessages(res); await services.updateOwnedOutputsForUser(); } else if (res.status === 'TxWaitingCipher') { // we received a tx but we don't have the cipher console.debug(`received notification in output ${res.commited_in}, waiting for cipher message`); await services.updateMessages(res); await services.updateOwnedOutputsForUser(); } else if (res.status === 'CipherWaitingTx') { // we received a cipher but we don't have the key console.debug(`received a cipher`); await services.updateMessages(res); } else if (res.status === 'SentWaitingConfirmation') { // We are sender and we're waiting for the challenge that will confirm recipient got the transaction and the message await services.updateMessages(res); await services.updateOwnedOutputsForUser(); } else if (res.status === 'MustSpendConfirmation') { // we received a challenge for a notification we made // that means we can stop rebroadcasting the tx and we must spend the challenge to confirm window.alert(`Spending ${res.confirmed_by} to prove our identity`); console.debug(`sending confirm message to ${res.recipient}`); await services.updateMessages(res); await services.answer_confirmation_message(res); } else if (res.status === 'ReceivedMustConfirm') { // we found a notification and decrypted the cipher window.alert(`Received message from ${res.sender}\n${res.plaintext}`); // we must spend the commited_in output to sender await services.updateMessages(res); await services.confirm_sender_address(res); } else if (res.status === 'Complete') { window.alert(`Received confirmation that ${res.sender} is the author of message ${res.plaintext}`) await services.updateMessages(res); await services.updateOwnedOutputsForUser(); } else { console.debug('Received an unimplemented valid message'); } } catch (error) { console.error('Received an invalid message:', error); } } else { console.error('Received a non-string message'); } })(); }); // Listen for possible errors this.ws.addEventListener('error', (event) => { console.error('WebSocket error:', event); }); // Listen for when the connection is closed this.ws.addEventListener('close', (event) => { console.log('WebSocket is closed now.'); }); } // Method to send messages public sendMessage(flag: AnkFlag, message: string): void { if (this.ws.readyState === WebSocket.OPEN) { const networkMessage: AnkNetworkMsg = { 'flag': flag, 'content': message } // console.debug("Sending message:", JSON.stringify(networkMessage)); this.ws.send(JSON.stringify(networkMessage)); } else { console.warn('WebSocket is not open. ReadyState:', this.ws.readyState); this.messageQueue.push(message); } } public getUrl(): string { return this.ws.url; } // Method to close the WebSocket connection public close(): void { this.ws.close(); } } export { WebSocketClient };