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

quick-reactions - New feature #7247

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

brumm
Copy link

@brumm brumm commented Feb 4, 2024

Optimistically applies reactions instead of waiting for a slow network request to finish.

This should work anywhere you can react to something on GitHub. Reactions are added, updated and removed immediately. New reactions are inserted in the correct place and will not jump around when the new markup comes in.

Test URLs

Screenshot

CleanShot 2024-02-04 at 22 28 20

@fregante
Copy link
Member

fregante commented Feb 5, 2024

Love this. I'm concerned that it will break quickly and painfully, but if it's solid enough I'll just merge it anyway, because we can deal with that via hotfixes.

The concern at the moment is that Refined GitHub is seeing some serious slowdowns in issues/PRs at the moment so I might not be able to merge this or add new features for a while :(

@brumm
Copy link
Author

brumm commented Feb 5, 2024

Thanks for taking a look! Let me know if I can do anything to get this into a state you're comfortable shipping. 💪

@fregante
Copy link
Member

fregante commented Feb 8, 2024

It's not an issue with this PR, but rather with Refined GitHub itself. I can merge it once I figure out the root cause of this:

because those features also use the selector observer, so I'm afraid that that's what's causing the slow-downs.

Copy link
Member

@fregante fregante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I'm almost ready to merge this, probably after the next release.

There are a few notes about the code though.

Also, does this behave well/correctly if I double-click the reaction button? Or is that not-possible?

Comment on lines +9 to +10
function optimisticReactionFromPost(event: DelegateEvent<MouseEvent>): void {
const reaction = event.delegateTarget as HTMLButtonElement;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC you can just pass the type here, no need to assert it. Refer to its docs if it fails, also fix it elsewhere

Suggested change
function optimisticReactionFromPost(event: DelegateEvent<MouseEvent>): void {
const reaction = event.delegateTarget as HTMLButtonElement;
function optimisticReactionFromPost(event: DelegateEvent<MouseEvent, HTMLButtonElement>): void {

function optimisticReactionFromMenu(event: DelegateEvent<MouseEvent>): void {
const reactionButton = event.delegateTarget as HTMLButtonElement;
const reactionsMenu = reactionButton.closest('reactions-menu')!;
const details = reactionsMenu.querySelector('details');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replace every querySelector usage with $ from select-dom? The only exception is when the line has multiple traversing, e.g. x.closest().querySelector('a') is still more readable than $('a', x.closest())

const details = reactionsMenu.querySelector('details');
details!.open = false; // Close reactions menu immediately

const reactionsContainer = reactionsMenu.nextElementSibling!.querySelector('.js-comment-reactions-options')!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Positional traversing makes the code a little more brittle because a simple span in between will break it. If possible, select the parent element instead (e.g. menu.parentElement.querySelector) or just change what reactionsMenu to select its parent instead

details!.open = false; // Close reactions menu immediately

const reactionsContainer = reactionsMenu.nextElementSibling!.querySelector('.js-comment-reactions-options')!;
const reactionButtonValue = reactionButton.getAttribute('value')!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you looking for reactionButton.value?

const countElement = buttonElement.querySelector(
'.js-discussion-reaction-group-count',
)!;
const count = Number.parseInt(countElement.textContent, 10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably also works

Suggested change
const count = Number.parseInt(countElement.textContent, 10);
const count = Number(countElement.textContent);


const reactionsContainer = reactionsMenu.nextElementSibling!.querySelector('.js-comment-reactions-options')!;
const reactionButtonValue = reactionButton.getAttribute('value')!;
const existingReaction = reactionsContainer.querySelector<HTMLButtonElement>(`[value="${reactionButtonValue}"]`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the selector includes the tag name, you probably won't need to specify the type

Suggested change
const existingReaction = reactionsContainer.querySelector<HTMLButtonElement>(`[value="${reactionButtonValue}"]`);
const existingReaction = $(`button[value="${reactionButtonValue}"]`, reactionsContainer);

// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If:

  • GitHub still updates the UI later
  • it simplifies this piece of code

Please just append the reaction instead, it will update within a second anyway. I'd rather have a feature that lasts longer than one that looks absolutely perfect for only one month.

const emojiElement = reactionButton.querySelector('g-emoji')!.cloneNode(true);
emojiElement.className = 'social-button-emoji';

reactionsContainer.insertBefore(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});
const referenceNode = reactionsContainer.querySelectorAll('button').item(insertionIndex);
const emojiElement = reactionButton.querySelector('g-emoji')!.cloneNode(true);
emojiElement.className = 'social-button-emoji';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this class doing and why is it not there already?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

None yet

3 participants