diff --git a/build/__snapshots__/features-meta.json b/build/__snapshots__/features-meta.json index d0cc3705464..d2e2af8e243 100644 --- a/build/__snapshots__/features-meta.json +++ b/build/__snapshots__/features-meta.json @@ -669,6 +669,11 @@ "description": "Adds a link to create issues from anywhere in a repository.", "screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/1402241/274816033-820ec518-049d-4248-9f8a-27b9423e350b.png" }, + { + "id": "quick-reactions", + "description": "Shows a reaction optimistically", + "screenshot": "https://user-images.githubusercontent.com/1402241/236628453-8b646178-b838-44a3-9541-0a9b5f54a84a.png" + }, { "id": "quick-repo-deletion", "description": "Lets you delete your repos in a click, if they have no stars, issues, or PRs.", diff --git a/build/__snapshots__/imported-features.txt b/build/__snapshots__/imported-features.txt index 43952a5c7d1..c2fdd018ccf 100644 --- a/build/__snapshots__/imported-features.txt +++ b/build/__snapshots__/imported-features.txt @@ -131,6 +131,7 @@ quick-file-edit quick-label-removal quick-mention quick-new-issue +quick-reactions quick-repo-deletion quick-review quick-review-comment-deletion diff --git a/readme.md b/readme.md index 4bcdb040d5f..f1b98f3daf2 100644 --- a/readme.md +++ b/readme.md @@ -212,6 +212,7 @@ Thanks for contributing! 🦋🙌 ### Reading comments - [](# "reactions-avatars") 🔥 [Adds reaction avatars showing _who_ reacted to a comment.](https://user-images.githubusercontent.com/1402241/236628453-8b646178-b838-44a3-9541-0a9b5f54a84a.png) +- [](# "quick-reactions") [Shows a reaction optimistically](https://user-images.githubusercontent.com/1402241/236628453-8b646178-b838-44a3-9541-0a9b5f54a84a.png) - [](# "embed-gist-inline") [Embeds short gists when linked in comments on their own lines.](https://user-images.githubusercontent.com/1402241/152117903-80d784d5-4f43-4786-bc4c-d4993aec5c79.png) - [](# "comments-time-machine-links") Adds links to [browse the repository](https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252749373-9313f1d9-3d92-44a2-a1d1-2b49a29e6a5c.png) and [linked files](https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252749616-085103bf-17be-4a7d-b63c-aa5003de6dff.png) at the time of each comment. - [](# "show-names") [Adds the real name of users by their usernames.](https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252756294-94785dc2-423e-498c-939a-359a012036e0.png) diff --git a/source/features/quick-reactions.tsx b/source/features/quick-reactions.tsx new file mode 100644 index 00000000000..638088c97ff --- /dev/null +++ b/source/features/quick-reactions.tsx @@ -0,0 +1,98 @@ +import delegate, {DelegateEvent} from 'delegate-it'; +import React from 'dom-chef'; +import * as pageDetect from 'github-url-detection'; + +import features from '../feature-manager.js'; + +const emojiSortOrder = ['THUMBS_UP', 'THUMBS_DOWN', 'LAUGH', 'HOORAY', 'CONFUSED', 'HEART', 'ROCKET', 'EYES']; + +function optimisticReactionFromPost(event: DelegateEvent): void { + const reaction = event.delegateTarget as HTMLButtonElement; + updateReaction(reaction); +} + +function optimisticReactionFromMenu(event: DelegateEvent): void { + const reactionButton = event.delegateTarget as HTMLButtonElement; + const reactionsMenu = reactionButton.closest('reactions-menu')!; + const details = reactionsMenu.querySelector('details'); + details!.open = false; // Close reactions menu immediately + + const reactionsContainer = reactionsMenu.nextElementSibling!.querySelector('.js-comment-reactions-options')!; + const reactionButtonValue = reactionButton.getAttribute('value')!; + const existingReaction = reactionsContainer.querySelector(`[value="${reactionButtonValue}"]`); + + if (existingReaction) { + // The user is updating an existing reaction via the menu, just update the reaction + updateReaction(existingReaction); + } else { + // Reactions have a specific order, so we need to insert the new reaction in the correct position + const reactionIndex = emojiSortOrder.findIndex(item => reactionButtonValue.startsWith(item)); + const insertionIndex = [...reactionsContainer.querySelectorAll('button')].findIndex(button => { + const index = Number.parseInt(button.getAttribute('data-button-index-position')!, 10); + return index > reactionIndex; + }); + const referenceNode = reactionsContainer.querySelectorAll('button').item(insertionIndex); + const emojiElement = reactionButton.querySelector('g-emoji')!.cloneNode(true); + emojiElement.className = 'social-button-emoji'; + + reactionsContainer.insertBefore( + <> + + {/* helps avoid unwanted margin due to adjacent siblings of .social-reaction-summary-item */} + , + referenceNode, + ); + } +} + +function updateReaction(buttonElement: HTMLButtonElement): void { + const countElement = buttonElement.querySelector( + '.js-discussion-reaction-group-count', + )!; + const count = Number.parseInt(countElement.textContent, 10); + const isIncrease = !buttonElement.getAttribute('value')!.endsWith('unreact'); + const newCount = isIncrease ? count + 1 : count - 1; + + if (newCount === 0) { + buttonElement.setAttribute('hidden', ''); + } else if (newCount > count) { + countElement.textContent = String(newCount); + buttonElement.classList.add('user-has-reacted'); + buttonElement.classList.remove('color-fg-muted'); + } else { + countElement.textContent = String(newCount); + buttonElement.classList.add('color-fg-muted'); + } +} + +function init(signal: AbortSignal): void { + delegate('reactions-menu + form button[value$=react]', 'click', optimisticReactionFromPost, {signal}); + delegate('reactions-menu button[value$=react]', 'click', optimisticReactionFromMenu, {signal}); +} + +void features.add(import.meta.url, { + include: [ + pageDetect.hasComments, + pageDetect.isReleasesOrTags, + pageDetect.isSingleReleaseOrTag, + pageDetect.isDiscussion, + ], + init, +}); + +/* + +Test URLs: + +https://github.com/brumm/tako/issues/27#issuecomment-617314195 +https://github.com/vercel/next.js/discussions/40684#discussioncomment-3689167 + +*/ diff --git a/source/refined-github.ts b/source/refined-github.ts index f00d1d5796e..56895a519be 100644 --- a/source/refined-github.ts +++ b/source/refined-github.ts @@ -168,6 +168,7 @@ import './features/jump-to-change-requested-comment.js'; import './features/esc-to-cancel.js'; import './features/easy-toggle-files.js'; import './features/quick-repo-deletion.js'; +import './features/quick-reactions.js'; import './features/clean-repo-sidebar.js'; import './features/rgh-feature-descriptions.js'; import './features/archive-forks-link.js';