Track and view replies (#133)

* Track and view replies

* Tweaks to reply saving (PR #133)

Co-authored-by: chylex <contact@chylex.com>
This commit is contained in:
Chupalika 2021-03-28 08:28:11 -04:00 committed by GitHub
parent 8c825aeb86
commit 183094ad63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 133 additions and 36 deletions

View File

@ -28,6 +28,7 @@ e
a
t
d
r
te
tag
avatar
@ -50,10 +51,13 @@ embeds
attachments
title
description
reply
toDate
memoizedProps
props
children
channel
messages
msSaveBlob
msSaveBlob
messageReference
message_id

View File

@ -56,7 +56,8 @@
* {
* url: <attachment url>
* }, ...
* ]
* ],
* r: <reply message id> // only present if referencing another message (reply)
* }, ...
* }, ...
* }
@ -234,6 +235,10 @@ class SAVEFILE{
}));
}
if (discordMessage.messageReference !== null){
obj.r = discordMessage.messageReference.message_id;
}
return obj;
}

View File

@ -41,6 +41,36 @@ var DISCORD = (function(){
var templateEmbedRichUnsupported;
var templateEmbedDownload;
var processMessageContents = function(contents){
var processed = DOM.escapeHTML(contents.replace(REGEX.formatUrlNoEmbed, "$1"));
if (STATE.settings.enableFormatting){
var escapeHtmlMatch = (full, match) => "&#"+match.charCodeAt(0)+";";
processed = processed
.replace(REGEX.specialEscapedBacktick, "&#96;")
.replace(REGEX.formatCodeBlock, (full, ignore, match) => "<code class='block'>"+match.replace(REGEX.specialUnescaped, escapeHtmlMatch)+"</code>")
.replace(REGEX.formatCodeInline, (full, ignore, match) => "<code class='inline'>"+match.replace(REGEX.specialUnescaped, escapeHtmlMatch)+"</code>")
.replace(REGEX.specialEscapedSingle, escapeHtmlMatch)
.replace(REGEX.specialEscapedDouble, full => full.replace(/\\/g, "").replace(/(.)/g, escapeHtmlMatch))
.replace(REGEX.formatBold, "<b>$1</b>")
.replace(REGEX.formatItalic, (full, pre, match) => pre === '\\' ? full : (pre || "")+"<i>"+match+"</i>")
.replace(REGEX.formatUnderline, "<u>$1</u>")
.replace(REGEX.formatStrike, "<s>$1</s>");
}
var animatedEmojiExtension = STATE.settings.enableAnimatedEmoji ? "gif" : "png";
processed = processed
.replace(REGEX.formatUrl, "<a href='$1' target='_blank' rel='noreferrer'>$1</a>")
.replace(REGEX.mentionChannel, (full, match) => "<span class='link mention-chat'>#"+STATE.getChannelName(match)+"</span>")
.replace(REGEX.mentionUser, (full, match) => "<span class='link mention-user' title='#"+(STATE.getUserTag(match) || "????")+"'>@"+STATE.getUserName(match)+"</span>")
.replace(REGEX.customEmojiStatic, "<img src='https://cdn.discordapp.com/emojis/$2.png' alt=':$1:' title=':$1:' class='emoji'>")
.replace(REGEX.customEmojiAnimated, "<img src='https://cdn.discordapp.com/emojis/$2."+animatedEmojiExtension+"' alt=':$1:' title=':$1:' class='emoji'>");
return "<p>"+processed+"</p>";
};
return {
setup: function(){
templateChannelServer = new TEMPLATE([
@ -59,18 +89,22 @@ var DISCORD = (function(){
templateMessageNoAvatar = new TEMPLATE([
"<div>",
"<div>{reply}</div>",
"<h2><strong class='username' title='#{user.tag}'>{user.name}</strong><span class='info time'>{timestamp}</span>{edit}{jump}</h2>",
"<div class='message'>{contents}{embeds}{attachments}</div>",
"</div>"
].join(""));
templateMessageWithAvatar = new TEMPLATE([
"<div>",
"<div class='reply-message'>{reply}</div>",
"<div class='avatar-wrapper'>",
"<div class='avatar'>{avatar}</div>",
"<div>",
"<h2><strong class='username' title='#{user.tag}'>{user.name}</strong><span class='info time'>{timestamp}</span>{edit}{jump}</h2>",
"<div class='message'>{contents}{embeds}{attachments}</div>",
"</div>",
"</div>",
"</div>"
].join(""));
@ -128,37 +162,7 @@ var DISCORD = (function(){
return getHumanReadableTime(value);
}
else if (property === "contents"){
if (value == null || value.length === 0){
return "";
}
var processed = DOM.escapeHTML(value.replace(REGEX.formatUrlNoEmbed, "$1"));
if (STATE.settings.enableFormatting){
var escapeHtmlMatch = (full, match) => "&#"+match.charCodeAt(0)+";";
processed = processed
.replace(REGEX.specialEscapedBacktick, "&#96;")
.replace(REGEX.formatCodeBlock, (full, ignore, match) => "<code class='block'>"+match.replace(REGEX.specialUnescaped, escapeHtmlMatch)+"</code>")
.replace(REGEX.formatCodeInline, (full, ignore, match) => "<code class='inline'>"+match.replace(REGEX.specialUnescaped, escapeHtmlMatch)+"</code>")
.replace(REGEX.specialEscapedSingle, escapeHtmlMatch)
.replace(REGEX.specialEscapedDouble, full => full.replace(/\\/g, "").replace(/(.)/g, escapeHtmlMatch))
.replace(REGEX.formatBold, "<b>$1</b>")
.replace(REGEX.formatItalic, (full, pre, match) => pre === '\\' ? full : (pre || "")+"<i>"+match+"</i>")
.replace(REGEX.formatUnderline, "<u>$1</u>")
.replace(REGEX.formatStrike, "<s>$1</s>");
}
var animatedEmojiExtension = STATE.settings.enableAnimatedEmoji ? "gif" : "png";
processed = processed
.replace(REGEX.formatUrl, "<a href='$1' target='_blank' rel='noreferrer'>$1</a>")
.replace(REGEX.mentionChannel, (full, match) => "<span class='link mention-chat'>#"+STATE.getChannelName(match)+"</span>")
.replace(REGEX.mentionUser, (full, match) => "<span class='link mention-user' title='#"+(STATE.getUserTag(match) || "????")+"'>@"+STATE.getUserName(match)+"</span>")
.replace(REGEX.customEmojiStatic, "<img src='https://cdn.discordapp.com/emojis/$2.png' alt=':$1:' title=':$1:' class='emoji'>")
.replace(REGEX.customEmojiAnimated, "<img src='https://cdn.discordapp.com/emojis/$2."+animatedEmojiExtension+"' alt=':$1:' title=':$1:' class='emoji'>");
return "<p>"+processed+"</p>";
return value == null || value.length === 0 ? "" : processMessageContents(value);
}
else if (property === "embeds"){
if (!value){
@ -200,6 +204,17 @@ var DISCORD = (function(){
else if (property === "jump"){
return STATE.hasActiveFilter ? "<span class='info jump' data-jump='" + value + "'>Jump to message</span>" : "";
}
else if (property === "reply"){
if (value === null) {
return "";
}
var user = "<span class='reply-username' title='#" + (value.user.tag ? value.user.tag : "????") + "'>" + value.user.name + "</span>";
var avatar = value.avatar ? "<span class='reply-avatar'>" + templateUserAvatar.apply(value.avatar) + "</span>" : "";
var contents = value.contents ? "<span class='reply-contents'>" + processMessageContents(value.contents) + "</span>" : "";
return "<span class='jump' data-jump='" + value.id + "'>Jump to reply</span><span class='user'>" + avatar + user + "</span>" + contents;
}
});
}
};

View File

@ -37,7 +37,7 @@ var STATE = (function(){
ROOT.onChannelsRefreshed = function(callback){
eventOnChannelsRefreshed = callback;
};
ROOT.onMessagesRefreshed = function(callback){
eventOnMessagesRefreshed = callback;
};
@ -147,7 +147,17 @@ var STATE = (function(){
var message = messages[key];
var user = FILE.getUser(message.u);
var avatar = user.avatar ? { id: FILE.getUserId(message.u), path: user.avatar } : null;
var reply = ("r" in message && message.r in messages) ? messages[message.r] : null;
var replyUser = reply ? FILE.getUser(reply.u) : null;
var replyAvatar = replyUser && replyUser.avatar ? { id: FILE.getUserId(reply.u), path: replyUser.avatar } : null;
var replyObj = reply ? {
"id": message.r,
"user": replyUser,
"avatar": replyAvatar,
"contents": reply.m
} : null;
return {
"user": user,
"avatar": avatar,
@ -156,7 +166,8 @@ var STATE = (function(){
"embeds": message.e,
"attachments": message.a,
"edit": ("te" in message) ? message.te : (message.f & 1) === 1,
"jump": key
"jump": key,
"reply": replyObj
};
});
};

View File

@ -154,3 +154,65 @@
vertical-align: -30%;
object-fit: contain;
}
.reply-message {
margin: 0 0 -2px 52px;
display: flex;
align-items: baseline;
flex-wrap: wrap;
line-height: 120%;
white-space: nowrap;
}
.reply-message .jump {
color: rgba(255, 255, 255, 0.4);
font-size: 12px;
text-underline-offset: 1px;
margin-right: 7px;
}
.reply-message .emoji {
width: 16px;
height: 16px;
vertical-align: -20%;
object-fit: contain;
}
.reply-message .user {
margin-right: 5px;
}
.reply-avatar {
margin-right: 4px;
}
.reply-avatar img {
width: 16px;
border-radius: 50%;
vertical-align: middle;
}
.reply-username {
color: #FFF;
font-size: 12px;
font-weight: 600;
letter-spacing: 0;
}
.reply-contents {
display: inline-block;
color: rgba(255, 255, 255, 0.7);
font-size: 12px;
max-width: calc(80%);
}
.reply-contents p {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.reply-contents a {
color: #0096CF;
text-decoration: none;
}