Fix reply text

This commit is contained in:
BordedDev 2025-07-20 03:38:18 +02:00
parent 3e2dd7ea04
commit 8c2e20dfe8
3 changed files with 71 additions and 29 deletions

View File

@ -368,7 +368,7 @@ input[type="text"], .chat-input textarea {
} }
} }
.message.switch-user + .message, .message.long-time + .message, .message:first-child { .message.switch-user + .message, .message.long-time + .message, .message-list-bottom + .message{
.time { .time {
display: block; display: block;
opacity: 1; opacity: 1;

View File

@ -11,8 +11,46 @@ const LONG_TIME = 1000 * 60 * 20
export class ReplyEvent extends Event { export class ReplyEvent extends Event {
constructor(messageTextTarget) { constructor(messageTextTarget) {
super('reply', { bubbles: true, composed: true }); super('reply', { bubbles: true, composed: true });
this.messageTextTarget = messageTextTarget; this.messageTextTarget = messageTextTarget;
const newMessage = messageTextTarget.cloneNode(true);
newMessage.style.maxHeight = "0"
messageTextTarget.parentElement.insertBefore(newMessage, messageTextTarget);
newMessage.querySelectorAll('.embed-url-link').forEach(link => {
link.remove()
})
newMessage.querySelectorAll('picture').forEach(picture => {
const img = picture.querySelector('img');
if (img) {
picture.replaceWith(img);
}
})
newMessage.querySelectorAll('img').forEach(img => {
const src = img.src || img.currentSrc;
img.replaceWith(document.createTextNode(src));
})
newMessage.querySelectorAll('iframe').forEach(iframe => {
const src = iframe.src || iframe.currentSrc;
iframe.replaceWith(document.createTextNode(src));
})
newMessage.querySelectorAll('a').forEach(a => {
const href = a.getAttribute('href');
const text = a.innerText || a.textContent;
if (text === href || text === '') {
a.replaceWith(document.createTextNode(href));
} else {
a.replaceWith(document.createTextNode(`[${text}](${href})`));
}
})
this.replyText = newMessage.innerText.replaceAll("\n\n", "\n").trim();
newMessage.remove()
} }
} }
@ -123,28 +161,33 @@ class MessageList extends HTMLElement {
this.messageMap = new Map(); this.messageMap = new Map();
this.visibleSet = new Set(); this.visibleSet = new Set();
this._observer = new IntersectionObserver((entries) => { this._observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
this.visibleSet.add(entry.target); this.visibleSet.add(entry.target);
const messageElement = entry.target; const messageElement = entry.target;
if (messageElement instanceof MessageElement) { if (messageElement instanceof MessageElement) {
messageElement.updateUI(); messageElement.updateUI();
}
} else {
this.visibleSet.delete(entry.target);
} }
}); } else {
this.visibleSet.delete(entry.target);
}
});
}, { }, {
root: this, root: this,
threshold: 0.1 threshold: 0,
}) })
for(const c of this.children) { for(const c of this.children) {
this._observer.observe(c); this._observer.observe(c);
if (c instanceof MessageElement) { if (c instanceof MessageElement) {
this.messageMap.set(c.dataset.uid, c); this.messageMap.set(c.dataset.uid, c);
} }
} }
this.endOfMessages = document.createElement('div');
this.endOfMessages.classList.add('message-list-bottom');
this.prepend(this.endOfMessages);
this.scrollToBottom(true); this.scrollToBottom(true);
} }
@ -188,7 +231,6 @@ class MessageList extends HTMLElement {
}; };
document.addEventListener('keydown', escListener); document.addEventListener('keydown', escListener);
}) })
} }
isElementVisible(element) { isElementVisible(element) {
if (!element) return false; if (!element) return false;
@ -201,13 +243,13 @@ class MessageList extends HTMLElement {
); );
} }
isScrolledToBottom() { isScrolledToBottom() {
return this.isElementVisible(this.firstElementChild); return this.isElementVisible(this.endOfMessages);
} }
scrollToBottom(force = false, behavior= 'instant') { scrollToBottom(force = false, behavior= 'instant') {
if (force || !this.isScrolledToBottom()) { if (force || !this.isScrolledToBottom()) {
this.firstElementChild.scrollIntoView({ behavior, block: 'start' }); this.firstElementChild.scrollIntoView({ behavior, block: 'end' });
setTimeout(() => { setTimeout(() => {
this.firstElementChild.scrollIntoView({ behavior, block: 'start' }); this.firstElementChild.scrollIntoView({ behavior, block: 'end' });
}, 200); }, 200);
} }
} }
@ -241,7 +283,6 @@ class MessageList extends HTMLElement {
upsertMessage(data) { upsertMessage(data) {
let message = this.messageMap.get(data.uid); let message = this.messageMap.get(data.uid);
const newMessage = !!message;
if (message) { if (message) {
message.parentElement?.removeChild(message); message.parentElement?.removeChild(message);
} }
@ -255,14 +296,14 @@ class MessageList extends HTMLElement {
if (message) { if (message) {
message.updateMessage(...(wrapper.firstElementChild._originalChildren || wrapper.firstElementChild.children)); message.updateMessage(...(wrapper.firstElementChild._originalChildren || wrapper.firstElementChild.children));
} else { } else {
message = wrapper.firstElementChild; message = wrapper.firstElementChild;
this.messageMap.set(data.uid, message); this.messageMap.set(data.uid, message);
this._observer.observe(message); this._observer.observe(message);
} }
const scrolledToBottom = this.isScrolledToBottom(); const scrolledToBottom = this.isScrolledToBottom();
this.prepend(message); this.prepend(message);
if (scrolledToBottom) this.scrollToBottom(true, !newMessage ? 'smooth' : 'auto'); if (scrolledToBottom) this.scrollToBottom(true);
} }
} }

View File

@ -139,12 +139,13 @@ chatInputField.textarea.focus();
// --- Reply helper --- // --- Reply helper ---
function replyMessage(message) { function replyMessage(message) {
chatInputField.value = "```markdown\n> " + (message || '').split("\n").join("\n> ") + "\n```\n"; chatInputField.value = "```markdown\n> " + (message || '').trim().split("\n").join("\n> ") + "\n```\n";
chatInputField.textarea.dispatchEvent(new Event('change', { bubbles: true }));
chatInputField.focus(); chatInputField.focus();
} }
messagesContainer.addEventListener("reply", (e) => { messagesContainer.addEventListener("reply", (e) => {
const messageText = e.messageTextTarget.textContent.trim(); const messageText = e.replyText || e.messageTextTarget.textContent.trim();
replyMessage(messageText); replyMessage(messageText);
}) })