Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@
"context": "..",
"dockerfile": "./Dockerfile"
},
"forwardPorts": [8282],
"runArgs": ["--publish=8282:8282"]
"forwardPorts": [8282]
}
74 changes: 0 additions & 74 deletions assets/chat/css/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -954,11 +954,6 @@ a.flair12:hover {
background-color: $color-chat-highlight;
}

.msg-highlight-reply {
color: #CCDCD1 !important;
background-color: #224E61 !important;
}

/* Focus or highlight a line */
.focus-user .msg-chat {
opacity:0.3;
Expand Down Expand Up @@ -1816,72 +1811,3 @@ button.btn {
display: block;
font-size: 10px;
}

.msg-reply-preview {
font-size: 0.85em;
color: #888;
margin-bottom: 2px;

.reply-arrow {
margin-right: 4px;
color: #666;
}

.reply-label {
margin-right: 2px;
color: #aaa;
}

.reply-user {
font-weight: 600;
color: #ccc;
margin: 0 2px;
}

.reply-text {
color: #bbb;
font-style: italic;
}
}

#chat-reply-banner {
background: #030303;
border: 1px solid #321a10;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
padding: $chat-gutter-sm;
display: flex;
align-items: center;
justify-content: space-between;
font-size: $text-size-sm;
color: $color-chat-text2;
position: relative;
z-index: 220;
box-shadow: 0px -1px 4px rgba(black, 0.3);
white-space: nowrap;
text-overflow: ellipsis;
}

#chat-reply-container {
display: flex;
}

#chat-reply-user {
color: $color-chat-text1;
font-weight: 500;
margin-left: 5px;
}

