From bc562f254e0f0e3c4b34025b0bcdcc26f8fc0886 Mon Sep 17 00:00:00 2001 From: status-im-auto Date: Fri, 27 Jan 2023 22:48:39 +0000 Subject: [PATCH] Updates --- noise-js/index.html | 2 +- noise-rtc/index.html | 1 - noise-rtc/index.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/noise-js/index.html b/noise-js/index.html index c541407..5f87ff4 100644 --- a/noise-js/index.html +++ b/noise-js/index.html @@ -37,7 +37,7 @@ } .container { - width: 800px; + width: 100%; min-width: 300px; max-width: 800px; height: 100%; diff --git a/noise-rtc/index.html b/noise-rtc/index.html index 9149c19..fcaaa79 100644 --- a/noise-rtc/index.html +++ b/noise-rtc/index.html @@ -189,7 +189,6 @@ type="text" > - diff --git a/noise-rtc/index.js b/noise-rtc/index.js index 53f6dec..b65e506 100644 --- a/noise-rtc/index.js +++ b/noise-rtc/index.js @@ -16,7 +16,7 @@ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var js_waku_lib_create_waku__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! js-waku/lib/create_waku */ \"./node_modules/js-waku/dist/lib/create_waku.js\");\n/* harmony import */ var js_waku__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! js-waku */ \"./node_modules/js-waku/dist/index.js\");\n/* harmony import */ var js_waku_lib_wait_for_remote_peer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! js-waku/lib/wait_for_remote_peer */ \"./node_modules/js-waku/dist/lib/wait_for_remote_peer.js\");\n/* harmony import */ var js_waku_lib_predefined_bootstrap_nodes__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! js-waku/lib/predefined_bootstrap_nodes */ \"./node_modules/js-waku/dist/lib/predefined_bootstrap_nodes.js\");\n/* harmony import */ var js_waku_lib_peer_discovery_static_list__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! js-waku/lib/peer_discovery_static_list */ \"./node_modules/js-waku/dist/lib/peer_discovery_static_list.js\");\n/* harmony import */ var _waku_noise__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @waku/noise */ \"./node_modules/@waku/noise/dist/index.js\");\n/* harmony import */ var protobufjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! protobufjs */ \"./node_modules/protobufjs/index.js\");\n/* harmony import */ var protobufjs__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(protobufjs__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var qrcode__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! qrcode */ \"./node_modules/qrcode/lib/browser.js\");\n\n\n\n\n\n\n\n\n\n\n// Protobuf\nconst ProtoMessage = new (protobufjs__WEBPACK_IMPORTED_MODULE_6___default().Type)(\"Message\").add(\n new (protobufjs__WEBPACK_IMPORTED_MODULE_6___default().Field)(\"data\", 3, \"string\")\n);\n\nmain();\n\nasync function main() {\n const ui = initUI();\n ui.waku.connecting();\n\n // Starting the node\n const node = await (0,js_waku_lib_create_waku__WEBPACK_IMPORTED_MODULE_0__.createLightNode)({\n libp2p: {\n peerDiscovery: [\n new js_waku_lib_peer_discovery_static_list__WEBPACK_IMPORTED_MODULE_4__.PeerDiscoveryStaticPeers((0,js_waku_lib_predefined_bootstrap_nodes__WEBPACK_IMPORTED_MODULE_3__.getPredefinedBootstrapNodes)(js_waku_lib_predefined_bootstrap_nodes__WEBPACK_IMPORTED_MODULE_3__.Fleet.Test)),\n ],\n },\n });\n\n try {\n await node.start();\n await (0,js_waku_lib_wait_for_remote_peer__WEBPACK_IMPORTED_MODULE_2__.waitForRemotePeer)(node, [js_waku__WEBPACK_IMPORTED_MODULE_1__.Protocols.Filter, js_waku__WEBPACK_IMPORTED_MODULE_1__.Protocols.LightPush]);\n\n ui.waku.connected();\n\n const [sender, responder] = getSenderAndResponder(node);\n const myStaticKey = _waku_noise__WEBPACK_IMPORTED_MODULE_5__.generateX25519KeyPair();\n const urlPairingInfo = getPairingInfoFromURL();\n\n const pairingObj = new _waku_noise__WEBPACK_IMPORTED_MODULE_5__.WakuPairing(\n sender,\n responder,\n myStaticKey,\n urlPairingInfo || new _waku_noise__WEBPACK_IMPORTED_MODULE_5__.ResponderParameters()\n );\n const pExecute = pairingObj.execute(120000); // timeout after 2m\n\n scheduleHandshakeAuthConfirmation(pairingObj, ui);\n\n let sendWakuMessage;\n let listenToWakuMessages;\n\n try {\n ui.handshake.waiting();\n\n if (!urlPairingInfo) {\n const pairingURL = buildPairingURLFromObj(pairingObj);\n ui.shareInfo.setURL(pairingURL);\n ui.shareInfo.renderQR(pairingURL);\n ui.shareInfo.show();\n }\n\n [sendWakuMessage, listenToWakuMessages] = await buildWakuMessage(\n node,\n pExecute\n );\n\n ui.handshake.connected();\n ui.shareInfo.hide();\n } catch (err) {\n ui.handshake.error(err.message);\n ui.hide();\n }\n\n /*\n // The information needs to be backed up to decrypt messages sent with\n // codecs generated with the handshake. The `handshakeResult` variable\n // contains private information that needs to be stored safely\n const contentTopic = pairingObj.contentTopic;\n const handshakeResult = pairingObj.getHandshakeResult();\n // To restore the codecs for decrypting older messages, or continuing an existing\n // session, use this:\n [encoder, decoder] = WakuPairing.getSecureCodec(contentTopic, handshakeResult);\n */\n ui.message.display();\n\n const { peerConnection, sendMessage: sendRTCMessage } = initRTC({\n ui,\n onReceive: ui.message.onReceive.bind(ui.message),\n });\n\n peerConnection.onicecandidate = async (event) => {\n if (event.candidate) {\n console.log(\"candidate sent\");\n try {\n // if (!peerConnection.remoteDescription) return;\n ui.rtc.sendingCandidate();\n await sendWakuMessage({\n type: \"candidate\",\n candidate: event.candidate,\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n }\n };\n\n const sendOffer = async () => {\n console.log(\"offer sent\");\n ui.rtc.sendingOffer();\n\n try {\n const offer = await peerConnection.createOffer();\n await peerConnection.setLocalDescription(offer);\n\n await sendWakuMessage({\n type: \"offer\",\n offer,\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const sendAnswer = async (data) => {\n console.log(\"answer sent\");\n ui.rtc.sendingAnswer();\n try {\n await peerConnection.setRemoteDescription(\n new RTCSessionDescription(data.offer)\n );\n\n const answer = await peerConnection.createAnswer();\n peerConnection.setLocalDescription(answer);\n\n await sendWakuMessage({\n type: \"answer\",\n answer,\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const receiveAnswer = async (data) => {\n try {\n console.log(\"answer received\");\n await peerConnection.setRemoteDescription(\n new RTCSessionDescription(data.answer)\n );\n console.log(\"answer saved\");\n\n await sendWakuMessage({\n type: \"ready\",\n text: \"received answer\",\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const receiveCandidate = async (data) => {\n try {\n // if (!peerConnection.pendingRemoteDescription) return;\n console.log(\"candidate saved\");\n await peerConnection.addIceCandidate(\n new RTCIceCandidate(data.candidate)\n );\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const handleWakuMessages = async (data) => {\n if (data.type === \"offer\") {\n await sendAnswer(data);\n }\n\n if (data.type === \"answer\") {\n await receiveAnswer(data);\n }\n\n if (data.type === \"ready\") {\n console.log(\"RTC: partner is\", data.text);\n }\n\n if (data.type === \"candidate\") {\n await receiveCandidate(data);\n }\n };\n\n await listenToWakuMessages(handleWakuMessages);\n ui.message.onSend(sendRTCMessage);\n\n // if we are initiator of Noise handshake\n // let's initiate Web RTC as well\n if (!urlPairingInfo) {\n await sendOffer();\n }\n } catch (err) {\n ui.waku.error(err.message);\n ui.hide();\n }\n}\n\nfunction buildPairingURLFromObj(pairingObj) {\n const pInfo = pairingObj.getPairingInfo();\n\n // Data to encode in the QR code. The qrMessageNametag too to the QR string (separated by )\n const messageNameTagParam = `messageNameTag=${js_waku__WEBPACK_IMPORTED_MODULE_1__.utils.bytesToHex(\n pInfo.qrMessageNameTag\n )}`;\n const qrCodeParam = `qrCode=${encodeURIComponent(pInfo.qrCode)}`;\n\n return `${window.location.href}?${messageNameTagParam}&${qrCodeParam}`;\n}\n\nfunction getPairingInfoFromURL() {\n const urlParams = new URLSearchParams(window.location.search);\n\n const messageNameTag = urlParams.get(\"messageNameTag\");\n const qrCodeString = urlParams.get(\"qrCode\");\n\n if (!(messageNameTag && qrCodeString)) {\n return undefined;\n }\n\n return new _waku_noise__WEBPACK_IMPORTED_MODULE_5__.InitiatorParameters(\n decodeURIComponent(qrCodeString),\n js_waku__WEBPACK_IMPORTED_MODULE_1__.utils.hexToBytes(messageNameTag)\n );\n}\n\nfunction getSenderAndResponder(node) {\n const sender = {\n async publish(encoder, msg) {\n await node.lightPush.push(encoder, msg);\n },\n };\n\n const msgQueue = new Array();\n const subscriptions = new Map();\n const intervals = new Map();\n\n const responder = {\n async subscribe(decoder) {\n const subscription = await node.filter.subscribe(\n [decoder],\n (wakuMessage) => {\n msgQueue.push(wakuMessage);\n }\n );\n subscriptions.set(decoder.contentTopic, subscription);\n },\n async nextMessage(contentTopic) {\n if (msgQueue.length != 0) {\n const oldestMsg = msgQueue.shift();\n if (oldestMsg.contentTopic === contentTopic) {\n return oldestMsg;\n }\n }\n\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n if (msgQueue.length != 0) {\n clearInterval(interval);\n const oldestMsg = msgQueue.shift();\n if (oldestMsg.contentTopic === contentTopic) {\n resolve(oldestMsg);\n }\n }\n }, 100);\n intervals.set(contentTopic, interval);\n });\n },\n async stop(contentTopic) {\n if (intervals.has(contentTopic)) {\n clearInterval(intervals.get(contentTopic));\n intervals.delete(contentTopic);\n }\n if (subscriptions.has(contentTopic)) {\n await subscriptions.get(contentTopic)();\n subscriptions.delete(contentTopic);\n } else {\n console.log(\"Subscriptipon doesnt exist\");\n }\n },\n };\n\n return [sender, responder];\n}\n\nasync function scheduleHandshakeAuthConfirmation(pairingObj, ui) {\n const authCode = await pairingObj.getAuthCode();\n ui.handshake.connecting();\n pairingObj.validateAuthCode(confirm(\"Confirm that authcode is: \" + authCode));\n}\n\nasync function buildWakuMessage(node, noiseExecute) {\n const [encoder, decoder] = await noiseExecute;\n\n const sendMessage = async (message) => {\n let payload = ProtoMessage.create({\n // data: utils.utf8ToBytes(JSON.stringify(message)),\n data: JSON.stringify(message),\n });\n payload = ProtoMessage.encode(payload).finish();\n\n return node.lightPush.push(encoder, { payload });\n };\n\n const listenToMessages = async (fn) => {\n return node.filter.subscribe([decoder], ({ payload }) => {\n const { data } = ProtoMessage.decode(payload);\n // fn(JSON.parse(utils.bytesToUtf8(data)));\n fn(JSON.parse(data));\n });\n };\n\n return [sendMessage, listenToMessages];\n}\n\nfunction initRTC({ ui, onReceive }) {\n const configuration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n };\n const peerConnection = new RTCPeerConnection(configuration);\n const sendChannel = peerConnection.createDataChannel(\"chat\");\n\n let receiveChannel;\n\n sendChannel.onopen = (event) => {\n ui.rtc.ready();\n console.log(\"onopen send\", event);\n };\n\n peerConnection.ondatachannel = (event) => {\n receiveChannel = event.channel;\n\n receiveChannel.onmessage = (event) => {\n onReceive(JSON.parse(event.data));\n };\n\n receiveChannel.onopen = (event) => {\n ui.rtc.ready();\n console.log(\"onopen receive\", event);\n };\n };\n\n const sendMessage = (text, nick) => {\n sendChannel.send(JSON.stringify({ text, nick, timestamp: Date.now() }));\n };\n\n return {\n peerConnection,\n sendChannel,\n receiveChannel,\n sendMessage,\n };\n}\n\nfunction initUI() {\n const messagesList = document.getElementById(\"messages\");\n const nicknameInput = document.getElementById(\"nick-input\");\n const textInput = document.getElementById(\"text-input\");\n const sendButton = document.getElementById(\"send-btn\");\n const chatArea = document.getElementById(\"chat-area\");\n const wakuStatusSpan = document.getElementById(\"waku-status\");\n const handshakeStatusSpan = document.getElementById(\"handshake-status\");\n\n const qrCanvas = document.getElementById(\"qr-canvas\");\n const qrUrlContainer = document.getElementById(\"qr-url-container\");\n const qrUrl = document.getElementById(\"qr-url\");\n const copyURLButton = document.getElementById(\"copy-url\");\n const openTabButton = document.getElementById(\"open-tab\");\n\n const rtcStatus = document.getElementById(\"rtc-status\");\n const connectChat = document.getElementById(\"connect-chat-btn\");\n\n copyURLButton.onclick = () => {\n const copyText = document.getElementById(\"qr-url\"); // need to get it each time otherwise copying does not work\n copyText.select();\n copyText.setSelectionRange(0, 99999);\n navigator.clipboard.writeText(copyText.value);\n };\n\n openTabButton.onclick = () => {\n window.open(qrUrl.value, \"_blank\");\n };\n\n const disableChatUIStateIfNeeded = () => {\n const readyToSend = nicknameInput.value !== \"\";\n textInput.disabled = !readyToSend;\n sendButton.disabled = !readyToSend;\n };\n nicknameInput.onchange = disableChatUIStateIfNeeded;\n nicknameInput.onblur = disableChatUIStateIfNeeded;\n\n return {\n shareInfo: {\n setURL(url) {\n qrUrl.value = url;\n },\n hide() {\n qrUrlContainer.style.display = \"none\";\n },\n show() {\n qrUrlContainer.style.display = \"flex\";\n },\n renderQR(url) {\n qrcode__WEBPACK_IMPORTED_MODULE_7__.toCanvas(qrCanvas, url, (err) => {\n if (err) {\n throw err;\n }\n });\n },\n },\n waku: {\n _val(msg) {\n wakuStatusSpan.innerText = msg;\n },\n _class(name) {\n wakuStatusSpan.className = name;\n },\n connecting() {\n this._val(\"connecting...\");\n this._class(\"progress\");\n },\n connected() {\n this._val(\"connected\");\n this._class(\"success\");\n },\n error(msg) {\n this._val(msg);\n this._class(\"error\");\n },\n },\n handshake: {\n _val(val) {\n handshakeStatusSpan.innerText = val;\n },\n _class(name) {\n handshakeStatusSpan.className = name;\n },\n error(msg) {\n this._val(msg);\n this._class(\"error\");\n },\n waiting() {\n this._val(\"waiting for handshake...\");\n this._class(\"progress\");\n },\n generating() {\n this._val(\"generating QR code...\");\n this._class(\"progress\");\n },\n connecting() {\n this._val(\"executing handshake...\");\n this._class(\"progress\");\n },\n connected() {\n this._val(\"handshake completed!\");\n this._class(\"success\");\n },\n },\n message: {\n _render({ time, text, nick }) {\n messagesList.innerHTML += `\n
  • \n (${nick})\n ${text}\n [${new Date(time).toISOString()}]\n
  • \n `;\n },\n _status(text, className) {\n sendButton.className = className;\n },\n onReceive(data) {\n const { timestamp, nick, text } = data;\n\n this._render({\n nick,\n time: timestamp * 1000,\n text,\n });\n },\n onSend(cb) {\n sendButton.addEventListener(\"click\", async () => {\n try {\n this._status(\"sending...\", \"progress\");\n await cb(textInput.value, nicknameInput.value);\n this._status(\"sent\", \"success\");\n\n this._render({\n time: Date.now(), // a bit different from what receiver will see but for the matter of example is good enough\n text: textInput.value,\n nick: nicknameInput.value,\n });\n textInput.value = \"\";\n } catch (e) {\n this._status(`error: ${e.message}`, \"error\");\n }\n });\n },\n display() {\n chatArea.style.display = \"block\";\n this._status(\"waiting for input\", \"progress\");\n },\n },\n rtc: {\n _val(msg) {\n rtcStatus.innerText = msg;\n },\n _class(name) {\n rtcStatus.className = name;\n },\n sendingOffer() {\n this._val(\"sending offer\");\n this._class(\"progress\");\n },\n sendingAnswer() {\n this._val(\"sending answer\");\n this._class(\"progress\");\n },\n sendingCandidate() {\n this._val(\"sending ice candidate\");\n this._class(\"progress\");\n },\n ready() {\n this._val(\"ready\");\n this._class(\"success\");\n },\n error(msg) {\n this._val(msg);\n this._class(\"error\");\n },\n onConnect(cb) {\n connectChat.addEventListener(\"click\", cb);\n },\n },\n hide() {\n this.shareInfo.hide();\n chatArea.style.display = \"none\";\n },\n };\n}\n\n\n//# sourceURL=webpack://@waku/noise-rtc/./index.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var js_waku_lib_create_waku__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! js-waku/lib/create_waku */ \"./node_modules/js-waku/dist/lib/create_waku.js\");\n/* harmony import */ var js_waku__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! js-waku */ \"./node_modules/js-waku/dist/index.js\");\n/* harmony import */ var js_waku_lib_wait_for_remote_peer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! js-waku/lib/wait_for_remote_peer */ \"./node_modules/js-waku/dist/lib/wait_for_remote_peer.js\");\n/* harmony import */ var js_waku_lib_predefined_bootstrap_nodes__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! js-waku/lib/predefined_bootstrap_nodes */ \"./node_modules/js-waku/dist/lib/predefined_bootstrap_nodes.js\");\n/* harmony import */ var js_waku_lib_peer_discovery_static_list__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! js-waku/lib/peer_discovery_static_list */ \"./node_modules/js-waku/dist/lib/peer_discovery_static_list.js\");\n/* harmony import */ var _waku_noise__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @waku/noise */ \"./node_modules/@waku/noise/dist/index.js\");\n/* harmony import */ var protobufjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! protobufjs */ \"./node_modules/protobufjs/index.js\");\n/* harmony import */ var protobufjs__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(protobufjs__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var qrcode__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! qrcode */ \"./node_modules/qrcode/lib/browser.js\");\n\n\n\n\n\n\n\n\n\n\n// Protobuf\nconst ProtoMessage = new (protobufjs__WEBPACK_IMPORTED_MODULE_6___default().Type)(\"Message\").add(\n new (protobufjs__WEBPACK_IMPORTED_MODULE_6___default().Field)(\"data\", 3, \"string\")\n);\n\nmain();\n\nasync function main() {\n const ui = initUI();\n ui.waku.connecting();\n\n // Starting the node\n const node = await (0,js_waku_lib_create_waku__WEBPACK_IMPORTED_MODULE_0__.createLightNode)({ defaultBootstrap: true });\n\n try {\n await node.start();\n await (0,js_waku_lib_wait_for_remote_peer__WEBPACK_IMPORTED_MODULE_2__.waitForRemotePeer)(node, [js_waku__WEBPACK_IMPORTED_MODULE_1__.Protocols.Filter, js_waku__WEBPACK_IMPORTED_MODULE_1__.Protocols.LightPush]);\n\n ui.waku.connected();\n\n const [sender, responder] = getSenderAndResponder(node);\n const myStaticKey = _waku_noise__WEBPACK_IMPORTED_MODULE_5__.generateX25519KeyPair();\n const urlPairingInfo = getPairingInfoFromURL();\n\n const pairingObj = new _waku_noise__WEBPACK_IMPORTED_MODULE_5__.WakuPairing(\n sender,\n responder,\n myStaticKey,\n urlPairingInfo || new _waku_noise__WEBPACK_IMPORTED_MODULE_5__.ResponderParameters()\n );\n const pExecute = pairingObj.execute(120000); // timeout after 2m\n\n scheduleHandshakeAuthConfirmation(pairingObj, ui);\n\n let sendWakuMessage;\n let listenToWakuMessages;\n\n try {\n ui.handshake.waiting();\n\n if (!urlPairingInfo) {\n const pairingURL = buildPairingURLFromObj(pairingObj);\n ui.shareInfo.setURL(pairingURL);\n ui.shareInfo.renderQR(pairingURL);\n ui.shareInfo.show();\n }\n\n [sendWakuMessage, listenToWakuMessages] = await buildWakuMessage(\n node,\n pExecute\n );\n\n ui.handshake.connected();\n ui.shareInfo.hide();\n } catch (err) {\n ui.handshake.error(err.message);\n ui.hide();\n }\n\n ui.message.display();\n\n const { peerConnection, sendMessage: sendRTCMessage } = initRTC({\n ui,\n onReceive: ui.message.onReceive.bind(ui.message),\n });\n\n peerConnection.onicecandidate = async (event) => {\n if (event.candidate) {\n console.log(\"candidate sent\");\n try {\n ui.rtc.sendingCandidate();\n await sendWakuMessage({\n type: \"candidate\",\n candidate: event.candidate,\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n }\n };\n\n const sendOffer = async () => {\n console.log(\"offer sent\");\n ui.rtc.sendingOffer();\n\n try {\n const offer = await peerConnection.createOffer();\n await peerConnection.setLocalDescription(offer);\n\n await sendWakuMessage({\n type: \"offer\",\n offer,\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const sendAnswer = async (data) => {\n console.log(\"answer sent\");\n ui.rtc.sendingAnswer();\n try {\n await peerConnection.setRemoteDescription(\n new RTCSessionDescription(data.offer)\n );\n\n const answer = await peerConnection.createAnswer();\n peerConnection.setLocalDescription(answer);\n\n await sendWakuMessage({\n type: \"answer\",\n answer,\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const receiveAnswer = async (data) => {\n try {\n console.log(\"answer received\");\n await peerConnection.setRemoteDescription(\n new RTCSessionDescription(data.answer)\n );\n console.log(\"answer saved\");\n\n await sendWakuMessage({\n type: \"ready\",\n text: \"received answer\",\n });\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const receiveCandidate = async (data) => {\n try {\n console.log(\"candidate saved\");\n await peerConnection.addIceCandidate(\n new RTCIceCandidate(data.candidate)\n );\n } catch (error) {\n ui.rtc.error(error.message);\n }\n };\n\n const handleWakuMessages = async (data) => {\n if (data.type === \"offer\") {\n await sendAnswer(data);\n }\n\n if (data.type === \"answer\") {\n await receiveAnswer(data);\n }\n\n if (data.type === \"ready\") {\n console.log(\"RTC: partner is\", data.text);\n }\n\n if (data.type === \"candidate\") {\n await receiveCandidate(data);\n }\n };\n\n await listenToWakuMessages(handleWakuMessages);\n ui.message.onSend(sendRTCMessage);\n\n // if we are initiator of Noise handshake\n // let's initiate Web RTC as well\n if (!urlPairingInfo) {\n await sendOffer();\n }\n } catch (err) {\n ui.waku.error(err.message);\n ui.hide();\n }\n}\n\nfunction buildPairingURLFromObj(pairingObj) {\n const pInfo = pairingObj.getPairingInfo();\n\n // Data to encode in the QR code. The qrMessageNametag too to the QR string (separated by )\n const messageNameTagParam = `messageNameTag=${js_waku__WEBPACK_IMPORTED_MODULE_1__.utils.bytesToHex(\n pInfo.qrMessageNameTag\n )}`;\n const qrCodeParam = `qrCode=${encodeURIComponent(pInfo.qrCode)}`;\n\n return `${window.location.href}?${messageNameTagParam}&${qrCodeParam}`;\n}\n\nfunction getPairingInfoFromURL() {\n const urlParams = new URLSearchParams(window.location.search);\n\n const messageNameTag = urlParams.get(\"messageNameTag\");\n const qrCodeString = urlParams.get(\"qrCode\");\n\n if (!(messageNameTag && qrCodeString)) {\n return undefined;\n }\n\n return new _waku_noise__WEBPACK_IMPORTED_MODULE_5__.InitiatorParameters(\n decodeURIComponent(qrCodeString),\n js_waku__WEBPACK_IMPORTED_MODULE_1__.utils.hexToBytes(messageNameTag)\n );\n}\n\nfunction getSenderAndResponder(node) {\n const sender = {\n async publish(encoder, msg) {\n await node.lightPush.push(encoder, msg);\n },\n };\n\n const msgQueue = new Array();\n const subscriptions = new Map();\n const intervals = new Map();\n\n const responder = {\n async subscribe(decoder) {\n const subscription = await node.filter.subscribe(\n [decoder],\n (wakuMessage) => {\n msgQueue.push(wakuMessage);\n }\n );\n subscriptions.set(decoder.contentTopic, subscription);\n },\n async nextMessage(contentTopic) {\n if (msgQueue.length != 0) {\n const oldestMsg = msgQueue.shift();\n if (oldestMsg.contentTopic === contentTopic) {\n return oldestMsg;\n }\n }\n\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n if (msgQueue.length != 0) {\n clearInterval(interval);\n const oldestMsg = msgQueue.shift();\n if (oldestMsg.contentTopic === contentTopic) {\n resolve(oldestMsg);\n }\n }\n }, 100);\n intervals.set(contentTopic, interval);\n });\n },\n async stop(contentTopic) {\n if (intervals.has(contentTopic)) {\n clearInterval(intervals.get(contentTopic));\n intervals.delete(contentTopic);\n }\n if (subscriptions.has(contentTopic)) {\n await subscriptions.get(contentTopic)();\n subscriptions.delete(contentTopic);\n } else {\n console.log(\"Subscriptipon doesnt exist\");\n }\n },\n };\n\n return [sender, responder];\n}\n\nasync function scheduleHandshakeAuthConfirmation(pairingObj, ui) {\n const authCode = await pairingObj.getAuthCode();\n ui.handshake.connecting();\n pairingObj.validateAuthCode(confirm(\"Confirm that authcode is: \" + authCode));\n}\n\nasync function buildWakuMessage(node, noiseExecute) {\n const [encoder, decoder] = await noiseExecute;\n\n const sendMessage = async (message) => {\n let payload = ProtoMessage.create({\n data: JSON.stringify(message),\n });\n payload = ProtoMessage.encode(payload).finish();\n\n return node.lightPush.push(encoder, { payload });\n };\n\n const listenToMessages = async (fn) => {\n return node.filter.subscribe([decoder], ({ payload }) => {\n const { data } = ProtoMessage.decode(payload);\n fn(JSON.parse(data));\n });\n };\n\n return [sendMessage, listenToMessages];\n}\n\nfunction initRTC({ ui, onReceive }) {\n const configuration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n };\n const peerConnection = new RTCPeerConnection(configuration);\n const sendChannel = peerConnection.createDataChannel(\"chat\");\n\n let receiveChannel;\n\n sendChannel.onopen = (event) => {\n ui.rtc.ready();\n console.log(\"onopen send\", event);\n };\n\n peerConnection.ondatachannel = (event) => {\n receiveChannel = event.channel;\n\n receiveChannel.onmessage = (event) => {\n onReceive(JSON.parse(event.data));\n };\n\n receiveChannel.onopen = (event) => {\n ui.rtc.ready();\n console.log(\"onopen receive\", event);\n };\n };\n\n const sendMessage = (text, nick) => {\n sendChannel.send(JSON.stringify({ text, nick, timestamp: Date.now() }));\n };\n\n return {\n peerConnection,\n sendChannel,\n receiveChannel,\n sendMessage,\n };\n}\n\nfunction initUI() {\n const messagesList = document.getElementById(\"messages\");\n const nicknameInput = document.getElementById(\"nick-input\");\n const textInput = document.getElementById(\"text-input\");\n const sendButton = document.getElementById(\"send-btn\");\n const chatArea = document.getElementById(\"chat-area\");\n const wakuStatusSpan = document.getElementById(\"waku-status\");\n const handshakeStatusSpan = document.getElementById(\"handshake-status\");\n\n const qrCanvas = document.getElementById(\"qr-canvas\");\n const qrUrlContainer = document.getElementById(\"qr-url-container\");\n const qrUrl = document.getElementById(\"qr-url\");\n const copyURLButton = document.getElementById(\"copy-url\");\n const openTabButton = document.getElementById(\"open-tab\");\n\n const rtcStatus = document.getElementById(\"rtc-status\");\n const connectChat = document.getElementById(\"connect-chat-btn\");\n\n copyURLButton.onclick = () => {\n const copyText = document.getElementById(\"qr-url\"); // need to get it each time otherwise copying does not work\n copyText.select();\n copyText.setSelectionRange(0, 99999);\n navigator.clipboard.writeText(copyText.value);\n };\n\n openTabButton.onclick = () => {\n window.open(qrUrl.value, \"_blank\");\n };\n\n const disableChatUIStateIfNeeded = () => {\n const readyToSend = nicknameInput.value !== \"\";\n textInput.disabled = !readyToSend;\n sendButton.disabled = !readyToSend;\n };\n nicknameInput.onchange = disableChatUIStateIfNeeded;\n nicknameInput.onblur = disableChatUIStateIfNeeded;\n\n return {\n shareInfo: {\n setURL(url) {\n qrUrl.value = url;\n },\n hide() {\n qrUrlContainer.style.display = \"none\";\n },\n show() {\n qrUrlContainer.style.display = \"flex\";\n },\n renderQR(url) {\n qrcode__WEBPACK_IMPORTED_MODULE_7__.toCanvas(qrCanvas, url, (err) => {\n if (err) {\n throw err;\n }\n });\n },\n },\n waku: {\n _val(msg) {\n wakuStatusSpan.innerText = msg;\n },\n _class(name) {\n wakuStatusSpan.className = name;\n },\n connecting() {\n this._val(\"connecting...\");\n this._class(\"progress\");\n },\n connected() {\n this._val(\"connected\");\n this._class(\"success\");\n },\n error(msg) {\n this._val(msg);\n this._class(\"error\");\n },\n },\n handshake: {\n _val(val) {\n handshakeStatusSpan.innerText = val;\n },\n _class(name) {\n handshakeStatusSpan.className = name;\n },\n error(msg) {\n this._val(msg);\n this._class(\"error\");\n },\n waiting() {\n this._val(\"waiting for handshake...\");\n this._class(\"progress\");\n },\n generating() {\n this._val(\"generating QR code...\");\n this._class(\"progress\");\n },\n connecting() {\n this._val(\"executing handshake...\");\n this._class(\"progress\");\n },\n connected() {\n this._val(\"handshake completed!\");\n this._class(\"success\");\n },\n },\n message: {\n _render({ time, text, nick }) {\n messagesList.innerHTML += `\n
  • \n (${nick})\n ${text}\n [${new Date(time).toISOString()}]\n
  • \n `;\n },\n _status(text, className) {\n sendButton.className = className;\n },\n onReceive(data) {\n const { timestamp, nick, text } = data;\n\n this._render({\n nick,\n time: timestamp,\n text,\n });\n },\n onSend(cb) {\n sendButton.addEventListener(\"click\", async () => {\n try {\n this._status(\"sending...\", \"progress\");\n await cb(textInput.value, nicknameInput.value);\n this._status(\"sent\", \"success\");\n\n this._render({\n time: Date.now(), // a bit different from what receiver will see but for the matter of example is good enough\n text: textInput.value,\n nick: nicknameInput.value,\n });\n textInput.value = \"\";\n } catch (e) {\n this._status(`error: ${e.message}`, \"error\");\n }\n });\n },\n display() {\n chatArea.style.display = \"block\";\n this._status(\"waiting for input\", \"progress\");\n },\n },\n rtc: {\n _val(msg) {\n rtcStatus.innerText = msg;\n },\n _class(name) {\n rtcStatus.className = name;\n },\n sendingOffer() {\n this._val(\"sending offer\");\n this._class(\"progress\");\n },\n sendingAnswer() {\n this._val(\"sending answer\");\n this._class(\"progress\");\n },\n sendingCandidate() {\n this._val(\"sending ice candidate\");\n this._class(\"progress\");\n },\n ready() {\n this._val(\"ready\");\n this._class(\"success\");\n },\n error(msg) {\n this._val(msg);\n this._class(\"error\");\n },\n onConnect(cb) {\n connectChat.addEventListener(\"click\", cb);\n },\n },\n hide() {\n this.shareInfo.hide();\n chatArea.style.display = \"none\";\n },\n };\n}\n\n\n//# sourceURL=webpack://@waku/noise-rtc/./index.js?"); /***/ }),