working messages!!@!@
This commit is contained in:
parent
b24b36adec
commit
92edb123f3
8 changed files with 189 additions and 18 deletions
|
|
@ -121,7 +121,7 @@
|
|||
"placeholder.username": "cat name",
|
||||
"placeholder.captcha.code": "captcha code",
|
||||
"placeholder.message.input": "meow...",
|
||||
"desc.messages.encryption.active": "meows are end-to-end encrypted :3",
|
||||
"desc.messages.loading": "loading meows...",
|
||||
"desc.no.dms": "no direct meowchats :c",
|
||||
"action.dm.opening": "opening meowchat...",
|
||||
"dm.open.failed": "failed to open meowchat :c",
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@
|
|||
"placeholder.username": "username",
|
||||
"placeholder.captcha.code": "captcha code",
|
||||
"placeholder.message.input": "Message...",
|
||||
"desc.messages.encryption.active": "Messages are end-to-end encrypted",
|
||||
"desc.messages.loading": "Fetching messages...",
|
||||
"desc.no.dms": "No direct messages",
|
||||
"action.dm.opening": "Opening DM...",
|
||||
"dm.open.failed": "Failed to open DM",
|
||||
|
|
|
|||
|
|
@ -1645,6 +1645,7 @@ async function gotoHome() {
|
|||
switchRoomContent("title.splash", splashScreen, false);
|
||||
switchRoomsBar("title.home", homeRoomBar);
|
||||
setActiveRoombarItem(null);
|
||||
setupWebSocket();
|
||||
await refreshDms(210);
|
||||
}
|
||||
|
||||
|
|
@ -1852,7 +1853,7 @@ async function openDm(dmId, username, targetId) {
|
|||
|
||||
let msgContainer = document.getElementById("chat-messages");
|
||||
if (msgContainer) {
|
||||
msgContainer.innerHTML = `<div style="text-align: center; opacity: 0.5; padding: 1rem;"><blah>desc.messages.encryption.active</blah></div>`;
|
||||
msgContainer.innerHTML = `<div style="text-align: center; opacity: 0.5; padding: 1rem;"><blah>desc.messages.loading</blah></div>`;
|
||||
let blahTags = msgContainer.getElementsByTagName("blah");
|
||||
for (let i = 0; i < blahTags.length; i++) {
|
||||
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||
|
|
@ -1861,6 +1862,15 @@ async function openDm(dmId, username, targetId) {
|
|||
|
||||
await loadDmMessages(dmId);
|
||||
|
||||
if (dmMessagePollInterval) clearInterval(dmMessagePollInterval);
|
||||
dmMessagePollInterval = setInterval(() => {
|
||||
if (currentDmId === dmId) {
|
||||
loadDmMessages(dmId);
|
||||
} else {
|
||||
clearInterval(dmMessagePollInterval);
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
setActiveRoombarItem(`dm-btn-${dmId}`);
|
||||
clearAction("dmopen");
|
||||
} catch (e) {
|
||||
|
|
@ -1906,7 +1916,8 @@ async function renderMessages(messages) {
|
|||
|
||||
if (msg.key && msg.key !== "") {
|
||||
try {
|
||||
content = await decryptAesGcmFromBase64(content, currentDmKey);
|
||||
let dmKeyBytes = await sha256Bytes(base64ToUint8(currentDmKey));
|
||||
content = await decryptAesGcmFromBase64(content, dmKeyBytes);
|
||||
} catch(e) {
|
||||
content = `<span style="color:var(--error-color)"><blah>error:messages.decrypt.failed</blah></span>`;
|
||||
}
|
||||
|
|
@ -1957,4 +1968,79 @@ async function renderMessages(messages) {
|
|||
|
||||
container.innerHTML = html;
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
if (!currentDmId || !currentDmKey) return;
|
||||
|
||||
let input = document.getElementById("chat-input");
|
||||
let content = input.value.trim();
|
||||
if (!content) return;
|
||||
|
||||
try {
|
||||
let dmKeyBytes = await sha256Bytes(base64ToUint8(currentDmKey));
|
||||
let encryptedContent = await encryptAesGcmToBase64(content, dmKeyBytes);
|
||||
|
||||
let msgPayload = {
|
||||
string1: currentDmId,
|
||||
string2: encryptedContent,
|
||||
string3: ""
|
||||
};
|
||||
|
||||
input.value = "";
|
||||
|
||||
let res = await fetchEncrypted("dm/message/send", JSON.stringify(msgPayload));
|
||||
if (res && res.startsWith("error:")) {
|
||||
showBlahNotification(res);
|
||||
} else {
|
||||
await loadDmMessages(currentDmId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showBlahNotification("error:message.send.failed");
|
||||
}
|
||||
}
|
||||
|
||||
function handleChatInputKey(event) {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
if (!isMobile) {
|
||||
event.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dmMessagePollInterval = null;
|
||||
let appWebSocket = null;
|
||||
|
||||
function setupWebSocket() {
|
||||
if (appWebSocket && appWebSocket.readyState === WebSocket.OPEN) return;
|
||||
|
||||
let wsUrl = url.replace(/^http/, "ws") + "/ws";
|
||||
appWebSocket = new WebSocket(wsUrl);
|
||||
|
||||
appWebSocket.onopen = async () => {
|
||||
try {
|
||||
let nonce = await getNonce(id, passwordHash);
|
||||
let secretEnc = await encryptWithNonce(passwordHash, passwordHash, nonce);
|
||||
appWebSocket.send(JSON.stringify({ string1: id, string2: secretEnc }));
|
||||
} catch (e) {
|
||||
console.error("WS auth failed", e);
|
||||
}
|
||||
};
|
||||
|
||||
appWebSocket.onmessage = (event) => {
|
||||
let data = event.data;
|
||||
if (data.startsWith("dm_message:")) {
|
||||
let msgDmId = data.substring("dm_message:".length);
|
||||
if (currentDmId === msgDmId) {
|
||||
loadDmMessages(msgDmId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
appWebSocket.onclose = () => {
|
||||
setTimeout(setupWebSocket, 5000);
|
||||
};
|
||||
}
|
||||
|
|
@ -132,10 +132,9 @@ var invitesEntry = `
|
|||
var chatScreen = `
|
||||
<div style="display: flex; flex-direction: column; height: 100%; width: 100%;">
|
||||
<div id="chat-messages" style="flex-grow: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<!-- messages go here -->
|
||||
</div>
|
||||
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem;">
|
||||
<input type="text" id="chat-input" class="forminput" style="flex-grow: 1; margin: 0;" placeholder="{blah(placeholder.message.input)}">
|
||||
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(event)"></textarea>
|
||||
<button class="submit-button" onclick="sendMessage()" style="margin: 0; padding: 0; aspect-ratio: 1;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.9 -0.5 15.6 16" fill="none" width="1.5rem">
|
||||
<path stroke="var(--main-bg-color)" stroke-linecap="round" stroke-linejoin="round" d="m3.75 7.5 -1.875 5.625 11.25 -5.625L1.875 1.875l1.875 5.625zm0 0h3.75" stroke-width="1.1"></path>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ loading {
|
|||
user-select: none !important;
|
||||
-webkit-user-drag: none !important;
|
||||
}
|
||||
button, input {
|
||||
button, input, textarea {
|
||||
border-radius: 0.8rem;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
margin: var(--button-margin);
|
||||
|
|
@ -92,7 +92,7 @@ button, input {
|
|||
touch-action: manipulation;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
input {
|
||||
input, textarea {
|
||||
padding-left: calc(var(--button-margin) * 2);
|
||||
-webkit-touch-callout: default;
|
||||
user-select: text !important;
|
||||
|
|
@ -677,7 +677,7 @@ space{
|
|||
border-radius: 0.8rem;
|
||||
transform: translateY(calc( ( var(--icon-button-height) + (var(--button-margin) * 2) ) / 2 - 0.6rem));
|
||||
}
|
||||
button:hover, input:hover {
|
||||
button:hover, input:hover, textarea:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.submit-button:hover {
|
||||
|
|
@ -694,14 +694,14 @@ space{
|
|||
filter: brightness(0.93);
|
||||
}
|
||||
}
|
||||
button:active, button.is-pressed, input:active, input.is-pressed {
|
||||
button:active, button.is-pressed, input:active, input.is-pressed, textarea:active, textarea.is-pressed {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
transform: var(--press-scale);
|
||||
}
|
||||
.invite-actions button:active {
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
button.active, input.active {
|
||||
button.active, input.active, textarea.active {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.submit-button:active, .submit-button.is-pressed {
|
||||
|
|
|
|||
|
|
@ -1645,6 +1645,7 @@ async function gotoHome() {
|
|||
switchRoomContent("title.splash", splashScreen, false);
|
||||
switchRoomsBar("title.home", homeRoomBar);
|
||||
setActiveRoombarItem(null);
|
||||
setupWebSocket();
|
||||
await refreshDms(210);
|
||||
}
|
||||
|
||||
|
|
@ -1861,6 +1862,15 @@ async function openDm(dmId, username, targetId) {
|
|||
|
||||
await loadDmMessages(dmId);
|
||||
|
||||
if (dmMessagePollInterval) clearInterval(dmMessagePollInterval);
|
||||
dmMessagePollInterval = setInterval(() => {
|
||||
if (currentDmId === dmId) {
|
||||
loadDmMessages(dmId);
|
||||
} else {
|
||||
clearInterval(dmMessagePollInterval);
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
setActiveRoombarItem(`dm-btn-${dmId}`);
|
||||
clearAction("dmopen");
|
||||
} catch (e) {
|
||||
|
|
@ -1906,7 +1916,8 @@ async function renderMessages(messages) {
|
|||
|
||||
if (msg.key && msg.key !== "") {
|
||||
try {
|
||||
content = await decryptAesGcmFromBase64(content, currentDmKey);
|
||||
let dmKeyBytes = await sha256Bytes(base64ToUint8(currentDmKey));
|
||||
content = await decryptAesGcmFromBase64(content, dmKeyBytes);
|
||||
} catch(e) {
|
||||
content = `<span style="color:var(--error-color)"><blah>error:messages.decrypt.failed</blah></span>`;
|
||||
}
|
||||
|
|
@ -1957,4 +1968,79 @@ async function renderMessages(messages) {
|
|||
|
||||
container.innerHTML = html;
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
if (!currentDmId || !currentDmKey) return;
|
||||
|
||||
let input = document.getElementById("chat-input");
|
||||
let content = input.value.trim();
|
||||
if (!content) return;
|
||||
|
||||
try {
|
||||
let dmKeyBytes = await sha256Bytes(base64ToUint8(currentDmKey));
|
||||
let encryptedContent = await encryptAesGcmToBase64(content, dmKeyBytes);
|
||||
|
||||
let msgPayload = {
|
||||
string1: currentDmId,
|
||||
string2: encryptedContent,
|
||||
string3: ""
|
||||
};
|
||||
|
||||
input.value = "";
|
||||
|
||||
let res = await fetchEncrypted("dm/message/send", JSON.stringify(msgPayload));
|
||||
if (res && res.startsWith("error:")) {
|
||||
showBlahNotification(res);
|
||||
} else {
|
||||
await loadDmMessages(currentDmId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showBlahNotification("error:message.send.failed");
|
||||
}
|
||||
}
|
||||
|
||||
function handleChatInputKey(event) {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
if (!isMobile) {
|
||||
event.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dmMessagePollInterval = null;
|
||||
let appWebSocket = null;
|
||||
|
||||
function setupWebSocket() {
|
||||
if (appWebSocket && appWebSocket.readyState === WebSocket.OPEN) return;
|
||||
|
||||
let wsUrl = url.replace(/^http/, "ws") + "/ws";
|
||||
appWebSocket = new WebSocket(wsUrl);
|
||||
|
||||
appWebSocket.onopen = async () => {
|
||||
try {
|
||||
let nonce = await getNonce(id, passwordHash);
|
||||
let secretEnc = await encryptWithNonce(passwordHash, passwordHash, nonce);
|
||||
appWebSocket.send(JSON.stringify({ string1: id, string2: secretEnc }));
|
||||
} catch (e) {
|
||||
console.error("WS auth failed", e);
|
||||
}
|
||||
};
|
||||
|
||||
appWebSocket.onmessage = (event) => {
|
||||
let data = event.data;
|
||||
if (data.startsWith("dm_message:")) {
|
||||
let msgDmId = data.substring("dm_message:".length);
|
||||
if (currentDmId === msgDmId) {
|
||||
loadDmMessages(msgDmId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
appWebSocket.onclose = () => {
|
||||
setTimeout(setupWebSocket, 5000);
|
||||
};
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ var chatScreen = `
|
|||
<div id="chat-messages" style="flex-grow: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
</div>
|
||||
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem;">
|
||||
<input type="text" id="chat-input" class="forminput" style="flex-grow: 1; margin: 0;" placeholder="{blah(placeholder.message.input)}">
|
||||
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(event)"></textarea>
|
||||
<button class="submit-button" onclick="sendMessage()" style="margin: 0; padding: 0; aspect-ratio: 1;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.9 -0.5 15.6 16" fill="none" width="1.5rem">
|
||||
<path stroke="var(--main-bg-color)" stroke-linecap="round" stroke-linejoin="round" d="m3.75 7.5 -1.875 5.625 11.25 -5.625L1.875 1.875l1.875 5.625zm0 0h3.75" stroke-width="1.1"></path>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ loading {
|
|||
user-select: none !important;
|
||||
-webkit-user-drag: none !important;
|
||||
}
|
||||
button, input {
|
||||
button, input, textarea {
|
||||
border-radius: 0.8rem;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
margin: var(--button-margin);
|
||||
|
|
@ -92,7 +92,7 @@ button, input {
|
|||
touch-action: manipulation;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
input {
|
||||
input, textarea {
|
||||
padding-left: calc(var(--button-margin) * 2);
|
||||
-webkit-touch-callout: default;
|
||||
user-select: text !important;
|
||||
|
|
@ -677,7 +677,7 @@ space{
|
|||
border-radius: 0.8rem;
|
||||
transform: translateY(calc( ( var(--icon-button-height) + (var(--button-margin) * 2) ) / 2 - 0.6rem));
|
||||
}
|
||||
button:hover, input:hover {
|
||||
button:hover, input:hover, textarea:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.submit-button:hover {
|
||||
|
|
@ -694,14 +694,14 @@ space{
|
|||
filter: brightness(0.93);
|
||||
}
|
||||
}
|
||||
button:active, button.is-pressed, input:active, input.is-pressed {
|
||||
button:active, button.is-pressed, input:active, input.is-pressed, textarea:active, textarea.is-pressed {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
transform: var(--press-scale);
|
||||
}
|
||||
.invite-actions button:active {
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
button.active, input.active {
|
||||
button.active, input.active, textarea.active {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.submit-button:active, .submit-button.is-pressed {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue