Update.
This commit is contained in:
parent
2deb8a2069
commit
f770dcf2db
@ -49,6 +49,7 @@ CREATE TABLE IF NOT EXISTS channel_member (
|
|||||||
is_read_only BOOLEAN,
|
is_read_only BOOLEAN,
|
||||||
label TEXT,
|
label TEXT,
|
||||||
new_count BIGINT,
|
new_count BIGINT,
|
||||||
|
last_read_at TEXT,
|
||||||
uid TEXT,
|
uid TEXT,
|
||||||
updated_at TEXT,
|
updated_at TEXT,
|
||||||
user_uid TEXT,
|
user_uid TEXT,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from snek.system.service import BaseService
|
from snek.system.service import BaseService
|
||||||
|
from snek.system.model import now
|
||||||
|
|
||||||
|
|
||||||
class ChannelMemberService(BaseService):
|
class ChannelMemberService(BaseService):
|
||||||
@ -8,6 +9,7 @@ class ChannelMemberService(BaseService):
|
|||||||
async def mark_as_read(self, channel_uid, user_uid):
|
async def mark_as_read(self, channel_uid, user_uid):
|
||||||
channel_member = await self.get(channel_uid=channel_uid, user_uid=user_uid)
|
channel_member = await self.get(channel_uid=channel_uid, user_uid=user_uid)
|
||||||
channel_member["new_count"] = 0
|
channel_member["new_count"] = 0
|
||||||
|
channel_member["last_read_at"] = now()
|
||||||
return await self.save(channel_member)
|
return await self.save(channel_member)
|
||||||
|
|
||||||
async def get_user_uids(self, channel_uid):
|
async def get_user_uids(self, channel_uid):
|
||||||
|
|||||||
@ -10,6 +10,9 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit/lib/xterm-addon-fit.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit/lib/xterm-addon-fit.js"></script>
|
||||||
|
|
||||||
<div id="terminal" class="hidden"></div>
|
<div id="terminal" class="hidden"></div>
|
||||||
|
<button id="jump-to-unread-btn" style="display: none; position: absolute; top: 10px; right: 10px; z-index: 1000; padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2);">
|
||||||
|
Jump to First Unread
|
||||||
|
</button>
|
||||||
<message-list class="chat-messages">
|
<message-list class="chat-messages">
|
||||||
{% if not messages %}
|
{% if not messages %}
|
||||||
|
|
||||||
@ -300,7 +303,76 @@ function isScrolledPastHalf() {
|
|||||||
// --- Initial layout update ---
|
// --- Initial layout update ---
|
||||||
updateLayout(true);
|
updateLayout(true);
|
||||||
|
|
||||||
|
// --- Jump to unread functionality ---
|
||||||
|
const jumpToUnreadBtn = document.getElementById('jump-to-unread-btn');
|
||||||
|
let firstUnreadMessageUid = null;
|
||||||
|
|
||||||
|
async function checkForUnreadMessages() {
|
||||||
|
try {
|
||||||
|
const uid = await app.rpc.getFirstUnreadMessageUid(channelUid);
|
||||||
|
if (uid) {
|
||||||
|
firstUnreadMessageUid = uid;
|
||||||
|
const messageElement = messagesContainer.querySelector(`[data-uid="${uid}"]`);
|
||||||
|
if (messageElement && !messagesContainer.isElementVisible(messageElement)) {
|
||||||
|
jumpToUnreadBtn.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
jumpToUnreadBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
jumpToUnreadBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking for unread messages:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jumpToUnread() {
|
||||||
|
if (!firstUnreadMessageUid) {
|
||||||
|
await checkForUnreadMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstUnreadMessageUid) {
|
||||||
|
let messageElement = messagesContainer.querySelector(`[data-uid="${firstUnreadMessageUid}"]`);
|
||||||
|
|
||||||
|
if (!messageElement) {
|
||||||
|
const messages = await app.rpc.getMessages(channelUid, 0, null);
|
||||||
|
const targetMessage = messages.find(m => m.uid === firstUnreadMessageUid);
|
||||||
|
|
||||||
|
if (targetMessage) {
|
||||||
|
const temp = document.createElement("div");
|
||||||
|
temp.innerHTML = targetMessage.html;
|
||||||
|
const newMessageElement = temp.firstChild;
|
||||||
|
|
||||||
|
messagesContainer.endOfMessages.after(newMessageElement);
|
||||||
|
messagesContainer.messageMap.set(targetMessage.uid, newMessageElement);
|
||||||
|
messagesContainer._observer.observe(newMessageElement);
|
||||||
|
|
||||||
|
messageElement = newMessageElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageElement) {
|
||||||
|
messageElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
messageElement.style.animation = 'highlight-fade 2s';
|
||||||
|
setTimeout(() => {
|
||||||
|
messageElement.style.animation = '';
|
||||||
|
}, 2000);
|
||||||
|
jumpToUnreadBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpToUnreadBtn.addEventListener('click', jumpToUnread);
|
||||||
|
checkForUnreadMessages();
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
@keyframes highlight-fade {
|
||||||
|
0% { background-color: rgba(255, 255, 0, 0.3); }
|
||||||
|
100% { background-color: transparent; }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -175,6 +175,26 @@ class RPCView(BaseView):
|
|||||||
messages.append(extended_dict)
|
messages.append(extended_dict)
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
|
async def get_first_unread_message_uid(self, channel_uid):
|
||||||
|
self._require_login()
|
||||||
|
channel_member = await self.services.channel_member.get(
|
||||||
|
channel_uid=channel_uid, user_uid=self.user_uid
|
||||||
|
)
|
||||||
|
if not channel_member:
|
||||||
|
return None
|
||||||
|
|
||||||
|
last_read_at = channel_member.get("last_read_at")
|
||||||
|
if not last_read_at:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async for message in self.services.channel_message.query(
|
||||||
|
"SELECT uid FROM channel_message WHERE channel_uid=:channel_uid AND created_at > :last_read_at AND deleted_at IS NULL ORDER BY created_at ASC LIMIT 1",
|
||||||
|
{"channel_uid": channel_uid, "last_read_at": last_read_at}
|
||||||
|
):
|
||||||
|
return message["uid"]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
async def get_channels(self):
|
async def get_channels(self):
|
||||||
self._require_login()
|
self._require_login()
|
||||||
channels = []
|
channels = []
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user