diff --git a/src/snek/model/channel_message.py b/src/snek/model/channel_message.py index 524a8a4..7fd1a27 100644 --- a/src/snek/model/channel_message.py +++ b/src/snek/model/channel_message.py @@ -1,6 +1,6 @@ from snek.model.user import UserModel from snek.system.model import BaseModel, ModelField - +from datetime import datetime,timezone class ChannelMessageModel(BaseModel): channel_uid = ModelField(name="channel_uid", required=True, kind=str) @@ -8,6 +8,9 @@ class ChannelMessageModel(BaseModel): message = ModelField(name="message", required=True, kind=str) html = ModelField(name="html", required=False, kind=str) + def get_seconds_since_last_update(self): + return int((datetime.now(timezone.utc) - datetime.fromisoformat(self["updated_at"])).total_seconds()) + async def get_user(self) -> UserModel: return await self.app.services.user.get(uid=self["user_uid"]) diff --git a/src/snek/static/base.css b/src/snek/static/base.css index 54bc61a..479d36b 100644 --- a/src/snek/static/base.css +++ b/src/snek/static/base.css @@ -151,7 +151,7 @@ footer { } } -.chat-messages > picture > img { +.chat-messages picture img { cursor: pointer; } .chat-messages::-webkit-scrollbar { diff --git a/src/snek/static/message-list.js b/src/snek/static/message-list.js index 210c2e8..6415a34 100644 --- a/src/snek/static/message-list.js +++ b/src/snek/static/message-list.js @@ -10,7 +10,7 @@ constructor() { super(); app.ws.addEventListener("update_message_text",(data)=>{ - this.updateMessageText(data.data.message_uid,data.data.text) + this.updateMessageText(data.data.uid,data.data) }) app.ws.addEventListener("set_typing",(data)=>{ this.triggerGlow(data.data.user_uid) @@ -19,14 +19,18 @@ this.items = []; } - updateMessageText(uid,text){ + updateMessageText(uid,message){ const messageDiv = this.querySelector("div[data-uid=\""+uid+"\"]") + if(!messageDiv){ return } + const receivedHtml = document.createElement("div") + receivedHtml.innerHTML = message.html + const html = receivedHtml.querySelector(".text").innerHTML const textElement = messageDiv.querySelector(".text") - textElement.innerText = text - textElement.style.display = text == '' ? 'none' : 'block' + textElement.innerHTML = html + textElement.style.display = message.text == '' ? 'none' : 'block' } triggerGlow(uid) { diff --git a/src/snek/templates/web.html b/src/snek/templates/web.html index 38f723c..113b77d 100644 --- a/src/snek/templates/web.html +++ b/src/snek/templates/web.html @@ -47,7 +47,18 @@ if(textBox.liveType == undefined){ textBox.liveType = false } + let typeTimeout = null; textBox.addEventListener('keydown',async (e) => { + if(typeTimeout){ + clearTimeout(typeTimeout) + typeTimeout = null + } + if(e.target.liveType){ + typeTimeout = setTimeout(()=>{ + e.target.lastMessageUid = null + e.target.value = '' + },3000) + } if(e.key === "ArrowUp"){ const value = findDivAboveText(e.target.value).querySelector('.text') e.target.value = value.textContent @@ -102,7 +113,9 @@ }else{ if(textBox.liveType){ - + if(e.target.value.endsWith("\n") || e.target.value.endsWith(" ")){ + return + } if(e.target.value[0] == "/"){ return } @@ -116,8 +129,10 @@ return; } app.rpc.updateMessageText(textBox.lastMessageUid, e.target.value) + }else{ + app.rpc.set_typing(channelUid) } - app.rpc.set_typing(channelUid) + } }); @@ -294,6 +309,10 @@ } app.addEventListener("channel-message", (data) => { + let display = 'block'; + if(!data.text || !data.text.trim()){ + display = "none"; + } if (data.channel_uid !== channelUid) { if(!isMentionForSomeoneElse(data.message)){ channelSidebar.notify(data); @@ -316,6 +335,7 @@ const message = document.createElement("div"); message.innerHTML = data.html; + message.style.display = display document.querySelector(".chat-messages").appendChild(message.firstChild); updateLayout(doScrollDownBecauseLastMessageIsVisible); setTimeout(() => { @@ -373,6 +393,9 @@ if(e.target.tagName != 'IMG') return const img = e.target + if(e.target.classList.contains('avatar')){ + return + } const overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.top = 0; diff --git a/src/snek/view/rpc.py b/src/snek/view/rpc.py index 0469e53..de94633 100644 --- a/src/snek/view/rpc.py +++ b/src/snek/view/rpc.py @@ -178,22 +178,28 @@ class RPCView(BaseView): message = await self.services.channel_message.get(message_uid) if message["user_uid"] != self.user_uid: raise Exception("Not allowed") - await self.services.socket.broadcast(message["channel_uid"], { - "channel_uid": message["channel_uid"], - "event": "update_message_text", - "data": { - - "message_uid": message_uid, - "text": text - } - }) - message["message"] = text + + if message.get_seconds_since_last_update() > 3: + return {"error": "Message too old","seconds_since_last_update": message.get_seconds_since_last_update(),"success": False} + + message['message'] = text if not text: message['deleted_at'] = now() else: message['deleted_at'] = None + await self.services.channel_message.save(message) - return True + data = message.record + data['text'] = message["message"] + data['message_uid'] = message_uid + + await self.services.socket.broadcast(message["channel_uid"], { + "channel_uid": message["channel_uid"], + "event": "update_message_text", + "data": message.record + }) + + return {"success": True} async def send_message(self, channel_uid, message): self._require_login()