.chat-reply-cancel {
cursor: pointer;
display: flex;
font-size: 20px;
align-items: center;
color: $color-chat-text3;
transition: color 0.2s ease;


&:hover {
color: $color-chat-text1;
}
}
144 changes: 7 additions & 137 deletions assets/chat/js/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ const commandsinfo = new Map([
],
["unhideemote", { desc: "Unhide a hidden emote." }],
["spoiler", { desc: "Wraps a message in the spoiler tags `||`." }],
["reply", { desc: "Reply to a user's most recent message." }],
]);
const banstruct = {
id: 0,
Expand Down Expand Up @@ -264,7 +263,6 @@ class Chat {
this.source.on("NAMES", data => this.onNAMES(data));
this.source.on("QUIT", data => this.onQUIT(data));
this.source.on("MSG", data => this.onMSG(data));
this.source.on("MSGREPLY", data => this.onREPLY(data));
this.source.on("MUTE", data => this.onMUTE(data));
this.source.on("UNMUTE", data => this.onUNMUTE(data));
this.source.on("BAN", data => this.onBAN(data));
Expand Down Expand Up @@ -319,7 +317,6 @@ class Chat {
this.cmdHIDEEMOTE(data, "UNHIDEEMOTE")
);
this.control.on("SPOILER", data => this.cmdSPOILER(data))
this.control.on("REPLY", data => this.cmdREPLY(data))

notificationSound.loadConfig();
}
Expand Down Expand Up @@ -444,25 +441,6 @@ class Chat {
e.stopPropagation();
if (!this.authenticated) {
this.loginscrn.show();
return;
}
if (this.input.hasClass("invalid-msg-warning")) return;

const text = this.input.val().toString().trim();
if (!text) return;

const prevMessageId = $("#chat-reply-banner").data("replyTo");
const prevText = $("#chat-reply-banner").data("prevText");
const targetUser = $("#chat-reply-banner").data("targetUser");

if (prevMessageId && prevText && targetUser) {
// Build the MSGREPLY payload
this.source.emit("MSGREPLY", { data: text, nick: this.user.nick, target: targetUser.nick, prev: prevText, prevMessageId: prevMessageId });

// Clear banner state
$("#chat-reply-banner").hide().removeData("replyTo").removeData("prevText").removeData("targetUser");
$("#chat-reply-user").text("");
this.input.val("").trigger("input");
} else {
// don't do anything if the message is marked invalid client-side (currently only when the message is too long)
if (!this.input.hasClass("invalid-msg-warning")) {
Expand Down Expand Up @@ -563,33 +541,6 @@ class Chat {
}
});

// Right click a reply to scroll to the orginal message
this.ui.on("contextmenu", ".msg-reply-preview", function (e) {
e.preventDefault();

const preview = $(this); // the reply preview div
const targetId = preview.data("reply-to"); // uses data-reply-to attr

if (targetId) {
const original = $(`[data-msg-id="${targetId}"]`);

if (original.length) {
original[0].scrollIntoView({ behavior: "smooth", block: "center" });

// temporary highlight
original.addClass("msg-highlight-reply");
setTimeout(() => original.removeClass("msg-highlight-reply"), 1500);
} else {
console.log("Original message not found in DOM:", targetId);
}
}
});

// Cancel a reply to someone
this.ui.on("click", ".chat-reply-cancel", () => {
$("#chat-reply-banner").hide().removeData("replyTo").removeData("prevText").removeData("targetUser");
});

// Visibility
document.addEventListener(
"visibilitychange",
Expand Down Expand Up @@ -1000,35 +951,21 @@ class Chat {
win.lastmessage.user &&
win.lastmessage.user.username.toLowerCase() ===
message.user.username.toLowerCase();
// Gets mentions from message and a reply mentions
message.mentioned = [
...Chat.extractNicks(message.message),
...(message.prevMessage ? Chat.extractNicks(message.prevMessage) : [])
].reduce((m, a) => {
// get mentions from message
message.mentioned = Chat.extractNicks(message.message).reduce((m, a) => {
const user = this.users.get(a.toLowerCase());
if (user && !m.includes(user.nick)) {
m.push(user.nick);
}
return m;
return user ? [...m, user.nick] : m;
}, []);
// add replytarget nick if present
if (message.replytarget?.nick && !message.mentioned.includes(message.replytarget.nick)) {
message.mentioned.push(message.replytarget.nick);
}

// set tagged state
message.tag = this.taggednicks.get(message.user.nick.toLowerCase());
// set highlighted state if this is not the current users message or a bot, as well as other highlight criteria
message.highlighted =
!message.isown &&
!message.user.hasFeature(UserFeatures.BOT) &&
(
// Highlight if the replytarget is the current user
(message.replytarget?.username === this.user.username) ||
// Check current user nick against msg.message (if highlight setting is on)
(this.regexhighlightself &&
this.settings.get("highlight") &&
this.regexhighlightself.test(message.message)) ||
// Check current user nick against msg.message (if highlight setting is on)
((this.regexhighlightself &&
this.settings.get("highlight") &&
this.regexhighlightself.test(message.message)) ||
// Check /highlight nicks against msg.nick
(this.regexhighlightnicks &&
this.regexhighlightnicks.test(message.user.username)) ||
Expand Down Expand Up @@ -1311,21 +1248,6 @@ class Chat {
}
}

onREPLY(data) {
const user = this.users.get(data.nick.toLowerCase());
const target = this.users.get(data.target.toLowerCase());

MessageBuilder.reply(
data.data, // current message
user, // sender user object
target, // target user object
data.prev, // previoustext
data.prevMessageId, // previoustext
data.messageId, // optional message ID
data.timestamp // optional timestamp
).into(this);
}

onMUTE(data) {
// data.data is the nick which has been banned, no info about duration
if (this.user.username.toLowerCase() === data.data.toLowerCase()) {
Expand Down Expand Up @@ -2064,58 +1986,6 @@ class Chat {
this.control.emit("SEND", `|| ${data.join(' ')} ||`)
}

findLastMessageByUser({ nick, id }, win = this.getActiveWindow()) {
const children = win.getlines();

for (let i = children.length - 1; i >= 0; i--) {
const line = $(children[i]);
const msg = line.data("message");

if (!msg) continue;

// Match by ID first
if (id && msg.id === id) {
return msg;
}

// Match by nickname
if (nick && msg.user && msg.user.nick.toLowerCase() === nick.toLowerCase()) {
return msg;
}
}

return null;
}


cmdREPLY(parts) {
if (!parts[0] || !nickregex.test(parts[0])) {
MessageBuilder.error("Usage: /reply <nick> <message>").into(this);
return;
}

const targetNick = parts[0];
const replyText = parts.slice(1).join(" ");

if (!replyText) {
MessageBuilder.error("Reply message cannot be empty").into(this);
return;
}

// Find the last message from that user in the active window
const prevMessage = this.findLastMessageByUser({ nick: targetNick });
if (!prevMessage) {
MessageBuilder.error(`No recent message found from ${targetNick}`).into(this);
return;
}

this.source.emit("MSGREPLY", { data: replyText, nick: this.user.nick, target: targetNick, prev: prevMessage.message, prevMessageId: prevMessage.msgid });

// Add to input history
this.inputhistory.add(`/reply ${targetNick} ${replyText}`);
}


openConversation(nick) {
const normalized = nick.toLowerCase();

Expand Down
19 changes: 0 additions & 19 deletions assets/chat/js/menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,25 +686,6 @@ class ChatContextMenu {
.focus()
.val(`/whisper ${this.targetUsername} `)
})

this.button.reply = this.addButton("contextmenu-reply", (id, e) => {
const msgChat = $(this.event.currentTarget).closest(".msg-chat");
const msgObj = msgChat.data("message");
const prevId = msgChat.attr("data-msg-id") || (msgObj && msgObj.id);
const targetUser = msgObj && msgObj.user;
const prevText = msgObj && msgObj.message;

if (!prevId || !targetUser || !prevText) return;

$("#chat-reply-user").text(targetUser.username ?? targetUser);
$("#chat-reply-banner")
.data("replyTo", prevId)
.data("prevText", prevText)
.data("targetUser", targetUser)
.show();

$("#chat-input-control").focus();
});

if (this.chat.settings.get("highlightnicks").includes(this.targetUsername.toLowerCase())) {
this.button.highlight = this.addButton("contextmenu-unhighlight", (id, e) => {
Expand Down
Loading