forked from retoor/snek
feat: add MessageList and UserList web components with load balancer and sync module
Implement MessageList custom element with real-time message text updates and typing glow animation, add UserList component with relative time formatting, introduce LoadBalancer class for backend process management, create DatasetWebSocketView for key-value sync over WebSocket, override save method in ChannelMessageService to render HTML templates, and return channel_message object from ChatService instead of boolean.
This commit is contained in:
+93
-38
@@ -9,19 +9,19 @@
|
||||
|
||||
|
||||
<section class="chat-area">
|
||||
<div class="chat-messages">
|
||||
{% for message in messages %}
|
||||
{% autoescape false %}
|
||||
{{ message.html }}
|
||||
{% endautoescape %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<message-list class="chat-messages">
|
||||
{% for message in messages %}
|
||||
{% autoescape false %}
|
||||
{{ message.html }}
|
||||
{% endautoescape %}
|
||||
{% endfor %}
|
||||
</message-list>
|
||||
<div class="chat-input">
|
||||
<textarea placeholder="Type a message..." rows="2"></textarea>
|
||||
<textarea list="chat-input-autocomplete-items" placeholder="Type a message..." rows="2" autocomplete="on"></textarea>
|
||||
<upload-button channel="{{ channel.uid.value }}"></upload-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include "online.html" %}
|
||||
<script type="module">
|
||||
import { app } from "/app.js";
|
||||
import { Schedule } from "/schedule.js";
|
||||
@@ -30,18 +30,95 @@
|
||||
function getInputField(){
|
||||
return document.querySelector("textarea")
|
||||
}
|
||||
|
||||
getInputField().autoComplete = {
|
||||
"/online": () =>{
|
||||
showOnlineUsers();
|
||||
},
|
||||
"/clear": () => {
|
||||
document.querySelector(".chat-messages").innerHTML = '';
|
||||
},
|
||||
"/live": () =>{
|
||||
getInputField().liveType = !getInputField().liveType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initInputField(textBox) {
|
||||
textBox.addEventListener('keydown', (e) => {
|
||||
if(textBox.liveType == undefined){
|
||||
textBox.liveType = false
|
||||
}
|
||||
textBox.addEventListener('keydown',async (e) => {
|
||||
if(e.key === "ArrowUp"){
|
||||
const value = findDivAboveText(e.target.value).querySelector('.text')
|
||||
e.target.value = value.textContent
|
||||
console.info("HIERR")
|
||||
return
|
||||
}
|
||||
if (e.key === "Tab") {
|
||||
|
||||
const message = e.target.value.trim();
|
||||
if (!message) {
|
||||
return
|
||||
}
|
||||
let autoCompleteHandler = null;
|
||||
Object.keys(e.target.autoComplete).forEach((key)=>{
|
||||
if(key.startsWith(message)){
|
||||
if(autoCompleteHandler){
|
||||
return
|
||||
}
|
||||
autoCompleteHandler = key
|
||||
}
|
||||
})
|
||||
if(autoCompleteHandler){
|
||||
e.preventDefault();
|
||||
e.target.value = autoCompleteHandler;
|
||||
return
|
||||
}
|
||||
}
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
const message = e.target.value.trim();
|
||||
if (message) {
|
||||
app.rpc.sendMessage(channelUid, message);
|
||||
e.target.value = '';
|
||||
if (!message) {
|
||||
return
|
||||
}
|
||||
let autoCompleteHandler = e.target.autoComplete[message]
|
||||
if(autoCompleteHandler){
|
||||
const value = message;
|
||||
e.target.value = '';
|
||||
autoCompleteHandler(value)
|
||||
return
|
||||
}
|
||||
|
||||
e.target.value = '';
|
||||
if(textBox.liveType && textBox.lastMessageUid && textBox.lastMessageUid != '?'){
|
||||
|
||||
|
||||
app.rpc.updateMessageText(textBox.lastMessageUid, message)
|
||||
textBox.lastMessageUid = null
|
||||
return
|
||||
}
|
||||
|
||||
const messageResponse = await app.rpc.sendMessage(channelUid, message);
|
||||
|
||||
}else{
|
||||
app.rpc.set_typing(channelUid)
|
||||
if(textBox.liveType){
|
||||
|
||||
if(e.target.value[0] == "/"){
|
||||
return
|
||||
}
|
||||
if(!textBox.lastMessageUid){
|
||||
textBox.lastMessageUid = '?'
|
||||
app.rpc.sendMessage(channelUid, e.target.value).then((messageResponse)=>{
|
||||
textBox.lastMessageUid = messageResponse
|
||||
})
|
||||
}
|
||||
if(textBox.lastMessageUid == '?'){
|
||||
return;
|
||||
}
|
||||
app.rpc.updateMessageText(textBox.lastMessageUid, e.target.value)
|
||||
}
|
||||
app.rpc.set_typing(channelUid)
|
||||
|
||||
}
|
||||
});
|
||||
document.querySelector("upload-button").addEventListener("upload",function(e){
|
||||
@@ -81,29 +158,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
function triggerGlow(uid) {
|
||||
document.querySelectorAll(".avatar").forEach((el)=>{
|
||||
const div = el.closest('a');
|
||||
if(el.href.indexOf(uid)!=-1){
|
||||
el.classList.add('glow')
|
||||
let originalColor = el.style.backgroundColor
|
||||
//console.error(originalColor)
|
||||
//el.style.backgroundColor = 'black'
|
||||
setTimeout(()=>{
|
||||
// el.style.backgroundColor = originalColor
|
||||
// console.error(el.style.backgroundColor)
|
||||
el.classList.remove('glow')
|
||||
},1200)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
app.ws.addEventListener("set_typing",(data)=>{
|
||||
triggerGlow(data.data.user_uid)
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
const chatInput = document.querySelector(".chat-area")
|
||||
chatInput.addEventListener("drop", async (e) => {
|
||||
|
||||
Reference in New Issue
Block a user