From 8c8865896dcd42f4d3c291f85975f021f4f4b6d7 Mon Sep 17 00:00:00 2001 From: weboko Date: Tue, 6 Dec 2022 23:22:57 +0100 Subject: [PATCH 01/10] init folder Signed-off-by: weboko --- light-filter/index.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 light-filter/index.html diff --git a/light-filter/index.html b/light-filter/index.html new file mode 100644 index 0000000..0601a09 --- /dev/null +++ b/light-filter/index.html @@ -0,0 +1 @@ +tbd From 0227dfe592d59a48c5d6cf9410164e7e035714b8 Mon Sep 17 00:00:00 2001 From: weboko Date: Wed, 7 Dec 2022 23:46:22 +0100 Subject: [PATCH 02/10] add light-chat basic functionality + template for html Signed-off-by: weboko --- light-chat/index.html | 35 ++++++++++++++++++++++++++++++ light-chat/index.js | 48 +++++++++++++++++++++++++++++++++++++++++ light-chat/package.json | 14 ++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 light-chat/index.html create mode 100644 light-chat/index.js create mode 100644 light-chat/package.json diff --git a/light-chat/index.html b/light-chat/index.html new file mode 100644 index 0000000..b35c91d --- /dev/null +++ b/light-chat/index.html @@ -0,0 +1,35 @@ + + + + + + JS-Waku light chat + + + +

Status

+
+ +

Local Peer Id

+
+ +

Remote Peer Id

+
+ +

Remote peer's multiaddr

+
+ +
+ +
+ +
+ +
+ + +
+ + + + diff --git a/light-chat/index.js b/light-chat/index.js new file mode 100644 index 0000000..2324b95 --- /dev/null +++ b/light-chat/index.js @@ -0,0 +1,48 @@ +import * as utils from 'https://unpkg.com/@waku/byte-utils@0.0.2/bundle/index.js'; +import * as wakuCreate from 'https://unpkg.com/@waku/create@0.0.4/bundle/index.js' +import { waitForRemotePeer } from 'https://unpkg.com/@waku/core@0.0.6/bundle/lib/wait_for_remote_peer.js' +import * as wakuMessage from 'https://unpkg.com/@waku/core@0.0.6/bundle/lib/waku_message/version_0.js' + +const MULTI_ADDR = "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm"; +const CONTENT_TOPIC = "/js-waku-examples/1/chat/utf8"; +const PROTOCOLS = ["filter", "lightpush"]; + +const { sendMessage, unsubscribeFromMessages } = await initializeWakuContext({ + multiAddr: MULTI_ADDR, + protocols: PROTOCOLS, + contentTopic: CONTENT_TOPIC, + onMessageReceived: (message) => { + console.log(message); + }, +}); + +async function initializeWakuContext({ + multiAddr, + protocols, + contentTopic, + onMessageReceived, +}) { + const Decoder = new wakuMessage.DecoderV0(contentTopic); + const Encoder = new wakuMessage.EncoderV0(contentTopic); + + const node = await wakuCreate.createLightNode(); + + await node.start(); + + await node.dial(multiAddr, protocols); + await waitForRemotePeer(node, protocols); + + const unsubscribeFromMessages = await node.filter.subscribe([Decoder], (wakuMessage) => { + const messageText = utils.bytesToUtf8(wakuMessage.payload); + onMessageReceived(messageText); + }); + + return { + unsubscribeFromMessages, + sendMessage: async (value) => { + await node.lightPush.push(Encoder, { + payload: utils.utf8ToBytes(value) + }); + } + }; +} diff --git a/light-chat/package.json b/light-chat/package.json new file mode 100644 index 0000000..63237e7 --- /dev/null +++ b/light-chat/package.json @@ -0,0 +1,14 @@ +{ + "name": "light-chat", + "version": "0.1.0", + "private": true, + "homepage": "/light-chat", + "devDependencies": { + "serve": "^14.1.2", + "gh-pages": "^4.0.0" + }, + "scripts": { + "start": "serve .", + "deploy": "gh-pages -d ." + } +} From 4fddc02896cf4f6107be05f9cd4a13897a36e498 Mon Sep 17 00:00:00 2001 From: weboko Date: Thu, 8 Dec 2022 21:33:06 +0100 Subject: [PATCH 03/10] add protobuf, change topic Signed-off-by: weboko --- light-chat/index.html | 3 ++- light-chat/index.js | 39 +++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/light-chat/index.html b/light-chat/index.html index b35c91d..d1a59ee 100644 --- a/light-chat/index.html +++ b/light-chat/index.html @@ -8,7 +8,7 @@

Status

