Skip to content
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

Add highest-rated-comment feature #2108

Merged
merged 17 commits into from Jun 4, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions source/content.ts
Expand Up @@ -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';
Expand Down
26 changes: 26 additions & 0 deletions source/features/highest-rated-comment.css
@@ -0,0 +1,26 @@
.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;
fregante marked this conversation as resolved.
Show resolved Hide resolved
}

.rgh-highest-rated-comment .rgh-highest-rated-comment-btn {
margin-right: 10px;
padding: 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;
fregante marked this conversation as resolved.
Show resolved Hide resolved
}
125 changes: 125 additions & 0 deletions 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';
fregante marked this conversation as resolved.
Show resolved Hide resolved

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): Node => (
<div className="timeline-comment-wrapper rgh-highest-rated-comment">
fregante marked this conversation as resolved.
Show resolved Hide resolved
<div className="avatar-parent-child timeline-comment-avatar">
<a href={`https://github.com/${username}`} className="d-inline-block">
<img src={avatar} alt={username} className="avatar rounded-1" height="44" width="44"/>
</a>
</div>
fregante marked this conversation as resolved.
Show resolved Hide resolved

<a href={`#${id}`}>
<div className="bg-white border details-reset rounded-1">
<div className="bg-gray border-bottom-0 px-2 py-0 rgh-highest-rated-comment-summary">
fregante marked this conversation as resolved.
Show resolved Hide resolved
<div className="d-flex flex-items-center">
fregante marked this conversation as resolved.
Show resolved Hide resolved
<a href={`#${id}`} className="btn btn-sm rgh-highest-rated-comment-btn">
fregante marked this conversation as resolved.
Show resolved Hide resolved
<svg height="16" className="octicon octicon-arrow-down" viewBox="0 0 10 16" version="1.1" width="20" aria-hidden="true">
<path fill-rule="evenodd" d="M7 7V3H3v4H0l5 6 5-6H7z"></path>
</svg>
fregante marked this conversation as resolved.
Show resolved Hide resolved
</a>

<div className="text-gray timeline-comment-header-text rgh-highest-rated-comment-text">
<span>Highest-Rated Comment: {text}</span>
fregante marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
</div>
</div>
</a>
</div>
);

function init(): void {
fregante marked this conversation as resolved.
Show resolved Hide resolved
fregante marked this conversation as resolved.
Show resolved Hide resolved
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);

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;
const username = select('.author', comment.$el)!.innerText;
const text = select('.comment-body', comment.$el)!.innerText.substring(0, 100);
fregante marked this conversation as resolved.
Show resolved Hide resolved
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: [
],
fregante marked this conversation as resolved.
Show resolved Hide resolved
load: features.onDomReady, // Wait for dom-ready
fregante marked this conversation as resolved.
Show resolved Hide resolved
init
});