mirror of
https://github.com/logos-messaging/examples.waku.org.git
synced 2026-01-05 22:33:08 +00:00
add RTC abstractions and pretify code
This commit is contained in:
parent
02f66cc2b9
commit
ce4f3e9b38
@ -153,6 +153,10 @@
|
|||||||
<b>Handshake Status:</b>
|
<b>Handshake Status:</b>
|
||||||
<span id="handshake-status" class="progress">waiting for waku</span>
|
<span id="handshake-status" class="progress">waiting for waku</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
<h3>
|
||||||
|
<b>RTC Status:</b>
|
||||||
|
<span id="rtc-status" class="progress">connecting</span>
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pairingInfo" id="qr-url-container" style="display: none">
|
<div class="pairingInfo" id="qr-url-container" style="display: none">
|
||||||
@ -182,6 +186,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
></textarea>
|
></textarea>
|
||||||
<button id="send-btn" type="button" disabled>Send message</button>
|
<button id="send-btn" type="button" disabled>Send message</button>
|
||||||
|
<button id="connect-chat-btn" type="button">Connect to chat</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import protobuf from "protobufjs";
|
|||||||
import QRCode from "qrcode";
|
import QRCode from "qrcode";
|
||||||
|
|
||||||
// Protobuf
|
// Protobuf
|
||||||
const ProtoChatMessage = new protobuf.Type("ChatMessage").add(
|
const ProtoMessage = new protobuf.Type("Message").add(
|
||||||
new protobuf.Field("text", 3, "bytes")
|
new protobuf.Field("data", 3, "bytes")
|
||||||
);
|
);
|
||||||
|
|
||||||
main();
|
main();
|
||||||
@ -51,8 +51,8 @@ async function main() {
|
|||||||
|
|
||||||
scheduleHandshakeAuthConfirmation(pairingObj, ui);
|
scheduleHandshakeAuthConfirmation(pairingObj, ui);
|
||||||
|
|
||||||
let encoder;
|
let sendWakuMessage;
|
||||||
let decoder;
|
let listenToWakuMessages;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ui.handshake.waiting();
|
ui.handshake.waiting();
|
||||||
@ -64,7 +64,10 @@ async function main() {
|
|||||||
ui.shareInfo.show();
|
ui.shareInfo.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
[encoder, decoder] = await pExecute;
|
[sendWakuMessage, listenToWakuMessages] = await buildWakuMessage(
|
||||||
|
node,
|
||||||
|
pExecute
|
||||||
|
);
|
||||||
|
|
||||||
ui.handshake.connected();
|
ui.handshake.connected();
|
||||||
ui.shareInfo.hide();
|
ui.shareInfo.hide();
|
||||||
@ -85,154 +88,102 @@ async function main() {
|
|||||||
*/
|
*/
|
||||||
ui.message.display();
|
ui.message.display();
|
||||||
|
|
||||||
const configuration = {
|
const { peerConnection, sendMessage: sendRTCMessage } = initRTC({
|
||||||
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
ui,
|
||||||
};
|
onReceive: ui.message.onReceive,
|
||||||
let peerConnection = new RTCPeerConnection(configuration);
|
});
|
||||||
let sendDataChannel = peerConnection.createDataChannel("chat");
|
|
||||||
let receiveChannel;
|
|
||||||
|
|
||||||
sendDataChannel.onopen = (event) => {
|
|
||||||
console.log("onopen", event);
|
|
||||||
};
|
|
||||||
sendDataChannel.onclose = (event) => {
|
|
||||||
console.log("onclose", event);
|
|
||||||
};
|
|
||||||
|
|
||||||
peerConnection.ondatachannel = (event) => {
|
|
||||||
receiveChannel = event.channel;
|
|
||||||
window.receiveChannel = receiveChannel;
|
|
||||||
receiveChannel.onmessage = (event) => {
|
|
||||||
console.log("onmessage", event);
|
|
||||||
};
|
|
||||||
receiveChannel.onopen = (event) => {
|
|
||||||
console.log("onopen receive", event);
|
|
||||||
};
|
|
||||||
receiveChannel.onclose = (event) => {
|
|
||||||
console.log("onclose receive", event);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
peerConnection.onicecandidate = async (event) => {
|
peerConnection.onicecandidate = async (event) => {
|
||||||
console.log("onicecandidate event", event);
|
|
||||||
if (event.candidate) {
|
if (event.candidate) {
|
||||||
// try {
|
try {
|
||||||
// await peerConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
|
ui.rtc.sendingCandidate();
|
||||||
// } catch(error) {
|
await sendWakuMessage({
|
||||||
// console.log("onicecandidate error", error);
|
type: "candidate",
|
||||||
// }
|
candidate: event.candidate,
|
||||||
let message = ProtoChatMessage.create({
|
});
|
||||||
text: utils.utf8ToBytes(
|
} catch (error) {
|
||||||
JSON.stringify({
|
ui.rtc.error(error.message);
|
||||||
type: "candidate",
|
}
|
||||||
candidate: event.candidate,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
});
|
|
||||||
message = ProtoChatMessage.encode(message).finish();
|
|
||||||
|
|
||||||
await node.lightPush.push(encoder, { payload: message });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
peerConnection.oniceconnectionstatechange = console.log;
|
const sendOffer = async () => {
|
||||||
|
ui.rtc.sendingOffer();
|
||||||
|
|
||||||
window.sendOffer = sendOffer;
|
try {
|
||||||
window.sendDataChannel = sendDataChannel;
|
const offer = await peerConnection.createOffer();
|
||||||
|
await peerConnection.setLocalDescription(offer);
|
||||||
|
|
||||||
async function sendOffer() {
|
await sendWakuMessage({
|
||||||
const offer = await peerConnection.createOffer();
|
type: "offer",
|
||||||
await peerConnection.setLocalDescription(offer);
|
offer,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
ui.rtc.error(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let offerMessage = ProtoChatMessage.create({
|
const sendAnswer = async (data) => {
|
||||||
text: utils.utf8ToBytes(
|
ui.rtc.sendAnswer();
|
||||||
JSON.stringify({
|
try {
|
||||||
type: "offer",
|
|
||||||
offer,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
});
|
|
||||||
offerMessage = ProtoChatMessage.encode(offerMessage).finish();
|
|
||||||
|
|
||||||
await node.lightPush.push(encoder, { payload: offerMessage });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendAnswer(data) {
|
|
||||||
await peerConnection.setRemoteDescription(
|
|
||||||
new RTCSessionDescription(data.offer)
|
|
||||||
);
|
|
||||||
|
|
||||||
const answer = await peerConnection.createAnswer();
|
|
||||||
peerConnection.setLocalDescription(answer);
|
|
||||||
|
|
||||||
let answerMessage = ProtoChatMessage.create({
|
|
||||||
text: utils.utf8ToBytes(
|
|
||||||
JSON.stringify({
|
|
||||||
type: "answer",
|
|
||||||
answer,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
});
|
|
||||||
answerMessage = ProtoChatMessage.encode(answerMessage).finish();
|
|
||||||
|
|
||||||
await node.lightPush.push(encoder, { payload: answerMessage });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleAnswer(data) {
|
|
||||||
if (peerConnection) {
|
|
||||||
await peerConnection.setRemoteDescription(
|
await peerConnection.setRemoteDescription(
|
||||||
new RTCSessionDescription(data.answer)
|
new RTCSessionDescription(data.offer)
|
||||||
);
|
);
|
||||||
|
|
||||||
let message = ProtoChatMessage.create({
|
const answer = await peerConnection.createAnswer();
|
||||||
text: utils.utf8ToBytes(
|
peerConnection.setLocalDescription(answer);
|
||||||
JSON.stringify({
|
|
||||||
type: "ready",
|
await sendWakuMessage({
|
||||||
text: "just ready",
|
type: "answer",
|
||||||
})
|
answer,
|
||||||
),
|
|
||||||
});
|
});
|
||||||
message = ProtoChatMessage.encode(message).finish();
|
} catch (error) {
|
||||||
|
ui.rtc.error(error.message);
|
||||||
await node.lightPush.push(encoder, { payload: message });
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async function handleCandidate(data) {
|
const receiveAnswer = async (data) => {
|
||||||
if (data.candidate) {
|
await peerConnection.setRemoteDescription(
|
||||||
try {
|
new RTCSessionDescription(data.answer)
|
||||||
console.log("handleCandidate", data.candidate);
|
);
|
||||||
await peerConnection.addIceCandidate(
|
|
||||||
new RTCIceCandidate(data.candidate)
|
await sendWakuMessage({
|
||||||
);
|
type: "ready",
|
||||||
} catch (error) {
|
text: "received answer",
|
||||||
console.error("handleCandidate", error);
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const receiveCandidate = async (data) => {
|
||||||
|
try {
|
||||||
|
await peerConnection.addIceCandidate(
|
||||||
|
new RTCIceCandidate(data.candidate)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
ui.rtc.error(error.message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async function handleReceive({ payload }) {
|
|
||||||
const { text } = ProtoChatMessage.decode(payload);
|
|
||||||
const data = JSON.parse(utils.bytesToUtf8(text));
|
|
||||||
|
|
||||||
|
const handleWakuMessages = async (data) => {
|
||||||
if (data.type === "offer") {
|
if (data.type === "offer") {
|
||||||
await sendAnswer(data);
|
await sendAnswer(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === "answer") {
|
if (data.type === "answer") {
|
||||||
await handleAnswer(data);
|
await receiveAnswer(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === "ready") {
|
if (data.type === "ready") {
|
||||||
console.log("partner is ready", data);
|
console.log("RTC: partner is", data.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === "candidate") {
|
if (data.type === "candidate") {
|
||||||
await handleCandidate(data);
|
await receiveCandidate(data);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
await node.filter.subscribe([decoder], handleReceive);
|
await listenToWakuMessages(handleWakuMessages);
|
||||||
|
ui.message.onSend(sendRTCMessage);
|
||||||
|
ui.rtc.onConnect(sendOffer);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ui.waku.error(err.message);
|
ui.waku.error(err.message);
|
||||||
ui.hide();
|
ui.hide();
|
||||||
@ -332,12 +283,70 @@ async function scheduleHandshakeAuthConfirmation(pairingObj, ui) {
|
|||||||
pairingObj.validateAuthCode(confirm("Confirm that authcode is: " + authCode));
|
pairingObj.validateAuthCode(confirm("Confirm that authcode is: " + authCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function buildWakuMessage(node, noiseExecute) {
|
||||||
|
const [encoder, decoder] = await noiseExecute;
|
||||||
|
|
||||||
|
const sendMessage = async (message) => {
|
||||||
|
let payload = ProtoMessage.create({
|
||||||
|
data: utils.utf8ToBytes(JSON.stringify(message)),
|
||||||
|
});
|
||||||
|
payload = ProtoMessage.encode(payload).finish();
|
||||||
|
|
||||||
|
return node.lightPush.push(encoder, { payload });
|
||||||
|
};
|
||||||
|
|
||||||
|
const listenToMessages = async (fn) => {
|
||||||
|
return node.filter.subscribe([decoder], (payload) => {
|
||||||
|
const { data } = ProtoMessage.decode(payload);
|
||||||
|
fn(JSON.parse(utils.bytesToUtf8(data)));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return [sendMessage, listenToMessages];
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRTC({ ui, onReceive }) {
|
||||||
|
const configuration = {};
|
||||||
|
const peerConnection = new RTCPeerConnection(configuration);
|
||||||
|
const sendChannel = peerConnection.createDataChannel("chat");
|
||||||
|
|
||||||
|
let receiveChannel;
|
||||||
|
|
||||||
|
sendChannel.onopen = (event) => {
|
||||||
|
ui.rtc.ready();
|
||||||
|
console.log("onopen send", event);
|
||||||
|
};
|
||||||
|
|
||||||
|
peerConnection.ondatachannel = (event) => {
|
||||||
|
receiveChannel = event.channel;
|
||||||
|
|
||||||
|
receiveChannel.onmessage = (event) => {
|
||||||
|
onReceive(JSON.parse(event.data));
|
||||||
|
};
|
||||||
|
|
||||||
|
receiveChannel.onopen = (event) => {
|
||||||
|
ui.rtc.ready();
|
||||||
|
console.log("onopen receive", event);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendMessage = (message, nick) => {
|
||||||
|
sendChannel.send(JSON.stringify({ message, nick, timestamp: Date.now() }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
peerConnection,
|
||||||
|
sendChannel,
|
||||||
|
receiveChannel,
|
||||||
|
sendMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function initUI() {
|
function initUI() {
|
||||||
const messagesList = document.getElementById("messages");
|
const messagesList = document.getElementById("messages");
|
||||||
const nicknameInput = document.getElementById("nick-input");
|
const nicknameInput = document.getElementById("nick-input");
|
||||||
const textInput = document.getElementById("text-input");
|
const textInput = document.getElementById("text-input");
|
||||||
const sendButton = document.getElementById("send-btn");
|
const sendButton = document.getElementById("send-btn");
|
||||||
const sendingStatusSpan = document.getElementById("sending-status");
|
|
||||||
const chatArea = document.getElementById("chat-area");
|
const chatArea = document.getElementById("chat-area");
|
||||||
const wakuStatusSpan = document.getElementById("waku-status");
|
const wakuStatusSpan = document.getElementById("waku-status");
|
||||||
const handshakeStatusSpan = document.getElementById("handshake-status");
|
const handshakeStatusSpan = document.getElementById("handshake-status");
|
||||||
@ -348,6 +357,9 @@ function initUI() {
|
|||||||
const copyURLButton = document.getElementById("copy-url");
|
const copyURLButton = document.getElementById("copy-url");
|
||||||
const openTabButton = document.getElementById("open-tab");
|
const openTabButton = document.getElementById("open-tab");
|
||||||
|
|
||||||
|
const rtcStatus = document.getElementById("rtc-status");
|
||||||
|
const connectChat = document.getElementById("connect-chat-btn");
|
||||||
|
|
||||||
copyURLButton.onclick = () => {
|
copyURLButton.onclick = () => {
|
||||||
const copyText = document.getElementById("qr-url"); // need to get it each time otherwise copying does not work
|
const copyText = document.getElementById("qr-url"); // need to get it each time otherwise copying does not work
|
||||||
copyText.select();
|
copyText.select();
|
||||||
@ -447,13 +459,13 @@ function initUI() {
|
|||||||
_status(text, className) {
|
_status(text, className) {
|
||||||
sendButton.className = className;
|
sendButton.className = className;
|
||||||
},
|
},
|
||||||
onReceive({ payload }) {
|
onReceive(data) {
|
||||||
const { timestamp, nick, text } = ProtoChatMessage.decode(payload);
|
const { timestamp, nick, text } = data;
|
||||||
|
|
||||||
this._render({
|
this._render({
|
||||||
nick,
|
nick,
|
||||||
time: timestamp * 1000,
|
time: timestamp * 1000,
|
||||||
text: utils.bytesToUtf8(text),
|
text,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSend(cb) {
|
onSend(cb) {
|
||||||
@ -479,6 +491,37 @@ function initUI() {
|
|||||||
this._status("waiting for input", "progress");
|
this._status("waiting for input", "progress");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
rtc: {
|
||||||
|
_val(msg) {
|
||||||
|
rtcStatus.innerText = msg;
|
||||||
|
},
|
||||||
|
_class(name) {
|
||||||
|
rtcStatus.className = name;
|
||||||
|
},
|
||||||
|
sendingOffer() {
|
||||||
|
this._val("sending offer");
|
||||||
|
this._class("progress");
|
||||||
|
},
|
||||||
|
sendingAnswer() {
|
||||||
|
this._val("sending answer");
|
||||||
|
this._class("progress");
|
||||||
|
},
|
||||||
|
sendingCandidate() {
|
||||||
|
this._val("sending ice candidate");
|
||||||
|
this._class("progress");
|
||||||
|
},
|
||||||
|
ready() {
|
||||||
|
this._val("ready");
|
||||||
|
this._class("success");
|
||||||
|
},
|
||||||
|
error(msg) {
|
||||||
|
this._val(msg);
|
||||||
|
this._class("error");
|
||||||
|
},
|
||||||
|
onConnect(cb) {
|
||||||
|
connectChat.addEventListener("click", cb);
|
||||||
|
},
|
||||||
|
},
|
||||||
hide() {
|
hide() {
|
||||||
this.shareInfo.hide();
|
this.shareInfo.hide();
|
||||||
chatArea.style.display = "none";
|
chatArea.style.display = "none";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user