-
+
Connecting... | Connected | Disconnected

Local Peer Id

@@ -30,6 +30,7 @@ + diff --git a/light-chat/index.js b/light-chat/index.js index 2324b95..5b35c1a 100644 --- a/light-chat/index.js +++ b/light-chat/index.js @@ -1,18 +1,18 @@ -import * as utils from 'https://unpkg.com/@waku/byte-utils@0.0.2/bundle/index.js'; -import * as wakuCreate from 'https://unpkg.com/@waku/create@0.0.4/bundle/index.js' -import { waitForRemotePeer } from 'https://unpkg.com/@waku/core@0.0.6/bundle/lib/wait_for_remote_peer.js' -import * as wakuMessage from 'https://unpkg.com/@waku/core@0.0.6/bundle/lib/waku_message/version_0.js' +import * as utils from "https://unpkg.com/@waku/byte-utils@0.0.2/bundle/index.js"; +import * as wakuCreate from "https://unpkg.com/@waku/create@0.0.4/bundle/index.js"; +import { waitForRemotePeer } from "https://unpkg.com/@waku/core@0.0.6/bundle/lib/wait_for_remote_peer.js"; +import * as wakuMessage from "https://unpkg.com/@waku/core@0.0.6/bundle/lib/waku_message/version_0.js"; const MULTI_ADDR = "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm"; -const CONTENT_TOPIC = "/js-waku-examples/1/chat/utf8"; +const CONTENT_TOPIC = "/toy-chat/2/huilong/proto"; const PROTOCOLS = ["filter", "lightpush"]; const { sendMessage, unsubscribeFromMessages } = await initializeWakuContext({ - multiAddr: MULTI_ADDR, protocols: PROTOCOLS, + multiAddr: MULTI_ADDR, contentTopic: CONTENT_TOPIC, - onMessageReceived: (message) => { - console.log(message); + onMessageReceived: ({ nick, timestamp, text }) => { + console.log(timestamp, '\t', nick, ": ", text); }, }); @@ -25,6 +25,11 @@ async function initializeWakuContext({ const Decoder = new wakuMessage.DecoderV0(contentTopic); const Encoder = new wakuMessage.EncoderV0(contentTopic); + const ChatMessage = new protobuf.Type("ChatMessage") + .add(new protobuf.Field("timestamp", 1, "uint64")) + .add(new protobuf.Field("nick", 2, "string")) + .add(new protobuf.Field("text", 3, "bytes")); + const node = await wakuCreate.createLightNode(); await node.start(); @@ -32,16 +37,26 @@ async function initializeWakuContext({ await node.dial(multiAddr, protocols); await waitForRemotePeer(node, protocols); + // Set a filter by using Decoder for a given ContentTopic const unsubscribeFromMessages = await node.filter.subscribe([Decoder], (wakuMessage) => { - const messageText = utils.bytesToUtf8(wakuMessage.payload); - onMessageReceived(messageText); + const messageObj = ChatMessage.decode(wakuMessage.payload); + onMessageReceived({ + ...messageObj, + text: utils.bytesToUtf8(messageObj.text), + }); }); return { unsubscribeFromMessages, - sendMessage: async (value) => { + sendMessage: async ({ text, nick }) => { + const protoMessage = ChatMessage.create({ + nick, + timestamp: Date.now(), + text: utils.utf8ToBytes(text), + }); + await node.lightPush.push(Encoder, { - payload: utils.utf8ToBytes(value) + payload: ChatMessage.encode(protoMessage).finish(), }); } }; From 190bd7af63228a0f6fe5b4d0ae473b4edaad0306 Mon Sep 17 00:00:00 2001 From: weboko Date: Fri, 9 Dec 2022 00:21:08 +0100 Subject: [PATCH 04/10] add UI controller,finalasie chat Signed-off-by: weboko --- light-chat/index.html | 182 ++++++++++++++++++++++++++++++++++++++---- light-chat/index.js | 121 ++++++++++++++++++++++++++-- 2 files changed, 280 insertions(+), 23 deletions(-) diff --git a/light-chat/index.html b/light-chat/index.html index d1a59ee..3d14e2b 100644 --- a/light-chat/index.html +++ b/light-chat/index.html @@ -4,30 +4,182 @@ JS-Waku light chat + -

Status

-
Connecting... | Connected | Disconnected
+
+
+

Status:

-

Local Peer Id

-
+
+ Peer's information -

Remote Peer Id

-
+

Content topic

+

-

Remote peer's multiaddr

-
+

Local Peer Id

+

-
+

Remote Peer Id

+

-
- -
+

Remote peer's multiaddr

+

+
+
-
- - +
+ +
diff --git a/light-chat/index.js b/light-chat/index.js index 5b35c1a..aafbc95 100644 --- a/light-chat/index.js +++ b/light-chat/index.js @@ -7,16 +7,41 @@ const MULTI_ADDR = "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/ const CONTENT_TOPIC = "/toy-chat/2/huilong/proto"; const PROTOCOLS = ["filter", "lightpush"]; -const { sendMessage, unsubscribeFromMessages } = await initializeWakuContext({ - protocols: PROTOCOLS, - multiAddr: MULTI_ADDR, - contentTopic: CONTENT_TOPIC, - onMessageReceived: ({ nick, timestamp, text }) => { - console.log(timestamp, '\t', nick, ": ", text); - }, +runApp().catch((err) => { + console.error(err); }); -async function initializeWakuContext({ +async function runApp() { + const ui = initUI(); + + ui.setStatus("connecting..."); + + const { info, sendMessage, unsubscribeFromMessages } = await initWakuContext({ + protocols: PROTOCOLS, + multiAddr: MULTI_ADDR, + contentTopic: CONTENT_TOPIC, + onMessageReceived: ui.renderMessage, + }); + + ui.setStatus("connected"); + + ui.setLocalPeer(info.localPeerId); + ui.setRemotePeer(info.remotePeerId); + ui.setRemoteMultiAddr(info.multiAddr); + ui.setContentTopic(info.contentTopic); + + ui.onSendMessage(sendMessage); + + ui.onExit(() => { + ui.setStatus("disconnecting..."); + unsubscribeFromMessages().then(() => { + ui.setStatus("disconnected"); + ui.resetMessages(); + }); + }); +} + +async function initWakuContext({ multiAddr, protocols, contentTopic, @@ -46,9 +71,24 @@ async function initializeWakuContext({ }); }); + const localPeerId = node.libp2p.peerId.toString(); + + const remotePeers = await node.libp2p.peerStore.all(); + const remotePeerId = remotePeers[0].id.toString(); + return { unsubscribeFromMessages, + info: { + multiAddr, + contentTopic, + localPeerId, + remotePeerId, + }, sendMessage: async ({ text, nick }) => { + if (!text || !nick) { + return; + } + const protoMessage = ChatMessage.create({ nick, timestamp: Date.now(), @@ -61,3 +101,68 @@ async function initializeWakuContext({ } }; } + +// UI adapter +function initUI() { + const exitButton = document.getElementById("exit"); + const sendButton = document.getElementById("send"); + + const statusBlock = document.getElementById("status"); + const localPeerBlock = document.getElementById("localPeerId"); + const remotePeerId = document.getElementById("remotePeerId"); + const remoteMultiAddr = document.getElementById("remoteMultiAddr"); + const contentTopicBlock = document.getElementById("contentTopic"); + + const messagesBlock = document.getElementById("messages"); + + const nickText = document.getElementById("nickText"); + const messageText = document.getElementById("messageText"); + + return { + // UI events + onExit: (cb) => { + exitButton.addEventListener("click", cb); + }, + onSendMessage: (cb) => { + sendButton.addEventListener("click", () => { + cb({ + nick: nickText.value, + text: messageText.value, + }).then(() => { + messageText.value = ""; + }); + }); + }, + // UI renderers + setStatus: (value) => { + statusBlock.innerText = value.toString(); + }, + setLocalPeer: (id) => { + localPeerBlock.innerText = id.toString(); + }, + setRemotePeer: (id) => { + remotePeerId.innerText = id.toString(); + }, + setRemoteMultiAddr: (multiAddr) => { + remoteMultiAddr.innerText = multiAddr.toString(); + }, + setContentTopic: (topic) => { + contentTopicBlock.innerText = topic.toString(); + }, + renderMessage: (messageObj) => { + const { nick, text, timestamp } = messageObj; + const date = new Date(timestamp); + + // WARNING: XSS vulnerable + messagesBlock.innerHTML += ` +
+

${nick} (${date.toDateString()}):

+

${text}

+
+ `; + }, + resetMessages: () => { + messagesBlock.innerHTML = ""; + }, + }; +} From e852f65c7e98cf68cc042e007fbf99202c565820 Mon Sep 17 00:00:00 2001 From: weboko Date: Fri, 9 Dec 2022 00:21:25 +0100 Subject: [PATCH 05/10] remove index.html Signed-off-by: weboko --- light-filter/index.html | 1 - 1 file changed, 1 deletion(-) delete mode 100644 light-filter/index.html diff --git a/light-filter/index.html b/light-filter/index.html deleted file mode 100644 index 0601a09..0000000 --- a/light-filter/index.html +++ /dev/null @@ -1 +0,0 @@ -tbd From 88aa19b06f165bf52db29bdecd2c74ee14a31052 Mon Sep 17 00:00:00 2001 From: weboko Date: Fri, 9 Dec 2022 01:06:01 +0100 Subject: [PATCH 06/10] extract css into separate file Signed-off-by: weboko --- light-chat/index.html | 140 +----------------------------------------- light-chat/style.css | 137 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 139 deletions(-) create mode 100644 light-chat/style.css diff --git a/light-chat/index.html b/light-chat/index.html index 3d14e2b..90b8037 100644 --- a/light-chat/index.html +++ b/light-chat/index.html @@ -4,145 +4,7 @@ JS-Waku light chat - + diff --git a/light-chat/style.css b/light-chat/style.css new file mode 100644 index 0000000..eb3e853 --- /dev/null +++ b/light-chat/style.css @@ -0,0 +1,137 @@ +* { + margin: 0; + padding: 0; + word-wrap: break-word; + box-sizing: border-box; +} + +html, body { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; +} + +html { + font-size: 16px; + overflow: hidden; +} + +body { + display: flex; + align-items: center; + padding: 10px; + justify-content: center; +} + +details { + margin-bottom: 15px; +} + +details p { + margin-bottom: 10px; +} + +summary { + cursor: pointer; + max-width: 100%; + margin-bottom: 5px; +} + +span { + font-weight: 300; +} + +input, textarea { + line-height: 1rem; + padding: 5px; +} + +textarea { + min-height: 3rem; +} + +h3 { + margin-bottom: 5px; +} + +.content { + width: 800px; + min-width: 300px; + max-width: 800px; + height: 100%; + display: flex; + flex-direction: column; + align-content: space-between; +} + +#messages { + overflow-y: scroll; + overflow-x: hidden; +} + +.message + .message { + margin-top: 15px; +} + +.message :first-child { + font-weight: bold; +} + +.message p + p { + margin-top: 5px; +} + +.message span { + font-size: 0.8rem; +} + +.inputArea { + display: flex; + gap: 10px; + flex-direction: column; + margin-top: 20px; +} + +.controls { + margin-top: 10px; + display: flex; + gap: 10px; +} + +.controls button { + flex-grow: 1; + cursor: pointer; + padding: 10px; +} + +#send { + background-color: #32d1a0; + border: none; + color: white; +} +#send:hover { + background-color: #3abd96; +} +#send:active { + background-color: #3ba183; +} + +#exit { + color: white; + border: none; + background-color: #ff3a31; +} +#exit:hover { + background-color: #e4423a; +} +#exit:active { + background-color: #c84740; +} + +.footer { + display: flex; + width: 100%; + flex-direction: column; + align-self: flex-end; +} From 5bee034de8d9ffe0a263530fd276a17bede8e5d0 Mon Sep 17 00:00:00 2001 From: weboko Date: Fri, 9 Dec 2022 19:42:31 +0100 Subject: [PATCH 07/10] move to async/await Signed-off-by: weboko --- light-chat/index.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/light-chat/index.js b/light-chat/index.js index aafbc95..8e90253 100644 --- a/light-chat/index.js +++ b/light-chat/index.js @@ -32,12 +32,11 @@ async function runApp() { ui.onSendMessage(sendMessage); - ui.onExit(() => { + ui.onExit(async () => { ui.setStatus("disconnecting..."); - unsubscribeFromMessages().then(() => { - ui.setStatus("disconnected"); - ui.resetMessages(); - }); + await unsubscribeFromMessages(); + ui.setStatus("disconnected"); + ui.resetMessages(); }); } @@ -124,13 +123,12 @@ function initUI() { exitButton.addEventListener("click", cb); }, onSendMessage: (cb) => { - sendButton.addEventListener("click", () => { - cb({ + sendButton.addEventListener("click", async () => { + await cb({ nick: nickText.value, text: messageText.value, - }).then(() => { - messageText.value = ""; }); + messageText.value = ""; }); }, // UI renderers From 38a7407355822730c17d75ce66f049277f6107ab Mon Sep 17 00:00:00 2001 From: weboko Date: Mon, 12 Dec 2022 12:26:34 +0100 Subject: [PATCH 08/10] move to default bootstrap --- light-chat/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/light-chat/index.js b/light-chat/index.js index 8e90253..05dd2c5 100644 --- a/light-chat/index.js +++ b/light-chat/index.js @@ -54,11 +54,9 @@ async function initWakuContext({ .add(new protobuf.Field("nick", 2, "string")) .add(new protobuf.Field("text", 3, "bytes")); - const node = await wakuCreate.createLightNode(); + const node = await wakuCreate.createLightNode({ defaultBootstrap: true }); await node.start(); - - await node.dial(multiAddr, protocols); await waitForRemotePeer(node, protocols); // Set a filter by using Decoder for a given ContentTopic From 415faab1052fd0bb112d175f5ce52544464fdd9d Mon Sep 17 00:00:00 2001 From: weboko Date: Mon, 12 Dec 2022 12:41:04 +0100 Subject: [PATCH 09/10] add status colors, render status error when fail --- light-chat/index.js | 20 ++++++++++---------- light-chat/style.css | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/light-chat/index.js b/light-chat/index.js index 05dd2c5..0054a38 100644 --- a/light-chat/index.js +++ b/light-chat/index.js @@ -7,14 +7,14 @@ const MULTI_ADDR = "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/ const CONTENT_TOPIC = "/toy-chat/2/huilong/proto"; const PROTOCOLS = ["filter", "lightpush"]; -runApp().catch((err) => { +const ui = initUI(); +runApp(ui).catch((err) => { console.error(err); + ui.setStatus(`error: ${err.message}`, "error"); }); -async function runApp() { - const ui = initUI(); - - ui.setStatus("connecting..."); +async function runApp(ui) { + ui.setStatus("connecting...", "progress"); const { info, sendMessage, unsubscribeFromMessages } = await initWakuContext({ protocols: PROTOCOLS, @@ -23,7 +23,7 @@ async function runApp() { onMessageReceived: ui.renderMessage, }); - ui.setStatus("connected"); + ui.setStatus("connected", "success"); ui.setLocalPeer(info.localPeerId); ui.setRemotePeer(info.remotePeerId); @@ -33,9 +33,9 @@ async function runApp() { ui.onSendMessage(sendMessage); ui.onExit(async () => { - ui.setStatus("disconnecting..."); + ui.setStatus("disconnecting...", "progress"); await unsubscribeFromMessages(); - ui.setStatus("disconnected"); + ui.setStatus("disconnected", "terminated"); ui.resetMessages(); }); } @@ -130,8 +130,8 @@ function initUI() { }); }, // UI renderers - setStatus: (value) => { - statusBlock.innerText = value.toString(); + setStatus: (value, className) => { + statusBlock.innerHTML = `${value}`; }, setLocalPeer: (id) => { localPeerBlock.innerText = id.toString(); diff --git a/light-chat/style.css b/light-chat/style.css index eb3e853..ff54ed1 100644 --- a/light-chat/style.css +++ b/light-chat/style.css @@ -129,6 +129,22 @@ h3 { background-color: #c84740; } +.success { + color: #3ba183; +} + +.progress { + color: #9ea13b; +} + +.terminated { + color: black; +} + +.error { + color: #c84740; +} + .footer { display: flex; width: 100%; From e436bd542eb0dd794e2fc6c3732bbdb9a01b01f2 Mon Sep 17 00:00:00 2001 From: weboko Date: Mon, 12 Dec 2022 12:46:51 +0100 Subject: [PATCH 10/10] add support for multiple remote peers --- light-chat/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/light-chat/index.js b/light-chat/index.js index 0054a38..c4c40d5 100644 --- a/light-chat/index.js +++ b/light-chat/index.js @@ -26,7 +26,7 @@ async function runApp(ui) { ui.setStatus("connected", "success"); ui.setLocalPeer(info.localPeerId); - ui.setRemotePeer(info.remotePeerId); + ui.setRemotePeer(info.remotePeerIds); ui.setRemoteMultiAddr(info.multiAddr); ui.setContentTopic(info.contentTopic); @@ -71,7 +71,7 @@ async function initWakuContext({ const localPeerId = node.libp2p.peerId.toString(); const remotePeers = await node.libp2p.peerStore.all(); - const remotePeerId = remotePeers[0].id.toString(); + const remotePeerIds = remotePeers.map(peer => peer.id.toString()); return { unsubscribeFromMessages, @@ -79,7 +79,7 @@ async function initWakuContext({ multiAddr, contentTopic, localPeerId, - remotePeerId, + remotePeerIds, }, sendMessage: async ({ text, nick }) => { if (!text || !nick) { @@ -136,8 +136,8 @@ function initUI() { setLocalPeer: (id) => { localPeerBlock.innerText = id.toString(); }, - setRemotePeer: (id) => { - remotePeerId.innerText = id.toString(); + setRemotePeer: (ids) => { + remotePeerId.innerText = ids.join("\n"); }, setRemoteMultiAddr: (multiAddr) => { remoteMultiAddr.innerText = multiAddr.toString();