Skip to content

Commit

Permalink
examples/chat: break up into separate components
Browse files Browse the repository at this point in the history
  • Loading branch information
caolan committed Jul 8, 2024
1 parent b914fc2 commit e86d8f2
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 155 deletions.
31 changes: 31 additions & 0 deletions examples/chat/components/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {watch} from "../lib/signaller.js";
import {local_peer_id} from "../state.js";

export default class ChatHeader extends HTMLElement {
constructor() {
super();
}

connectedCallback() {
const shadow = this.attachShadow({mode: "open"});
shadow.innerHTML = `
<link rel="stylesheet" href="style.css">
<header>
<h1>Chat Example</h1>
<span id="local-peer-id"></span>
</header>
`;
const setText = () => {
const span = shadow.getElementById('local-peer-id');
span.textContent = `You: ${local_peer_id.value}`;
};
setText();
this.stop = watch([local_peer_id], setText);
}

disconnectedCallback() {
this.stop();
}
}

customElements.define("chat-header", ChatHeader);
48 changes: 48 additions & 0 deletions examples/chat/components/message-history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {watch} from "../lib/signaller.js";
import {local_peer_id, selected_invite, messages} from "../state.js";

export default class ChatMessageHistory extends HTMLElement {
constructor() {
super();
}

connectedCallback() {
this.shadow = this.attachShadow({mode: "open"});
this.shadow.innerHTML = `
<link rel="stylesheet" href="style.css">
<pre id="message-history"></pre>
`;
this.history = this.shadow.getElementById('message-history');
this.stop = watch([local_peer_id, selected_invite, messages], () => {
this.updateMessages();
});
this.updateMessages();
}

disconnectedCallback() {
this.stop();
}

updateMessages() {
let txt = "";
if (selected_invite.value) {
for (const msg of messages.value) {
const match_from = (
msg.from.peer === selected_invite.value.peer &&
msg.from.app_instance_uuid === selected_invite.value.app_instance_uuid
);
const match_to = (
msg.to.peer === selected_invite.value.peer &&
msg.to.app_instance_uuid === selected_invite.value.app_instance_uuid
);
if (match_from || match_to) {
const from = msg.from.peer === local_peer_id.value ? 'You' : msg.from.peer;
txt += `<${from}>: ${msg.message}\n`;
}
}
}
this.history.textContent = txt;
}
}

customElements.define("chat-message-history", ChatMessageHistory);
62 changes: 62 additions & 0 deletions examples/chat/components/peers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {watch} from "../lib/signaller.js";
import {delegate} from "../lib/events.js";
import {invites, selected_invite} from "../state.js";

export default class ChatPeers extends HTMLElement {
constructor() {
super();
}

connectedCallback() {
this.shadow = this.attachShadow({mode: "open"});
this.shadow.innerHTML = `
<link rel="stylesheet" href="style.css">
<div id="peers"></div>
`;
this.peers = this.shadow.getElementById('peers');
this.cleanup = [
watch([invites], () => this.updatePeers()),
watch([selected_invite], () => this.updateSelected()),
delegate(this.peers, "click", "#peers li", function () {
selected_invite.value = JSON.parse(this.dataset.invite);
}),
];
this.updatePeers();
}

disconnectedCallback() {
for (const destroy of this.cleanup) destroy();
}

updatePeers() {
this.peers.innerHTML = '';
if (invites.value.length === 0) {
const span = document.createElement('span');
span.textContent = "No peers discovered yet";
this.peers.appendChild(span);
} else {
const ul = document.createElement('ul');
for (const invite of invites.value) {
const li = document.createElement('li');
li.textContent = invite.peer;
li.dataset.invite = JSON.stringify(invite);
ul.appendChild(li);
}
this.peers.appendChild(ul);
}
this.updateSelected();
}

updateSelected() {
const json = JSON.stringify(selected_invite.value);
for (const li of this.peers.querySelectorAll("li")) {
if (li.dataset.invite === json) {
li.classList.add("active");
} else {
li.classList.remove("active");
}
}
}
}

customElements.define("chat-peers", ChatPeers);
66 changes: 66 additions & 0 deletions examples/chat/components/send-message-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {bind} from "../lib/events.js";
import {watch} from "../lib/signaller.js";
import {appendMessage, local_peer_id, local_app_uuid, selected_invite} from "../state.js";

export default class ChatSendMessageForm extends HTMLElement {
constructor() {
super();
}

connectedCallback() {
this.shadow = this.attachShadow({mode: "open"});
this.shadow.innerHTML = `
<link rel="stylesheet" href="style.css">
<form id="send-message-form">
<input type="text" name="message" placeholder="Type your message&hellip;" />
<button type="submit">Send</button>
</form>
`;
this.form = this.shadow.getElementById('send-message-form');
this.input = this.shadow.querySelector('[name=message]');
this.cleanup = [
bind(this.form, 'submit', ev => this.submit(ev)),
watch([selected_invite], () => this.updateVisibility()),
];
}

disconnectedCallback() {
for (const destroy of this.cleanup) destroy();
}

updateVisibility() {
if (selected_invite.value) {
this.form.style.display = 'flex';
this.input.focus();
} else {
this.form.style.display = 'none';
}
}

async submit(ev) {
ev.preventDefault();
if (selected_invite.value && local_peer_id.value && local_app_uuid.value) {
const message = this.input.value;
await fetch("/_api/v1/message_send", {
method: "POST",
body: JSON.stringify({
peer: selected_invite.value.peer,
app_instance_uuid: selected_invite.value.app_instance_uuid,
message,
})
});
this.input.value = "";
const from = {
peer: local_peer_id.value,
app_instance_uuid: local_app_uuid.value,
};
const to = {
peer: selected_invite.value.peer,
app_instance_uuid: selected_invite.value.app_instance_uuid,
};
appendMessage(from, to, message);
}
}
}

customElements.define("chat-send-message-form", ChatSendMessageForm);
14 changes: 4 additions & 10 deletions examples/chat/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@
<script type="module" src="main.js"></script>
</head>
<body>
<header>
<h1>Chat example</h1>
<span id="local-peer-id"></span>
</header>
<chat-header></chat-header>
<main>
<div id="peers"></div>
<chat-peers></chat-peers>
<div id="messages">
<pre id="message-history"></pre>
<form id="send-message-form">
<input type="text" name="message" placeholder="Type your message&hellip;" />
<button type="submit">Send</button>
</form>
<chat-message-history></chat-message-history>
<chat-send-message-form></chat-send-message-form>
</div>
</main>
</body>
Expand Down
6 changes: 6 additions & 0 deletions examples/chat/events.js → examples/chat/lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ export function delegate(node, event_name, selector, listener, options) {
// Return undelegate function
return () => node.removeEventListener(event_name, fn);
}

export function bind(node, event_name, listener, options) {
node.addEventListener(event_name, listener, options);
// Return unbind function
return () => node.removeEventListener(event_name, listener);
}
File renamed without changes.
Loading

0 comments on commit e86d8f2

Please sign in to comment.