mirror of
https://github.com/thelounge/thelounge
synced 2024-11-22 20:13:07 +00:00
Merge pull request #1709 from MaxLeiter/master
Link nicks mentioned in messages
This commit is contained in:
commit
4e2eed2023
6 changed files with 113 additions and 5 deletions
17
client/js/libs/handlebars/ircmessageparser/findNames.js
Normal file
17
client/js/libs/handlebars/ircmessageparser/findNames.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
"use strict";
|
||||
|
||||
function findNames(text, users) {
|
||||
const result = [];
|
||||
let index = 0;
|
||||
users.forEach((nick) => {
|
||||
index = text.indexOf(nick, index);
|
||||
result.push({
|
||||
start: index,
|
||||
end: index + nick.length,
|
||||
nick: nick,
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = findNames;
|
|
@ -6,7 +6,9 @@ const anyIntersection = require("./ircmessageparser/anyIntersection");
|
|||
const findChannels = require("./ircmessageparser/findChannels");
|
||||
const findLinks = require("./ircmessageparser/findLinks");
|
||||
const findEmoji = require("./ircmessageparser/findEmoji");
|
||||
const findNames = require("./ircmessageparser/findNames");
|
||||
const merge = require("./ircmessageparser/merge");
|
||||
const colorClass = require("./colorClass");
|
||||
|
||||
// Create an HTML `span` with styling information for a given fragment
|
||||
function createFragment(fragment) {
|
||||
|
@ -47,9 +49,14 @@ function createFragment(fragment) {
|
|||
return escapedText;
|
||||
}
|
||||
|
||||
// Transform an IRC message potentially filled with styling control codes, URLs
|
||||
// and channels into a string of HTML elements to display on the client.
|
||||
module.exports = function parse(text) {
|
||||
// Transform an IRC message potentially filled with styling control codes, URLs,
|
||||
// nicknames, and channels into a string of HTML elements to display on the client.
|
||||
module.exports = function parse(text, users) {
|
||||
// if it's not the users we're expecting, but rather is passed from Handlebars (occurs when users passed to template is null or undefined)
|
||||
if (users && users.hash) {
|
||||
users = [];
|
||||
}
|
||||
|
||||
// Extract the styling information and get the plain text version from it
|
||||
const styleFragments = parseStyle(text);
|
||||
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
||||
|
@ -62,11 +69,13 @@ module.exports = function parse(text) {
|
|||
const channelParts = findChannels(cleanText, channelPrefixes, userModes);
|
||||
const linkParts = findLinks(cleanText);
|
||||
const emojiParts = findEmoji(cleanText);
|
||||
const nameParts = findNames(cleanText, (users || []));
|
||||
|
||||
// Sort all parts identified based on their position in the original text
|
||||
const parts = channelParts
|
||||
.concat(linkParts)
|
||||
.concat(emojiParts)
|
||||
.concat(nameParts)
|
||||
.sort((a, b) => a.start - b.start || b.end - a.end)
|
||||
.reduce((prev, curr) => {
|
||||
const intersection = prev.some((p) => anyIntersection(p, curr));
|
||||
|
@ -77,7 +86,7 @@ module.exports = function parse(text) {
|
|||
return prev.concat([curr]);
|
||||
}, []);
|
||||
|
||||
// Merge the styling information with the channels / URLs / text objects and
|
||||
// Merge the styling information with the channels / URLs / nicks / text objects and
|
||||
// generate HTML strings with the resulting fragments
|
||||
return merge(parts, styleFragments).map((textPart) => {
|
||||
// Create HTML strings with styling information
|
||||
|
@ -92,6 +101,9 @@ module.exports = function parse(text) {
|
|||
return `<span class="inline-channel" role="button" tabindex="0" data-chan="${escapedChannel}">${fragments}</span>`;
|
||||
} else if (textPart.emoji) {
|
||||
return `<span class="emoji">${fragments}</span>`;
|
||||
} else if (textPart.nick) {
|
||||
const nick = Handlebars.Utils.escapeExpression(textPart.nick);
|
||||
return `<span role="button" class="user ${colorClass(nick)}" data-name="${nick}">${fragments}</span>`;
|
||||
}
|
||||
|
||||
return fragments;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{{/if}}
|
||||
</span>
|
||||
<span class="content">
|
||||
<span class="text">{{{parse text}}}</span>
|
||||
<span class="text">{{{parse text users}}}</span>
|
||||
|
||||
{{#each previews}}
|
||||
<div class="preview" data-url="{{link}}"></div>
|
||||
|
|
|
@ -4,6 +4,7 @@ const Chan = require("../../models/chan");
|
|||
const Msg = require("../../models/msg");
|
||||
const LinkPrefetch = require("./link");
|
||||
const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage");
|
||||
const nickRegExp = /[\w[\]\\`^{|}-]{4,}/gi;
|
||||
|
||||
module.exports = function(irc, network) {
|
||||
const client = this;
|
||||
|
@ -88,6 +89,14 @@ module.exports = function(irc, network) {
|
|||
highlight = network.highlightRegex.test(data.message);
|
||||
}
|
||||
|
||||
const users = [];
|
||||
let match;
|
||||
while ((match = nickRegExp.exec(data.message))) {
|
||||
if (chan.findUser(match[0])) {
|
||||
users.push(match[0]);
|
||||
}
|
||||
}
|
||||
|
||||
const msg = new Msg({
|
||||
type: data.type,
|
||||
time: data.time,
|
||||
|
@ -95,6 +104,7 @@ module.exports = function(irc, network) {
|
|||
text: data.message,
|
||||
self: self,
|
||||
highlight: highlight,
|
||||
users: users,
|
||||
});
|
||||
|
||||
// No prefetch URLs unless are simple MESSAGE or ACTION types
|
||||
|
|
26
test/client/js/libs/handlebars/ircmessageparser/findNames.js
Normal file
26
test/client/js/libs/handlebars/ircmessageparser/findNames.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
"use strict";
|
||||
|
||||
const expect = require("chai").expect;
|
||||
const findNames = require("../../../../../../client/js/libs/handlebars/ircmessageparser/findNames");
|
||||
|
||||
describe("findNames", () => {
|
||||
it("should find nicks in text", () => {
|
||||
const input = "<MaxLeiter>: Hello, xPaw, how's it going?";
|
||||
const expected = [
|
||||
{
|
||||
start: 1,
|
||||
end: 10,
|
||||
nick: "MaxLeiter",
|
||||
},
|
||||
{
|
||||
start: 20,
|
||||
end: 24,
|
||||
nick: "xPaw",
|
||||
},
|
||||
];
|
||||
const nicks = ["MaxLeiter", "xPaw"];
|
||||
const actual = findNames(input, nicks);
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
});
|
||||
});
|
|
@ -243,6 +243,49 @@ describe("parse Handlebars helper", () => {
|
|||
expect(actual).to.deep.equal(expected);
|
||||
});
|
||||
|
||||
it("should find nicks", () => {
|
||||
const testCases = [{
|
||||
users: ["MaxLeiter"],
|
||||
input: "test, MaxLeiter",
|
||||
expected:
|
||||
"test, " +
|
||||
"<span role=\"button\" class=\"user color-12\" data-name=\"MaxLeiter\">" +
|
||||
"MaxLeiter" +
|
||||
"</span>",
|
||||
}];
|
||||
|
||||
const actual = testCases.map((testCase) => parse(testCase.input, testCase.users));
|
||||
const expected = testCases.map((testCase) => testCase.expected);
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
});
|
||||
|
||||
it("should not find nicks", () => {
|
||||
const testCases = [{
|
||||
users: ["MaxLeiter, test"],
|
||||
input: "#test-channelMaxLeiter",
|
||||
expected:
|
||||
"<span class=\"inline-channel\" role=\"button\" tabindex=\"0\" data-chan=\"#test-channelMaxLeiter\">" +
|
||||
"#test-channelMaxLeiter" +
|
||||
"</span>",
|
||||
},
|
||||
{
|
||||
users: ["MaxLeiter, test"],
|
||||
input: "https://www.MaxLeiter.com/test",
|
||||
expected:
|
||||
"<a href=\"https://www.MaxLeiter.com/test\" target=\"_blank\" rel=\"noopener\">" +
|
||||
"https://www.MaxLeiter.com/test" +
|
||||
"</a>",
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
const actual = testCases.map((testCase) => parse(testCase.input));
|
||||
const expected = testCases.map((testCase) => testCase.expected);
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
});
|
||||
|
||||
it("should go bonkers like mirc", () => {
|
||||
const testCases = [{
|
||||
input: "\x02irc\x0f://\x1dfreenode.net\x0f/\x034,8thelounge",
|
||||
|
|
Loading…
Reference in a new issue