Addded quote chat message feature

This commit is contained in:
defnax 2026-07-25 22:37:01 +02:00
parent e35dfebe33
commit 6202ff7077
3 changed files with 131 additions and 8 deletions

View File

@ -1058,10 +1058,16 @@ const ChatRoomJoinView = () => {
const Layout = {
dismissMenu: () => {
let redraw = false;
if (ChatHubState.activeMenu) {
ChatHubState.activeMenu = null;
m.redraw();
redraw = true;
}
if (ChatHubState.messageContextMenu && ChatHubState.messageContextMenu.show) {
ChatHubState.messageContextMenu.show = false;
redraw = true;
}
if (redraw) m.redraw();
},
oninit: () => {
ChatHubState.activeTab = 'chat';
@ -1481,7 +1487,45 @@ const Layout = {
'Select a chat room from the left panel to view details or join a conversation.'
),
]),
]),
]),
ChatHubState.messageContextMenu.show && m('.chat-msg-context-menu', {
style: `position: fixed; top: ${ChatHubState.messageContextMenu.y}px; left: ${ChatHubState.messageContextMenu.x}px; background: #ffffff; border: 1px solid #cbd5e1; border-radius: 0.5rem; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); padding: 0.35rem 0; z-index: 3000; min-width: 160px;`,
onclick: (e) => e.stopPropagation(),
}, [
m('.context-menu-item', {
style: 'padding: 0.5rem 1rem; font-size: 0.85rem; font-weight: 600; color: #1e293b; display: flex; align-items: center; gap: 0.6rem; cursor: pointer; transition: background 0.15s ease;',
onmouseenter: (e) => (e.currentTarget.style.background = '#f1f5f9'),
onmouseleave: (e) => (e.currentTarget.style.background = 'transparent'),
onclick: () => {
const { username, messageText } = ChatHubState.messageContextMenu;
const quoteHeader = `> [${username}]: ${messageText}\n`;
const textarea = document.querySelector('.chat-hub-input-area textarea') || document.querySelector('#msginput');
if (textarea) {
textarea.value = (textarea.value ? textarea.value.trim() + '\n' : '') + quoteHeader;
textarea.focus();
}
ChatHubState.messageContextMenu.show = false;
m.redraw();
},
}, [
m('i.fas.fa-quote-right', { style: 'color: #3b82f6;' }),
'Quote Message'
]),
m('.context-menu-item', {
style: 'padding: 0.5rem 1rem; font-size: 0.85rem; font-weight: 600; color: #1e293b; display: flex; align-items: center; gap: 0.6rem; cursor: pointer; transition: background 0.15s ease;',
onmouseenter: (e) => (e.currentTarget.style.background = '#f1f5f9'),
onmouseleave: (e) => (e.currentTarget.style.background = 'transparent'),
onclick: () => {
const { messageText } = ChatHubState.messageContextMenu;
navigator.clipboard.writeText(messageText);
ChatHubState.messageContextMenu.show = false;
m.redraw();
},
}, [
m('i.far.fa-copy', { style: 'color: #64748b;' }),
'Copy Text'
]),
])
]);
},
};

View File

@ -149,7 +149,7 @@ function renderChatMessage(rawText) {
.replaceAll('<br>', '\n')
.replace(new RegExp('<style[^<]*</style>|<[^>]*>', 'gm'), '');
if (cleanText) {
parts.push(renderTextWithEmoji(cleanText));
parts.push(renderFormattedMessageText(cleanText));
}
}
@ -182,11 +182,61 @@ function renderChatMessage(rawText) {
// 3. Normal text message
const cleanText = rawText
.replace(/<blockquote[^>]*>/gi, '\n> ')
.replace(/<\/blockquote>/gi, '\n')
.replaceAll('<br/>', '\n')
.replaceAll('<br>', '\n')
.replace(new RegExp('<style[^<]*</style>|<[^>]*>', 'gm'), '');
return renderTextWithEmoji(cleanText);
return renderFormattedMessageText(cleanText);
}
function renderFormattedMessageText(text) {
if (!text) return '';
const lines = text.split('\n');
const elements = [];
let currentQuoteLines = [];
const flushQuote = () => {
if (currentQuoteLines.length > 0) {
const quoteText = currentQuoteLines.join('\n');
elements.push(
m('blockquote.chat-quote-block', {
style: {
borderLeft: '3px solid #3b82f6',
backgroundColor: '#f8fafc',
color: '#475569',
padding: '0.35rem 0.65rem',
margin: '0.35rem 0',
borderRadius: '0 0.375rem 0.375rem 0',
fontSize: '0.9em',
fontStyle: 'italic',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
}
}, renderTextWithEmoji(quoteText))
);
currentQuoteLines = [];
}
};
lines.forEach((line, idx) => {
if (line.trim().startsWith('>')) {
const lineContent = line.trim().replace(/^>\s?/, '');
currentQuoteLines.push(lineContent);
} else {
flushQuote();
if (line) {
elements.push(renderTextWithEmoji(line));
}
if (idx < lines.length - 1) {
elements.push(m('br'));
}
}
});
flushQuote();
return elements.length > 0 ? elements : renderTextWithEmoji(text);
}
/**
@ -381,13 +431,34 @@ const Message = () => {
const chatType = ChatLobbyModel.currentLobby && ChatLobbyModel.currentLobby.chatType;
const isRoom = chatType === 3;
const handleContextMenu = (e) => {
e.preventDefault();
e.stopPropagation();
const sel = window.getSelection() ? window.getSelection().toString() : '';
const targetText = sel && sel.trim() ? sel : rawText;
ChatHubState.messageContextMenu = {
show: true,
x: e.clientX,
y: e.clientY,
messageText: targetText,
username: username,
};
m.redraw();
};
if (isRoom) {
const nickColor = getNicknameColor(gxsId, username);
return m(
'.message.compact',
m('span.datetime', datetime),
m('span.username', { style: { color: nickColor } }, username + ':'),
m('span.messagetext', renderChatMessage(rawText))
{
oncontextmenu: handleContextMenu,
},
[
m('span.datetime', datetime),
m('span.username', { style: { color: nickColor } }, username + ':'),
m('span.messagetext', renderChatMessage(rawText)),
]
);
}
@ -808,6 +879,13 @@ const ChatHubState = {
historySearchQuery: '',
fullHistoryMessages: [],
isHistoryLoading: false,
messageContextMenu: {
show: false,
x: 0,
y: 0,
messageText: '',
username: '',
},
};
module.exports = {

View File

@ -1,11 +1,11 @@
const m = require('mithril');
const rs = require('rswebui');
const peopleState = require('people/people_state');
const chatState = require('chat/chat_state');
const HistoryBrowserModal = () => {
return {
oninit: (vnode) => {
const chatState = require('chat/chat_state');
const isRoom = vnode.attrs && vnode.attrs.isRoom;
if (isRoom) {
chatState.ChatHubState.historySearchQuery = '';
@ -19,6 +19,7 @@ const HistoryBrowserModal = () => {
}
},
view: (vnode) => {
const chatState = require('chat/chat_state');
const isRoom = vnode.attrs && vnode.attrs.isRoom;
const stateObj = isRoom ? chatState.ChatHubState : peopleState.State;