From 345082fce45528a3a13a14cd95c824d5706f6070 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 04:58:26 -0300 Subject: [PATCH 01/17] Add highest-rated-comment feature --- source/content.ts | 1 + source/features/highest-rated-comment.css | 26 +++++ source/features/highest-rated-comment.tsx | 125 ++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 source/features/highest-rated-comment.css create mode 100644 source/features/highest-rated-comment.tsx diff --git a/source/content.ts b/source/content.ts index 64e83f26d8d..421afdec2fb 100644 --- a/source/content.ts +++ b/source/content.ts @@ -102,6 +102,7 @@ import './features/tag-changelog-link'; import './features/link-to-file-in-file-history'; import './features/clean-sidebar'; import './features/open-issue-to-latest-comment'; +import './features/highest-rated-comment'; import './features/scrollable-code-and-blockquote.css'; import './features/center-reactions-popup.css'; diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css new file mode 100644 index 00000000000..c4e38b446aa --- /dev/null +++ b/source/features/highest-rated-comment.css @@ -0,0 +1,26 @@ +.rgh-highest-rated-comment.comment { + border-color: orange; + border-width: 2px; +} + +.rgh-highest-rated-comment .rgh-highest-rated-comment-summary { + border: solid 1px orange; + border-bottom: solid 1px orange !important; +} + +.rgh-highest-rated-comment .rgh-highest-rated-comment-btn { + margin-right: 10px; + padding: 3px 3px; +} + +.rgh-highest-rated-comment .rgh-highest-rated-comment-text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + flex-grow: 1; +} + +.rgh-highest-rated-comment a { + /* reset default styles */ + text-decoration: none!important; +} diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx new file mode 100644 index 00000000000..bd3010bbe19 --- /dev/null +++ b/source/features/highest-rated-comment.tsx @@ -0,0 +1,125 @@ +import React from 'dom-chef'; +import select from 'select-dom'; +import features from '../libs/features'; +import './highest-rated-comment.css'; + +type Option = { + $el: Element, + likes: number, + unlikes: number, + index: number +} +type Props = { + id: string, + username: string, + text: string, + avatar: string +} + +const like = '👍' +const unlike = '👎' + +const element = ({ id, username, text, avatar }: Props) => ( +
+
+ + {username} + +
+ + +
+
+
+ + + + +
+ Highest-Rated Comment: {text} +
+
+
+
+ +
+) + +function init(): void { + const $comments: Element[] = select.all('.comment') + + const options: Option[] = select.all('.comment-reactions-options') // search for those with reactions + .map($reactions => { + const $el = $reactions.closest('.comment')! + const $buttons = select.all('button', $reactions) + const reactions: {[key: string]: number} = $buttons.reduce((acc: {[s: string]: number}, $button) => { + try { + const [emoji, countStr] = $button.innerText + .split(' ') + .map(x => x.trim()) + const count = parseInt(countStr, 10) + acc[emoji] = count + return acc + } catch (error) { + return acc + } + }, {}) + + const likes = reactions[like] || 0 + const unlikes = reactions[unlike] || 0 + const index = $comments.indexOf($el) + + return { + $el, + likes, + unlikes, + index + } + }) + .sort((a, b) => b.likes - a.likes) + + const highestNumber = Math.max(...options.map(option => option.likes)) + + function candidate (option: Option) { + // is the 5th or later comment (it doesn't make sense to highlight a comment that is right under the opening issue already) + const notClose = option.index >= 4 + // has the most 👍 reactions + const mostLikes = option.likes >= highestNumber + // has at least 10 👍 reactions (or 👍.count > comments.length * 0.8) + const minimum = option.likes >= 10 || option.likes > $comments.length * 0.8 + // controversial: 👎.count >= 👍.count / 2 + const controversial = option.unlikes >= (option.likes / 2) + + return notClose && mostLikes && minimum && !controversial + } + + const comment = options.find(candidate) + + if (comment && comment.$el) { + const $parent = comment.$el.closest('.timeline-comment-group')! + const id = $parent.id + const username = select('.author', comment.$el)!.innerText + const text = select('.comment-body', comment.$el)!.innerText.substring(0, 100) + const $avatar = select('img.avatar', $parent)! as HTMLImageElement + const avatar = $avatar.src + const props: Props = { id, username, text, avatar } + + comment.$el.classList.add('rgh-highest-rated-comment') + select('.js-discussion')!.prepend(element(props)) + } +} + +features.add({ + id: 'highest-rated-comment', + description: 'Highlight and make a shortcut to most useful comments in issues.', + screenshot: 'https://i.imgur.com/vXmv0R6.png', + include: [ + features.isIssue + ], + exclude: [ + ], + load: features.onDomReady, // Wait for dom-ready + init +}); From 08f0fa901f8f6f323f2ae9f7f2f5913711653951 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 05:35:15 -0300 Subject: [PATCH 02/17] Fix lint --- source/features/highest-rated-comment.css | 12 +-- source/features/highest-rated-comment.tsx | 98 +++++++++++------------ 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index c4e38b446aa..cfa40f2d932 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -1,16 +1,16 @@ .rgh-highest-rated-comment.comment { - border-color: orange; - border-width: 2px; + border-color: #ffa500; + border-width: 2px; } .rgh-highest-rated-comment .rgh-highest-rated-comment-summary { - border: solid 1px orange; - border-bottom: solid 1px orange !important; + border: solid 1px #ffa500; + border-bottom: solid 1px #ffa500 !important; } .rgh-highest-rated-comment .rgh-highest-rated-comment-btn { margin-right: 10px; - padding: 3px 3px; + padding: 3px; } .rgh-highest-rated-comment .rgh-highest-rated-comment-text { @@ -22,5 +22,5 @@ .rgh-highest-rated-comment a { /* reset default styles */ - text-decoration: none!important; + text-decoration: none !important; } diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index bd3010bbe19..154949ee49a 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -4,22 +4,22 @@ import features from '../libs/features'; import './highest-rated-comment.css'; type Option = { - $el: Element, - likes: number, - unlikes: number, - index: number + $el: Element; + likes: number; + unlikes: number; + index: number; } type Props = { - id: string, - username: string, - text: string, - avatar: string + id: string; + username: string; + text: string; + avatar: string; } -const like = '👍' -const unlike = '👎' +const like = '👍'; +const unlike = '👎'; -const element = ({ id, username, text, avatar }: Props) => ( +const element = ({id, username, text, avatar}: Props): Node => (
@@ -45,69 +45,69 @@ const element = ({ id, username, text, avatar }: Props) => (
-) +); function init(): void { - const $comments: Element[] = select.all('.comment') + const $comments: Element[] = select.all('.comment'); - const options: Option[] = select.all('.comment-reactions-options') // search for those with reactions + const options: Option[] = select.all('.comment-reactions-options') // Search for those with reactions .map($reactions => { - const $el = $reactions.closest('.comment')! - const $buttons = select.all('button', $reactions) + const $el = $reactions.closest('.comment')!; + const $buttons = select.all('button', $reactions); const reactions: {[key: string]: number} = $buttons.reduce((acc: {[s: string]: number}, $button) => { try { const [emoji, countStr] = $button.innerText .split(' ') - .map(x => x.trim()) - const count = parseInt(countStr, 10) - acc[emoji] = count - return acc + .map(x => x.trim()); + const count = parseInt(countStr, 10); + acc[emoji] = count; + return acc; } catch (error) { - return acc + return acc; } - }, {}) + }, {}); - const likes = reactions[like] || 0 - const unlikes = reactions[unlike] || 0 - const index = $comments.indexOf($el) + const likes = reactions[like] || 0; + const unlikes = reactions[unlike] || 0; + const index = $comments.indexOf($el); return { $el, likes, unlikes, index - } + }; }) - .sort((a, b) => b.likes - a.likes) + .sort((a, b) => b.likes - a.likes); - const highestNumber = Math.max(...options.map(option => option.likes)) + const highestNumber = Math.max(...options.map(option => option.likes)); - function candidate (option: Option) { - // is the 5th or later comment (it doesn't make sense to highlight a comment that is right under the opening issue already) - const notClose = option.index >= 4 - // has the most 👍 reactions - const mostLikes = option.likes >= highestNumber - // has at least 10 👍 reactions (or 👍.count > comments.length * 0.8) - const minimum = option.likes >= 10 || option.likes > $comments.length * 0.8 - // controversial: 👎.count >= 👍.count / 2 - const controversial = option.unlikes >= (option.likes / 2) + function candidate(option: Option): boolean { + // Is the 5th or later comment (it doesn't make sense to highlight a comment that is right under the opening issue already) + const notClose = option.index >= 4; + // Has the most 👍 reactions + const mostLikes = option.likes >= highestNumber; + // Has at least 10 👍 reactions (or 👍.count > comments.length * 0.8) + const minimum = option.likes >= 10 || option.likes > $comments.length * 0.8; + // Controversial: 👎.count >= 👍.count / 2 + const controversial = option.unlikes >= (option.likes / 2); - return notClose && mostLikes && minimum && !controversial + return notClose && mostLikes && minimum && !controversial; } - const comment = options.find(candidate) + const comment = options.find(candidate); if (comment && comment.$el) { - const $parent = comment.$el.closest('.timeline-comment-group')! - const id = $parent.id - const username = select('.author', comment.$el)!.innerText - const text = select('.comment-body', comment.$el)!.innerText.substring(0, 100) - const $avatar = select('img.avatar', $parent)! as HTMLImageElement - const avatar = $avatar.src - const props: Props = { id, username, text, avatar } - - comment.$el.classList.add('rgh-highest-rated-comment') - select('.js-discussion')!.prepend(element(props)) + const $parent = comment.$el.closest('.timeline-comment-group')!; + const {id} = $parent; + const username = select('.author', comment.$el)!.innerText; + const text = select('.comment-body', comment.$el)!.innerText.substring(0, 100); + const $avatar = select('img.avatar', $parent)! as HTMLImageElement; + const avatar = $avatar.src; + const props: Props = {id, username, text, avatar}; + + comment.$el.classList.add('rgh-highest-rated-comment'); + select('.js-discussion')!.prepend(element(props)); } } From 5fc2860ebcd1168c28764f0c99156913212b0220 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 09:25:23 -0300 Subject: [PATCH 03/17] Implement requested changes --- source/features/highest-rated-comment.css | 20 +--- source/features/highest-rated-comment.tsx | 133 ++++++++-------------- source/libs/icons.tsx | 2 + 3 files changed, 49 insertions(+), 106 deletions(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index cfa40f2d932..316dbe30d2d 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -1,16 +1,5 @@ -.rgh-highest-rated-comment.comment { - border-color: #ffa500; - border-width: 2px; -} - -.rgh-highest-rated-comment .rgh-highest-rated-comment-summary { - border: solid 1px #ffa500; - border-bottom: solid 1px #ffa500 !important; -} - -.rgh-highest-rated-comment .rgh-highest-rated-comment-btn { - margin-right: 10px; - padding: 3px; +.rgh-highest-rated-comment { + border: solid 1px #ffa500 !important; } .rgh-highest-rated-comment .rgh-highest-rated-comment-text { @@ -19,8 +8,3 @@ overflow: hidden; flex-grow: 1; } - -.rgh-highest-rated-comment a { - /* reset default styles */ - text-decoration: none !important; -} diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 154949ee49a..43a2188e252 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -1,44 +1,29 @@ +import './highest-rated-comment.css'; import React from 'dom-chef'; import select from 'select-dom'; import features from '../libs/features'; -import './highest-rated-comment.css'; +import * as icons from '../libs/icons'; -type Option = { - $el: Element; - likes: number; - unlikes: number; - index: number; -} type Props = { id: string; - username: string; text: string; - avatar: string; + avatar: HTMLImageElement; } -const like = '👍'; -const unlike = '👎'; - -const element = ({id, username, text, avatar}: Props): Node => ( -
-
- - {username} - -
+const element = ({id, text, avatar}: Props): Node => ( +
+ {avatar} - -
-
+ +
+
- - - + + {icons.arrowDown()} +
- Highest-Rated Comment: {text} + Highest-rated comment: {text}
@@ -47,68 +32,42 @@ const element = ({id, username, text, avatar}: Props): Node => (
); -function init(): void { - const $comments: Element[] = select.all('.comment'); - - const options: Option[] = select.all('.comment-reactions-options') // Search for those with reactions - .map($reactions => { - const $el = $reactions.closest('.comment')!; - const $buttons = select.all('button', $reactions); - const reactions: {[key: string]: number} = $buttons.reduce((acc: {[s: string]: number}, $button) => { - try { - const [emoji, countStr] = $button.innerText - .split(' ') - .map(x => x.trim()); - const count = parseInt(countStr, 10); - acc[emoji] = count; - return acc; - } catch (error) { - return acc; - } - }, {}); - - const likes = reactions[like] || 0; - const unlikes = reactions[unlike] || 0; - const index = $comments.indexOf($el); - - return { - $el, - likes, - unlikes, - index - }; - }) - .sort((a, b) => b.likes - a.likes); - - const highestNumber = Math.max(...options.map(option => option.likes)); - - function candidate(option: Option): boolean { - // Is the 5th or later comment (it doesn't make sense to highlight a comment that is right under the opening issue already) - const notClose = option.index >= 4; - // Has the most 👍 reactions - const mostLikes = option.likes >= highestNumber; - // Has at least 10 👍 reactions (or 👍.count > comments.length * 0.8) - const minimum = option.likes >= 10 || option.likes > $comments.length * 0.8; - // Controversial: 👎.count >= 👍.count / 2 - const controversial = option.unlikes >= (option.likes / 2); +function getCount(reaction: HTMLElement): number { + return Number(reaction.textContent!.match(/\d+/)![0]); +} - return notClose && mostLikes && minimum && !controversial; +function init(): void { + let highest; + const $likes = select.all('.js-discussion .js-timeline-item:nth-child(n+6) [aria-label*="reacted with thumbs up"]'); + for (const $like of $likes) { + const count = getCount($like); + const $dislike = select('[aria-label*="reacted with thumbs down"]', $like.parentElement!); + + if ($dislike && getCount($dislike) >= count / 2) { + continue; // Controversial comment + } + + if (!highest) { + highest = {$like, count}; + } else if (count > highest.count) { + highest = {$like, count}; + } } - const comment = options.find(candidate); + if (!highest || highest.count < 10) { + return; + } - if (comment && comment.$el) { - const $parent = comment.$el.closest('.timeline-comment-group')!; - const {id} = $parent; - const username = select('.author', comment.$el)!.innerText; - const text = select('.comment-body', comment.$el)!.innerText.substring(0, 100); - const $avatar = select('img.avatar', $parent)! as HTMLImageElement; - const avatar = $avatar.src; - const props: Props = {id, username, text, avatar}; + const $parent = highest.$like.closest('.js-timeline-item')!; + const $comment = select('.comment', $parent)!; + const {id} = select('.timeline-comment-group', $parent)!; + const text = select('.comment-body', $parent)!.textContent!.substring(0, 100); + const $avatar = select('.avatar-parent-child.timeline-comment-avatar', $parent)! as HTMLImageElement; + const avatar = $avatar.cloneNode(true) as HTMLImageElement; + const props: Props = {id, text, avatar}; - comment.$el.classList.add('rgh-highest-rated-comment'); - select('.js-discussion')!.prepend(element(props)); - } + select('.js-discussion')!.prepend(element(props)); + $comment.classList.add('rgh-highest-rated-comment'); } features.add({ @@ -118,8 +77,6 @@ features.add({ include: [ features.isIssue ], - exclude: [ - ], - load: features.onDomReady, // Wait for dom-ready + load: features.onAjaxedPages, init }); diff --git a/source/libs/icons.tsx b/source/libs/icons.tsx index ee681eff1fe..184d4d64cd1 100644 --- a/source/libs/icons.tsx +++ b/source/libs/icons.tsx @@ -67,3 +67,5 @@ export const privateLockFilled = (): SVGElement => ( ); + +export const arrowDown = (): SVGElement => ; From decb520b55fe001f6153d40425de51af9a5ed6a1 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 17:46:16 -0300 Subject: [PATCH 04/17] Updates on highest-rated-comments * Simplify template. * Update CSS to fit the template changes. * Update init signature. * Remove $ from DOM variables. * Minor implementation changes. * Correct arrowDown SVG template. --- source/features/highest-rated-comment.css | 2 +- source/features/highest-rated-comment.tsx | 57 ++++++++++------------- source/libs/icons.tsx | 2 +- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index 316dbe30d2d..1763e7219da 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -1,5 +1,5 @@ .rgh-highest-rated-comment { - border: solid 1px #ffa500 !important; + border: solid 1px #ffa500; } .rgh-highest-rated-comment .rgh-highest-rated-comment-text { diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 43a2188e252..69e05d876dc 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -5,27 +5,23 @@ import features from '../libs/features'; import * as icons from '../libs/icons'; type Props = { - id: string; + hash: string; text: string; avatar: HTMLImageElement; } -const element = ({id, text, avatar}: Props): Node => ( +const element = ({hash, text, avatar}: Props): Node => (
{avatar} - -
-
- @@ -36,38 +32,35 @@ function getCount(reaction: HTMLElement): number { return Number(reaction.textContent!.match(/\d+/)![0]); } -function init(): void { +function init(): false | void { let highest; - const $likes = select.all('.js-discussion .js-timeline-item:nth-child(n+6) [aria-label*="reacted with thumbs up"]'); - for (const $like of $likes) { - const count = getCount($like); - const $dislike = select('[aria-label*="reacted with thumbs down"]', $like.parentElement!); + const likes = select.all('.js-discussion .js-timeline-item:nth-child(n+6) [aria-label*="reacted with thumbs up"]'); + for (const like of likes) { + const count = getCount(like); + const dislike = select('[aria-label*="reacted with thumbs down"]', like.parentElement!); - if ($dislike && getCount($dislike) >= count / 2) { + if (dislike && getCount(dislike) >= count / 2) { continue; // Controversial comment } - if (!highest) { - highest = {$like, count}; - } else if (count > highest.count) { - highest = {$like, count}; + if (!highest || count > highest.count) { + highest = {like, count}; } } if (!highest || highest.count < 10) { - return; + return false; } - const $parent = highest.$like.closest('.js-timeline-item')!; - const $comment = select('.comment', $parent)!; - const {id} = select('.timeline-comment-group', $parent)!; - const text = select('.comment-body', $parent)!.textContent!.substring(0, 100); - const $avatar = select('.avatar-parent-child.timeline-comment-avatar', $parent)! as HTMLImageElement; - const avatar = $avatar.cloneNode(true) as HTMLImageElement; - const props: Props = {id, text, avatar}; + const parent = highest.like.closest('.js-timeline-item')!; + const {hash} = select('.timestamp', parent)!; + const text = select('.comment-body', parent)!.textContent!.substring(0, 100); + const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)! + .cloneNode(true) as HTMLImageElement; + const props: Props = {hash, text, avatar}; select('.js-discussion')!.prepend(element(props)); - $comment.classList.add('rgh-highest-rated-comment'); + select('.comment', parent)!.classList.add('rgh-highest-rated-comment'); } features.add({ diff --git a/source/libs/icons.tsx b/source/libs/icons.tsx index 184d4d64cd1..c03a676d882 100644 --- a/source/libs/icons.tsx +++ b/source/libs/icons.tsx @@ -68,4 +68,4 @@ export const privateLockFilled = (): SVGElement => ( ); -export const arrowDown = (): SVGElement => ; +export const arrowDown = (): SVGElement => ; From 18840489a866513a5d87859dbb8e836d16288d20 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 17:48:12 -0300 Subject: [PATCH 05/17] Add @lubien to CODEOWNERS Feature: highest-rated-comment.tsx --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 85c60216a39..3446b2842ce 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,3 +5,4 @@ source/features/link-to-file-in-file-history.tsx @HardikModha source/features/default-to-rich-diff.tsx @idasbiste source/features/indented-code-wrapping.tsx @notlmn source/features/open-issue-to-latest-comment.tsx @dotconnor +source/features/highest-rated-comment.tsx @lubien From 717f7e967a6fa10f0384e527cf788ef76040023f Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 17:59:53 -0300 Subject: [PATCH 06/17] Update rgh-highest-rated-comment CSS We need to force just the color since the original comment original CSS takes precedence unless we do this. --- source/features/highest-rated-comment.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index 1763e7219da..b247cf05fc0 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -1,5 +1,7 @@ .rgh-highest-rated-comment { - border: solid 1px #ffa500; + border-width: 1px; + border-style: solid; + border-color: #ffa500 !important; } .rgh-highest-rated-comment .rgh-highest-rated-comment-text { From 6c72057070949611d3a0e64595170d9f00472279 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 19:20:26 -0300 Subject: [PATCH 07/17] Simplify rgh-highest-rated-comment * Oneliner CSS border. * JSX render function directly in init. * Remove Prop type. * Simplify JSX. --- source/features/highest-rated-comment.css | 5 +-- source/features/highest-rated-comment.tsx | 43 +++++++++-------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index b247cf05fc0..1a88c84af96 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -1,12 +1,9 @@ .rgh-highest-rated-comment { - border-width: 1px; - border-style: solid; - border-color: #ffa500 !important; + border: 1px solid #ffa500 !important; } .rgh-highest-rated-comment .rgh-highest-rated-comment-text { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - flex-grow: 1; } diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 69e05d876dc..ad9a93ef011 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -4,30 +4,6 @@ import select from 'select-dom'; import features from '../libs/features'; import * as icons from '../libs/icons'; -type Props = { - hash: string; - text: string; - avatar: HTMLImageElement; -} - -const element = ({hash, text, avatar}: Props): Node => ( - -); - function getCount(reaction: HTMLElement): number { return Number(reaction.textContent!.match(/\d+/)![0]); } @@ -56,10 +32,23 @@ function init(): false | void { const {hash} = select('.timestamp', parent)!; const text = select('.comment-body', parent)!.textContent!.substring(0, 100); const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)! - .cloneNode(true) as HTMLImageElement; - const props: Props = {hash, text, avatar}; + .cloneNode(true); + + select('.js-discussion')!.prepend(( + + )); select('.comment', parent)!.classList.add('rgh-highest-rated-comment'); } From 3997338bae8578d17614ca423f7b00841303c602 Mon Sep 17 00:00:00 2001 From: lubien Date: Sat, 1 Jun 2019 19:46:14 -0300 Subject: [PATCH 08/17] Get avatar in one line --- source/features/highest-rated-comment.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index ad9a93ef011..91d33793eed 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -31,8 +31,7 @@ function init(): false | void { const parent = highest.like.closest('.js-timeline-item')!; const {hash} = select('.timestamp', parent)!; const text = select('.comment-body', parent)!.textContent!.substring(0, 100); - const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)! - .cloneNode(true); + const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)!.cloneNode(true); select('.js-discussion')!.prepend((
From d119bb4cb35d8471a3697c001c3babf4c99696a9 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 13:57:50 +0800 Subject: [PATCH 09/17] Drop extra class --- source/features/highest-rated-comment.css | 2 +- source/features/highest-rated-comment.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index 1a88c84af96..6bbf883b9b6 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -2,7 +2,7 @@ border: 1px solid #ffa500 !important; } -.rgh-highest-rated-comment .rgh-highest-rated-comment-text { +a.rgh-highest-rated-comment .timeline-comment-header-text { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 91d33793eed..a77193b46c4 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -42,7 +42,7 @@ function init(): false | void { {icons.arrowDown()} - + Highest-rated comment: {text} From ac16d82d1f08f0f29a2228974dcfe0ce93b4ba6b Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 13:58:51 +0800 Subject: [PATCH 10/17] Fix position (it should be after the first one) --- source/features/highest-rated-comment.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index a77193b46c4..5d9f90760d3 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -33,7 +33,7 @@ function init(): false | void { const text = select('.comment-body', parent)!.textContent!.substring(0, 100); const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)!.cloneNode(true); - select('.js-discussion')!.prepend(( + parent.parentElement!.firstElementChild!.after((
{avatar} From 8b71d4501e0747975ae777a12c0d5999b4a0e045 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 13:59:05 +0800 Subject: [PATCH 11/17] Correctly highlight the comment --- source/features/highest-rated-comment.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 5d9f90760d3..4d78b96e934 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -33,6 +33,7 @@ function init(): false | void { const text = select('.comment-body', parent)!.textContent!.substring(0, 100); const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)!.cloneNode(true); + select('.unminimized-comment', parent)!.classList.add('rgh-highest-rated-comment'); parent.parentElement!.firstElementChild!.after((
{avatar} @@ -48,7 +49,6 @@ function init(): false | void {
)); - select('.comment', parent)!.classList.add('rgh-highest-rated-comment'); } features.add({ From b64d3459279408bcc6e9dbb6e917457e42b585eb Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 14:14:15 +0800 Subject: [PATCH 12/17] Use 2px border and unset max-width --- source/features/highest-rated-comment.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index 6bbf883b9b6..1b055b5e7b1 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -1,9 +1,10 @@ .rgh-highest-rated-comment { - border: 1px solid #ffa500 !important; + border: 2px solid #ffa500 !important; } a.rgh-highest-rated-comment .timeline-comment-header-text { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; + max-width: auto; } From 84659d1035ee760534f3ee48f26dd67d74b5e33d Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 14:14:54 +0800 Subject: [PATCH 13/17] Change var name and order for readability --- source/features/highest-rated-comment.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 4d78b96e934..fc00bf24a71 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -28,13 +28,13 @@ function init(): false | void { return false; } - const parent = highest.like.closest('.js-timeline-item')!; - const {hash} = select('.timestamp', parent)!; - const text = select('.comment-body', parent)!.textContent!.substring(0, 100); - const avatar = select('.avatar-parent-child.timeline-comment-avatar', parent)!.cloneNode(true); + const event = highest.like.closest('.js-timeline-item')!; + const text = select('.comment-body', event)!.textContent!.substring(0, 100); + const avatar = select('.timeline-comment-avatar', event)!.cloneNode(true); + const {hash} = select('.timestamp', event)!; - select('.unminimized-comment', parent)!.classList.add('rgh-highest-rated-comment'); - parent.parentElement!.firstElementChild!.after(( + select('.unminimized-comment', event)!.classList.add('rgh-highest-rated-comment'); + event.parentElement!.firstElementChild!.after((
{avatar} From 76ff18e6fcfbf4e7180bae1e9bebe795272b125b Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 14:17:12 +0800 Subject: [PATCH 14/17] Update description and screenshot and mention in readme --- readme.md | 1 + source/features/highest-rated-comment.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 6b6141981d9..253a917e6e3 100644 --- a/readme.md +++ b/readme.md @@ -141,6 +141,7 @@ GitHub Enterprise is also supported. More info in the options. - [Hidden comments are previewed inline.](https://user-images.githubusercontent.com/1402241/52545036-6e271700-2def-11e9-8c0c-b5e0fa6f37dd.png) - [Your issues and PRs are highlighted.](https://user-images.githubusercontent.com/1402241/53065281-01560000-3506-11e9-9a51-0bdf69e20b4a.png) - [SVG files in a PR default to rich-diff view.](https://user-images.githubusercontent.com/5243867/57125552-c08a2b00-6d81-11e9-9b84-cdb535baa98e.png) +- [The most useful comment in issues is highlighted.](https://user-images.githubusercontent.com/1402241/58757449-5b238880-853f-11e9-9526-e86c41a32f00.png) ### Declutter diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index fc00bf24a71..662453d1cf1 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -53,8 +53,8 @@ function init(): false | void { features.add({ id: 'highest-rated-comment', - description: 'Highlight and make a shortcut to most useful comments in issues.', - screenshot: 'https://i.imgur.com/vXmv0R6.png', + description: 'The most useful comment in issues is highlighted.', + screenshot: 'https://user-images.githubusercontent.com/1402241/58757449-5b238880-853f-11e9-9526-e86c41a32f00.png', include: [ features.isIssue ], From 24e1a335ba5745096ecb3cb70671550f8be2b4cb Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Sun, 2 Jun 2019 16:01:28 +0800 Subject: [PATCH 15/17] Use 2px border and unset max-width /2 --- source/features/highest-rated-comment.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/features/highest-rated-comment.css b/source/features/highest-rated-comment.css index 1b055b5e7b1..5c26f3e4012 100644 --- a/source/features/highest-rated-comment.css +++ b/source/features/highest-rated-comment.css @@ -6,5 +6,5 @@ a.rgh-highest-rated-comment .timeline-comment-header-text { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - max-width: auto; + max-width: none; } From f94a89d8409501abfbfd6b0be0d8aec0c562199e Mon Sep 17 00:00:00 2001 From: lubien Date: Mon, 3 Jun 2019 05:25:42 -0300 Subject: [PATCH 16/17] Don't show highest-rated-comment for closest ones --- source/features/highest-rated-comment.tsx | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 662453d1cf1..6c96fea9f00 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -34,21 +34,25 @@ function init(): false | void { const {hash} = select('.timestamp', event)!; select('.unminimized-comment', event)!.classList.add('rgh-highest-rated-comment'); - event.parentElement!.firstElementChild!.after(( - - )); + + const position = select.all('.js-comment').indexOf(highest.like.closest('.js-comment') as HTMLElement); + if (position >= 4) { + event.parentElement!.firstElementChild!.after(( + + )); + } } features.add({ From 10b4f50628e858a1aa6914542354ed03f99f9a0f Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 5 Jun 2019 04:11:01 +0800 Subject: [PATCH 17/17] Drop comment counter in selector --- source/features/highest-rated-comment.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 6c96fea9f00..7ac4471afec 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -10,8 +10,7 @@ function getCount(reaction: HTMLElement): number { function init(): false | void { let highest; - const likes = select.all('.js-discussion .js-timeline-item:nth-child(n+6) [aria-label*="reacted with thumbs up"]'); - for (const like of likes) { + for (const like of select.all('[aria-label*="reacted with thumbs up"]')) { const count = getCount(like); const dislike = select('[aria-label*="reacted with thumbs down"]', like.parentElement!);