-
Notifications
You must be signed in to change notification settings - Fork 703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support multiple previews per message #1303
Conversation
client/js/render.js
Outdated
@@ -90,6 +99,54 @@ function buildChatMessage(data) { | |||
return msg; | |||
} | |||
|
|||
function renderPreview(preview, msg) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we keep all this preview code in msg_preview
, otherwise we will end up having this render file huge again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do that, then we need to do a require("client/js/socket-events/msg_preview")
in this file, which I think we shouldn't do. The code in this function is very much related to the renderer, so I think we should leave it there, and modularize the renderer in a later PR if necessary.
For the click
event, however, I'm less happy to move it here, but we have the same need for require
otherwise :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why exactly do you need it? You already append the preview in template, and you seem to be duplicating it here.
You're probably scared of the socket-events
folder, but at least all the related code will be in a single file, instead of being here in a somewhat generic render file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previews aren't appended in the template anymore.
I'm not scared of the socket-events
folder lol. When refreshing the page, we're not getting the preview as a socket event, it doesn't make sense to load it from there. Everything gets loaded from the chain in render
(build channels -> build messages -> etc.), and the rendering of previews is a part of that chain. So definitely makes sense to have it there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having it as part of render doesn't stop us from splitting the code.
client/js/render.js
Outdated
data.msg.preview.shown = options.shouldOpenMessagePreview(data.msg.preview.type); | ||
if (data.msg.previews.length) { | ||
data.msg.previews.forEach((preview) => { | ||
preview.shown = options.shouldOpenMessagePreview(preview.type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wtf, you want to open all previews at once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? I haven't improved/changed the UI one bit, so before that PR, if you were posting a URL and that would show as open, with this PR that same behavior applies for multiple URLs.
One reason that I didn't touch UI is because you are improving it in #1301 and I think we should keep those improvements there.
I'm still debating whether we want the caret right next to the URLs (à la Slack) or not, but to be honest the current UI works fairly well even with multiple URLs. Sure, it's taking a lot of space for nothing, but I'm not saying it's definitive :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're changing how many previews are rendered though. So with this PR I assume sending 5 links will render all 5 previews at once? Seems like quite the overkill.
if (res === null) { | ||
return; | ||
} | ||
Array.from(new Set( // Remove duplicate links |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add proper caching instead of just deduping? As long as lounge process is running, it would keep X recent urls in memory (and block while one request is in flight, like await an existing promise).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the intent, but this goes way beyond the goal of this PR and the time I can allocate on this. I would rather keep the semi-naive solution I have right now (which is not worse than what we have in master
anyway), and we can work on proper caching/deduping in a later PR, IMO.
Is this going to append expand button per new line after the text? You added divs with urls, but I don't see any code changes to move buttons there. |
client/views/msg.tpl
Outdated
@@ -9,8 +9,8 @@ | |||
</span> | |||
<span class="text"> | |||
{{~{parse text}~}} | |||
{{#if preview}} | |||
{{#each previews}} | |||
{{> msg_preview}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to match what you're doing in appends.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, this should just all go, I just forgot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
client/js/libs/handlebars/parse.js
Outdated
@@ -81,5 +81,6 @@ module.exports = function parse(text) { | |||
} | |||
|
|||
return fragments; | |||
}).join(""); | |||
}).join("") + | |||
linkParts.map((part) => `<div data-url="${part.link}"></div>`).join(""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
part.link
is not escaped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Is there something better I could do though?
Add a test with multiple URLs, both for parse and link previews. |
52184eb
to
007569e
Compare
Correct. Not ideal, but not a definitive solution, and I'm leaving UI improvements to your #1301 that we can build upon after this PR lands. I'm also not planning to release this PR without your work in #1301 :) |
That PR is unnecessary if buttons are going to appear after links. |
What do you mean? I already did add/update tests, no? |
There's no test that has multiple |
client/js/libs/handlebars/parse.js
Outdated
@@ -81,5 +81,8 @@ module.exports = function parse(text) { | |||
} | |||
|
|||
return fragments; | |||
}).join("") + linkParts.map((part) => { | |||
const escapedLink = Handlebars.Utils.escapeExpression(part.link); | |||
return `<div data-url="${escapedLink}"></div>`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, escaping this is going to make finding stuff with jQuery difficult (it's matching based on preview.link
, which is unescaped). Do we actually need to escape this, here? It's not for show, it's mostly a key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want a XSS? That's how you're going to get an XSS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough 😅 What do you recommend? Because right now rendering of something that needs escaping will fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this is fixed now, yay
Are we set on this idea though? I think it's reasonable to settle on the actual capability of having multiple previews, and then play with different UIs, as yours is already an improvement.
Will add a test for |
007569e
to
5c2fd6b
Compare
Test added. |
c87abd4
to
4ae1629
Compare
@@ -72,6 +75,12 @@ function buildChatMessage(data) { | |||
const msg = $(templates[template](data.msg)); | |||
const text = msg.find(".text"); | |||
|
|||
if (data.msg.previews.length) { | |||
data.msg.previews.forEach((preview) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be oneliner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't because of this rule: http://eslint.org/docs/rules/brace-style.
Alternative is to do data.msg.previews.forEach((preview) => renderPreview(preview, msg));
, which has an implicit return value that we shouldn't convey, so this is actually the best option IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Essentially, the following:
data.msg.previews.forEach((preview) => renderPreview(preview, msg));
translates to:
data.msg.previews.forEach((preview) => {
return renderPreview(preview, msg);
});
and not:
data.msg.previews.forEach((preview) => {
renderPreview(preview, msg);
});
That extra return
is unwanted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That extra return is unwanted.
Yeah, but it doesn't cause any problems. I wouldn't have an issue with this, tbh.
if (bottom) { | ||
container.scrollBottom(); | ||
} | ||
data.link = Handlebars.Utils.escapeExpression(data.link); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this isn't going to have side effects like double escapes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my understanding, it's coming back unescaped from the preview
socket events. That's why we also escape when getting the msg
event.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then the msg_preview
template is going to escape it again, is it not? Try testing it with &
in url or something.
EDIT: I just tried it, works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tried with the msdn URL given in test for parse
, and yeah it does work fine :)
client/views/msg_preview_toggle.tpl
Outdated
@@ -0,0 +1 @@ | |||
<button class="toggle-button opened" data-url={{url}} aria-label="Toggle prefetched media"></button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was no quotes intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
4ae1629
to
4f8114b
Compare
4f8114b
to
432eb62
Compare
client/css/style.css
Outdated
@@ -293,6 +294,16 @@ kbd { | |||
content: "\f005"; /* http://fontawesome.io/icon/star/ */ | |||
} | |||
|
|||
#chat .toggle-button { | |||
/* These 2 directives are directly from .fa-fw */ | |||
width: 1.28571429em; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need such a precise width, the button can be wider to hit it easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I blindly copied it from font awesome for consistency sake, and then extra width for easy hit is at button
level and not i
level.
I'll round it up, double check if width is enough, and make sure height is as big as it can get.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Button is now a bit bigger, and roughly square (takes all height).
client/views/msg_preview_toggle.tpl
Outdated
@@ -0,0 +1 @@ | |||
<button class="toggle-button opened" data-url="{{url}}" aria-label="Toggle prefetched media"></button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure we are able to change text depending on type, so Toggle image
and Toggle link preview
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do that most likely yeah.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
aa5b6a4
to
6810a42
Compare
- Load up to 5 previews per message (to avoid abuse) - Do not load multiple times the same URL - Prepare preview containers per message instead of appending (to maintain correct order) - Store an array of previews instead of a single preview in `Msg` objects - Consolidate preview rendering for new messages and upon refresh/load history (when rendering entire channels) - Update `parse` tests to reflect previous point - Add test for multiple URLs - Switch preview tests from `assert` API to `expect` API
…from ellipsis to caret
Re: concern of being blocking, I did some tests on the server, using: ───────────────────────────────────
modified: src/plugins/irc-events/link.js
───────────────────────────────────
@ link.js:31 @ module.exports = function(client, chan, msg) {
))
.slice(0, 5) // Only preview the first 5 URLs in message to avoid abuse
.forEach((link) => {
+ console.log(`Starting ${link}`);
fetch(link, function(res) {
if (res === null) {
return;
}
+ console.log(`Finished ${link}`);
+
parse(msg, link, res, client);
});
}); Posting this message (
So unless I'm missing something, it looks fine. |
6810a42
to
2820083
Compare
@xPaw, I believe I have addressed all your comments, including splitting the rendering code into its own module :)) |
@@ -293,6 +294,16 @@ kbd { | |||
content: "\f005"; /* http://fontawesome.io/icon/star/ */ | |||
} | |||
|
|||
#chat .toggle-button { | |||
/* These 2 directives are loosely taken from .fa-fw */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer valid comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah it is, I updated it from directly from
to loosely taken from
:D Just a reminder to keep an eye on .fw-fa
in the future.
<button class="toggle-button {{#if shown}} opened{{/if}}" | ||
data-url="{{link}}" | ||
{{#equal type "image"}} | ||
aria-label="Toggle image preview" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this not going to show up on hover? I don't see tooltipped classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it was always aria-label
, not tooltips 😅
I'm up for making these tooltips, but mind doing that later after merging? I'm going to be fairly busy this weekend, hence me addressing comments at 5am :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See https://github.com/thelounge/lounge/pull/1303/files#diff-22e42391cd970f0b0b44672abb685a0aL3, and no tooltips in UI right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, really nice change.
@@ -72,6 +75,12 @@ function buildChatMessage(data) { | |||
const msg = $(templates[template](data.msg)); | |||
const text = msg.find(".text"); | |||
|
|||
if (data.msg.previews.length) { | |||
data.msg.previews.forEach((preview) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That extra return is unwanted.
Yeah, but it doesn't cause any problems. I wouldn't have an issue with this, tbh.
@@ -81,5 +81,8 @@ module.exports = function parse(text) { | |||
} | |||
|
|||
return fragments; | |||
}).join("") + linkParts.map((part) => { | |||
const escapedLink = Handlebars.Utils.escapeExpression(part.link); | |||
return `<div class="preview" data-url="${escapedLink}"></div>`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure about putting these right next to the link, but I can't think of a better option. It's probably the best we can do when there are multiple previews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, alternatively it was messing with jQuery to insert at the right position depending on the order, etc. so this feels more "natural" but I completely feel you. Nothing we can't update if a better solution presents itself anyway
…eviews Support multiple previews per message
For history: This PR broke storage de-referencing because it didn't update the relevant code. Fixed in #2824 |
Msg
objectsparse
tests to reflect previous pointassert
API toexpect
APIIt's probably not the nicest code, but it works pretty nicely, and I'm sure reviews will highlight the required improvements.
Note that this does not improve the UI of displaying toggles/previews, this will be better left off to another PR and/or #1301.
Example of multiple URLs (screenshots show an outdated toggle button)
UI of toggle button