summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Laxman <notlmn@outlook.com> 2019-08-01 23:12:46 +0530
committerGravatar Federico Brigante <github@bfred.it> 2019-08-02 00:42:46 +0700
commitf36d4f10c725b4511e0d28d5c1fec2cf1b2a22b6 (patch)
treefdfa7f5c02cfe77f3c02bcc744b340ae3ebd28a5
parente03473b5f08333d00f052578361b78b54d3eab7c (diff)
downloadrefined-github-f36d4f10c725b4511e0d28d5c1fec2cf1b2a22b6.tar.gz
refined-github-f36d4f10c725b4511e0d28d5c1fec2cf1b2a22b6.tar.zst
refined-github-f36d4f10c725b4511e0d28d5c1fec2cf1b2a22b6.zip
Add `minimize-user-comments` feature (#2146)
Co-Authored-By: Federico Brigante <github@bfred.it>
-rw-r--r--package-lock.json5
-rw-r--r--package.json2
-rw-r--r--readme.md1
-rw-r--r--source/content.ts1
-rw-r--r--source/features/minimize-user-comments.tsx104
-rw-r--r--source/globals.d.ts1
-rw-r--r--source/libs/anchor-scroll.ts18
-rw-r--r--source/options-storage.ts2
-rw-r--r--source/options.css19
-rw-r--r--source/options.html5
-rw-r--r--source/options.tsx29
11 files changed, 167 insertions, 20 deletions
diff --git a/package-lock.json b/package-lock.json
index 8180ee5e..34dd8c34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16398,11 +16398,6 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz",
"integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw=="
- },
- "webext-detect-page": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/webext-detect-page/-/webext-detect-page-0.9.1.tgz",
- "integrity": "sha512-/QoN7CIlobiTqilq721l5cpROBJxbkwCc6UE6KeH+xLZReAgSRyM0Q9r2T0SC+wX8HoNnFKrMDNYGYsq/SgJ4w=="
}
}
},
diff --git a/package.json b/package.json
index ea29aadc..3c224e6f 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"webext-additional-permissions": "^0.1.0",
"webext-detect-page": "^1.0.2",
"webext-domain-permission-toggle": "^1.0.0-1",
- "webext-options-sync": "^0.21.1",
+ "webext-options-sync": "^0.21.2",
"webext-permissions-events-polyfill": "^1.0.1",
"webext-storage-cache": "^1.0.2",
"webextension-polyfill": "^0.4.0",
diff --git a/readme.md b/readme.md
index 185c3925..c3284543 100644
--- a/readme.md
+++ b/readme.md
@@ -179,6 +179,7 @@ Thanks for contributing! 🦋🙌
- [](# "scrollable-code-and-blockquote") [Limits the height of tall code blocks and quotes.](https://github.com/sindresorhus/refined-github/issues/1123)
- [](# "hide-comments-faster") [Simplifies the UI to hide comments.](https://user-images.githubusercontent.com/1402241/43039221-1ddc91f6-8d29-11e8-9ed4-93459191a510.gif)
- [](# "open-issue-to-latest-comment") [Links the comments icon to the latest comment.](https://user-images.githubusercontent.com/14323370/57962709-7019de00-78e8-11e9-8398-7e617ba7a96f.png)
+- [](# "minimize-user-comments") [Adds ability to minimize comments of certain users.](https://user-images.githubusercontent.com/37769974/61595681-d6d4b400-ac17-11e9-98b9-03f27b004a94.gif)
<!-- Refer to style guide above. Keep this message between sections. -->
diff --git a/source/content.ts b/source/content.ts
index 0eb26b72..ab6f4a05 100644
--- a/source/content.ts
+++ b/source/content.ts
@@ -123,6 +123,7 @@ import './features/open-issue-to-latest-comment';
import './features/limit-commit-title-length';
import './features/highest-rated-comment';
import './features/clean-issue-filters';
+import './features/minimize-user-comments';
import './features/minimize-upload-bar';
import './features/cycle-lists-with-keyboard-shortcuts';
import './features/forked-to';
diff --git a/source/features/minimize-user-comments.tsx b/source/features/minimize-user-comments.tsx
new file mode 100644
index 00000000..90c73dc9
--- /dev/null
+++ b/source/features/minimize-user-comments.tsx
@@ -0,0 +1,104 @@
+import React from 'dom-chef';
+import select from 'select-dom';
+import delegate, {DelegateEvent} from 'delegate-it';
+import features from '../libs/features';
+import {appendBefore} from '../libs/dom-utils';
+import optionsStorage from '../options-storage';
+import {getUsername} from '../libs/utils';
+import onNewComments from '../libs/on-new-comments';
+import anchorScroll from '../libs/anchor-scroll';
+
+const getLabel = (restore: boolean): string => `${restore ? 'Restore' : 'Minimize'} user’s comments`;
+
+function getUsernameFromComment(comment: Element): string {
+ return select<HTMLAnchorElement>('.author', comment)!.pathname.slice(1);
+}
+
+async function getMinimizedUsers(): Promise<string[]> {
+ return (await optionsStorage.getAll()).minimizedUsers.trim().split(/\s+/);
+}
+
+async function setMinimizedUsers(minimizedUsers: string[]): Promise<void> {
+ return optionsStorage.set({minimizedUsers: minimizedUsers.join(' ')});
+}
+
+function toggleComment(comment: HTMLElement, minimize: boolean): void {
+ if (comment.id.startsWith('issue-') || comment.id.startsWith('pullrequestreview-')) {
+ return;
+ }
+
+ select('.unminimized-comment', comment)!.classList.toggle('d-none', minimize);
+ select('.minimized-comment', comment)!.classList.toggle('d-none', !minimize);
+ select('.minimized-comment .Details-element', comment)!.toggleAttribute('open', !minimize);
+}
+
+async function onButtonClick(event: React.MouseEvent<HTMLButtonElement>): Promise<void> {
+ const comment = event.currentTarget.closest('.js-targetable-comment')!;
+ const user = getUsernameFromComment(comment);
+
+ let minimizedUsers = await getMinimizedUsers();
+ if (minimizedUsers.includes(user)) {
+ minimizedUsers = minimizedUsers.filter(mutedUser => mutedUser !== user);
+ } else {
+ minimizedUsers.push(user);
+ }
+
+ await setMinimizedUsers(minimizedUsers);
+ anchorScroll(minimizeMutedUserComments, comment);
+}
+
+async function handleMenuOpening(event: DelegateEvent): Promise<void> {
+ const dropdown = event.delegateTarget.nextElementSibling!;
+ const user = getUsernameFromComment(dropdown.closest('.js-targetable-comment')!);
+ if (user === getUsername()) {
+ return;
+ }
+
+ const minimizedUsers = await getMinimizedUsers();
+ const shouldMinimizeComment = minimizedUsers.includes(user);
+
+ const existingButton = select('.rgh-minimize-user-comments-button', dropdown);
+ if (existingButton) {
+ existingButton.textContent = getLabel(shouldMinimizeComment);
+ return;
+ }
+
+ // Add option to mute or unmute user
+ appendBefore(dropdown, 'a[data-ga-click^="Report"]',
+ <button
+ className="dropdown-item btn-link rgh-minimize-user-comments-button"
+ role="menuitem"
+ type="button"
+ onClick={onButtonClick}>
+ {getLabel(shouldMinimizeComment)}
+ </button>
+ );
+}
+
+async function minimizeMutedUserComments(): Promise<void> {
+ const minimizedUsers = await getMinimizedUsers();
+
+ for (const comment of select.all('.js-discussion .js-minimizable-comment-group')) {
+ if (minimizedUsers.includes(getUsernameFromComment(comment))) {
+ toggleComment(comment, true);
+ }
+ }
+}
+
+function init(): void {
+ minimizeMutedUserComments();
+ onNewComments(minimizeMutedUserComments);
+ delegate('.timeline-comment-action', 'click', handleMenuOpening);
+}
+
+features.add({
+ id: __featureName__,
+ description: 'Adds ability to minimize comments of certain users.',
+ screenshot: 'https://user-images.githubusercontent.com/37769974/61595681-d6d4b400-ac17-11e9-98b9-03f27b004a94.gif',
+ include: [
+ features.isIssue,
+ features.isPRConversation
+ ],
+ load: features.onAjaxedPages,
+ init
+});
diff --git a/source/globals.d.ts b/source/globals.d.ts
index a88d8dd0..5b7634a2 100644
--- a/source/globals.d.ts
+++ b/source/globals.d.ts
@@ -1,4 +1,5 @@
type AnyObject = Record<string, any>;
+type AsyncVoidFunction = () => Promise<void>;
interface FeatureInfo {
name: string;
diff --git a/source/libs/anchor-scroll.ts b/source/libs/anchor-scroll.ts
new file mode 100644
index 00000000..559864da
--- /dev/null
+++ b/source/libs/anchor-scroll.ts
@@ -0,0 +1,18 @@
+export default async function anchorScroll(
+ action: VoidFunction | AsyncVoidFunction,
+ anchor: Element = document.elementFromPoint(innerWidth / 2, innerHeight / 2)!
+): Promise<void> {
+ if (anchor) {
+ const originalPosition = anchor.getBoundingClientRect().top;
+
+ await action();
+
+ requestAnimationFrame(() => {
+ const newPositon = anchor.getBoundingClientRect().top;
+ window.scrollBy(0, newPositon - originalPosition);
+ });
+ } else {
+ // Anchor not found; proceed without anchoring
+ action();
+ }
+}
diff --git a/source/options-storage.ts b/source/options-storage.ts
index 7cab0b01..0fc0722f 100644
--- a/source/options-storage.ts
+++ b/source/options-storage.ts
@@ -4,6 +4,7 @@ export interface RGHOptions {
customCSS: string;
personalToken: string;
logging: boolean;
+ minimizedUsers: string;
[featureName: string]: string | boolean;
}
@@ -27,6 +28,7 @@ export default new OptionsSync({
customCSS: '',
personalToken: '',
logging: false,
+ minimizedUsers: '',
...featureOptions
} as RGHOptions,
migrations: [
diff --git a/source/options.css b/source/options.css
index 70b8483a..718ce0b0 100644
--- a/source/options.css
+++ b/source/options.css
@@ -1,5 +1,7 @@
html {
min-width: 550px;
+ max-width: 700px;
+ margin: auto;
overflow-x: hidden;
}
@@ -55,8 +57,9 @@ h2 ~ h2 {
margin-top: 2em;
}
-.js-features p {
+.js-features .feature {
display: flex;
+ margin-top: 1em;
}
.js-features input {
@@ -69,8 +72,11 @@ h2 ~ h2 {
text-decoration: line-through;
}
+.js-features :not(:checked) + .info .extended-options {
+ display: none;
+}
+
.js-features .description {
- font-style: 0.8em;
opacity: 0.8;
}
@@ -78,6 +84,15 @@ h2 ~ h2 {
font-size: 0.9em;
}
+.js-features .info {
+ flex-grow: 1;
+}
+
+.js-features .info p {
+ margin-top: 0.25em;
+ margin-bottom: 0.125em;
+}
+
.feature-name + a {
margin-left: 0.6em;
}
diff --git a/source/options.html b/source/options.html
index 41401590..f94c2a70 100644
--- a/source/options.html
+++ b/source/options.html
@@ -42,6 +42,11 @@
<div class="js-features"></div>
</div>
+ <div class="js-minimized-users-container extended-options">
+ <p>User list:</p>
+ <textarea name="minimizedUsers" rows="2" spellcheck="false"></textarea>
+ </div>
+
<hr>
<p>
diff --git a/source/options.tsx b/source/options.tsx
index 89ee563b..c3388dee 100644
--- a/source/options.tsx
+++ b/source/options.tsx
@@ -32,25 +32,30 @@ function buildFeatureCheckbox({name, description, screenshot, disabled}: Feature
);
return (
- <p>
+ <div className="feature">
<input type="checkbox" name={id} id={id} disabled={Boolean(disabled)} />
- <label for={id} className="info">
- <span className="feature-name">{name}</span>
- {' '}
- <a href={`https://github.com/sindresorhus/refined-github/blob/master/source/features/${name}.tsx`}>
- source
- </a>
- {screenshot ? <>, <a href={screenshot}>screenshot</a></> : ''}
- <br/>
- <span className="description">{parsedDescription}</span>
- </label>
- </p>
+ <div className="info">
+ <label for={id}>
+ <span className="feature-name">{name}</span>
+ {' '}
+ <a href={`https://github.com/sindresorhus/refined-github/blob/master/source/features/${name}.tsx`}>
+ source
+ </a>
+ {screenshot ? <>, <a href={screenshot}>screenshot</a></> : ''}
+ <br/>
+ <p className="description">{parsedDescription}</p>
+ </label>
+ </div>
+ </div>
);
}
async function init(): Promise<void> {
select('.js-features')!.append(...__featuresInfo__.map(buildFeatureCheckbox));
+ // Move minimized users input field below the respective feature checkbox
+ select('[for="feature:minimize-user-comments"]')!.after(select('.js-minimized-users-container')!);
+
await optionsStorage.syncForm('#options-form');
fitTextarea.watch('textarea');