diff --git a/client/index.html b/client/index.html
index f4f876b..968cc68 100644
--- a/client/index.html
+++ b/client/index.html
@@ -33,6 +33,9 @@
diff --git a/client/index.js b/client/index.js
index e201fad..c1d3301 100644
--- a/client/index.js
+++ b/client/index.js
@@ -11,6 +11,7 @@ import {
} from "./sync.js";
import * as render from "./render.js";
import * as devtools from "./devtools.js";
+import * as matrix from "./matrix.js";
let syncv2ServerUrl; // will be populated with the URL of the CS API e.g 'https://matrix-client.matrix.org'
let slidingSync;
@@ -428,4 +429,27 @@ window.addEventListener("load", async (event) => {
// interrupt the sync request to send up new filters
syncConnection.abort();
});
+
+ // hook up the send message input
+ document
+ .getElementById("sendmessageinput")
+ .addEventListener("keydown", async (ev) => {
+ if (ev.key == "Enter") {
+ ev.target.setAttribute("disabled", "");
+ const msg = ev.target.value;
+ try {
+ await matrix.sendMessage(
+ syncv2ServerUrl,
+ document.getElementById("accessToken").value,
+ slidingSync.roomSubscription,
+ msg
+ );
+ ev.target.value = "";
+ } catch (err) {
+ document.getElementById("errorMsg").textContent =
+ "Error sending message: " + err;
+ }
+ ev.target.removeAttribute("disabled");
+ }
+ });
});
diff --git a/client/matrix.js b/client/matrix.js
new file mode 100644
index 0000000..54b4d01
--- /dev/null
+++ b/client/matrix.js
@@ -0,0 +1,106 @@
+/**
+ * This file contains Matrix CS API code. Not relevant for sliding sync, but relevant for general
+ * client code e.g sending events.
+ */
+
+async function doRequest(fullUrl, accessToken, method, body) {
+ const resp = await fetch(fullUrl, {
+ method: method,
+ headers: {
+ Authorization: "Bearer " + accessToken,
+ "Content-Type": "application/json",
+ },
+ body: body ? JSON.stringify(body) : undefined,
+ });
+ if (!resp.ok) {
+ throw new Error("HTTP " + resp.status);
+ }
+ return await resp.json();
+}
+
+async function inviteToRoom(v2serverUrl, accessToken, roomId, userId) {
+ await doRequest(
+ `${v2serverUrl}/_matrix/client/v3/rooms/${encodeURIComponent(
+ roomId
+ )}/invite`,
+ accessToken,
+ "POST",
+ {
+ user_id: userId,
+ }
+ );
+}
+
+async function joinRoom(v2serverUrl, accessToken, roomIdOrAlias) {
+ await doRequest(
+ `${v2serverUrl}/_matrix/client/v3/join/${encodeURIComponent(
+ roomIdOrAlias
+ )}`,
+ accessToken,
+ "POST",
+ {}
+ );
+}
+
+async function leaveRoom(v2serverUrl, accessToken, roomId) {
+ await doRequest(
+ `${v2serverUrl}/_matrix/client/v3/rooms/${encodeURIComponent(
+ roomId
+ )}/leave`,
+ accessToken,
+ "POST",
+ {}
+ );
+}
+
+/**
+ * Send a Matrix event.
+ * @param {string} v2serverUrl The CS API endpoint base URL.
+ * @param {string} accessToken The user's access token.
+ * @param {string} roomId The room to send the event in.
+ * @param {string} rawText The raw text input by the user. The text entered will affect the operation.
+ */
+export async function sendMessage(v2serverUrl, accessToken, roomId, rawText) {
+ rawText = rawText.trim();
+
+ if (rawText.startsWith("/invite ")) {
+ await inviteToRoom(
+ v2serverUrl,
+ accessToken,
+ roomId,
+ rawText.substring("/invite ".length)
+ );
+ return;
+ } else if (rawText.startsWith("/join ")) {
+ await joinRoom(
+ v2serverUrl,
+ accessToken,
+ rawText.substring("/join ".length)
+ );
+ return;
+ } else if (rawText === "/leave") {
+ await leaveRoom(v2serverUrl, accessToken, roomId);
+ return;
+ }
+
+ let jsonBody = {
+ msgtype: "m.text",
+ body: rawText,
+ };
+ if (rawText.startsWith("/me ")) {
+ jsonBody = {
+ msgtype: "m.emote",
+ body: rawText.substring("/me ".length),
+ };
+ }
+ const txnId = "" + new Date().getTime();
+
+ await doRequest(
+ `${v2serverUrl}/_matrix/client/v3/rooms/${encodeURIComponent(
+ roomId
+ )}/send/m.room.message/${txnId}`,
+ accessToken,
+ "PUT",
+ jsonBody
+ );
+}
diff --git a/client/styles.css b/client/styles.css
index 44ae0b3..7718f3a 100644
--- a/client/styles.css
+++ b/client/styles.css
@@ -142,3 +142,11 @@ body {
border: 1px solid;
padding: 5px;
}
+
+#roomfooter {
+ margin: 5px;
+}
+
+#sendmessageinput {
+ width: 100%;
+}