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 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/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..5c26f3e4012 --- /dev/null +++ b/source/features/highest-rated-comment.css @@ -0,0 +1,10 @@ +.rgh-highest-rated-comment { + border: 2px solid #ffa500 !important; +} + +a.rgh-highest-rated-comment .timeline-comment-header-text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + max-width: none; +} diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx new file mode 100644 index 00000000000..7ac4471afec --- /dev/null +++ b/source/features/highest-rated-comment.tsx @@ -0,0 +1,66 @@ +import './highest-rated-comment.css'; +import React from 'dom-chef'; +import select from 'select-dom'; +import features from '../libs/features'; +import * as icons from '../libs/icons'; + +function getCount(reaction: HTMLElement): number { + return Number(reaction.textContent!.match(/\d+/)![0]); +} + +function init(): false | void { + let highest; + 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!); + + if (dislike && getCount(dislike) >= count / 2) { + continue; // Controversial comment + } + + if (!highest || count > highest.count) { + highest = {like, count}; + } + } + + if (!highest || highest.count < 10) { + return false; + } + + 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', event)!.classList.add('rgh-highest-rated-comment'); + + const position = select.all('.js-comment').indexOf(highest.like.closest('.js-comment') as HTMLElement); + if (position >= 4) { + event.parentElement!.firstElementChild!.after(( +
+ {avatar} + + + + {icons.arrowDown()} + + + + Highest-rated comment: {text} + + +
+ )); + } +} + +features.add({ + id: 'highest-rated-comment', + 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 + ], + load: features.onAjaxedPages, + init +}); diff --git a/source/libs/icons.tsx b/source/libs/icons.tsx index ee681eff1fe..c03a676d882 100644 --- a/source/libs/icons.tsx +++ b/source/libs/icons.tsx @@ -67,3 +67,5 @@ export const privateLockFilled = (): SVGElement => ( ); + +export const arrowDown = (): SVGElement => ;