-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
List Card Async Follow Functionality #11311
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
List Card Async Follow Functionality #11311
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors the async follow functionality for list cards to improve user experience by preventing page redirects and adding better internationalization support. The changes focus on making the follow/unfollow actions seamless and adding proper localization.
Key changes:
- Added internationalization support for follow/unfollow buttons with proper i18n strings
- Implemented button disabling during requests and proper error handling with toast notifications
- Refactored code organization by extracting async following logic into a separate method
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
File | Description |
---|---|
openlibrary/templates/lists/list_follow.html | Added alt attributes for accessibility and moved owner_username variable declaration |
openlibrary/plugins/openlibrary/js/book-page-lists.js | Added new initAsyncFollowing method with fetch-based follow/unfollow functionality |
openlibrary/macros/Follow.html | Added i18n data attributes to follow buttons for localization support |
openlibrary/i18n/messages.pot | Added new translatable strings for accessibility alt texts |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
|
||
if (publisherField.value === publisher) { | ||
const followButton = followForm.querySelector('button'); | ||
const i18nStrings = JSON.parse(followButton.dataset.i18n) |
Copilot
AI
Sep 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSON.parse can throw an error if the dataset contains invalid JSON. This should be wrapped in a try-catch block to handle potential parsing errors gracefully.
Copilot uses AI. Check for mistakes.
<input type="hidden" value="$custom_request_path" name="redir_url"/> | ||
$ css_treatment = 'delete' if following else 'primary' | ||
<button type="submit" class="cta-btn cta-btn--$(css_treatment)">$_("Unfollow" if following else "Follow")</button> | ||
<button type="submit" class="cta-btn cta-btn--$(css_treatment)" data-i18n="$json_encode(i18n_strings)">$_("Unfollow" if following else "Follow")</button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<button type="submit" class="cta-btn cta-btn--$(css_treatment)" data-i18n="$json_encode(i18n_strings)">$_("Unfollow" if following else "Follow")</button> | |
<button type="submit" class="cta-btn cta-btn--$(css_treatment)" data-i18n="$json_encode(i18n_strings)">$(_("Unfollow") if following else _("Follow"))</button> |
Somehow, this is creating a "UnfollowFollow" entry in messages.pot
, here.
This should probably be fixed in a separate PR. Will open an issue after this is merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've left several suggested code changes. Many of them are not showstoppers, and can likely be handled by new contributors as good first issues (I'll create them after this is merged). However, I cannot merge this until the issues with the initAsyncFollowing
function are resolved.
I'm planning on committing the code changes that fix the issues, finding a reviewer for that commit, merging this branch into main, then making some follow-up issues.
<div class="list-follow-card__bottom"> | ||
<div class="list-follow-card__user"> | ||
|
||
$ owner_username = owner.key.split('/')[-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ owner_username = owner.key.split('/')[-1] | |
$ owner_username = owner.key.split('/')[-1] |
const followForms = listSection.querySelectorAll('.follow-form'); | ||
initAsyncFollowing(followForms) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const followForms = listSection.querySelectorAll('.follow-form'); | |
initAsyncFollowing(followForms) | |
const followForms = listSection.querySelectorAll('.follow-form'); | |
initAsyncFollowing(followForms) |
This needs to be here, as we are hydrating follow buttons that were asynchronously rendered.
We should also query the page for and hydrate follow buttons in the main index.js
file. Doing so would make these buttons asynchronous throughout the site.
We'd likely also want to move the initAsyncFollowing
function to a new file (maybe follows.js
) and import it into this file.
followForms.forEach(followForm => { | ||
const followButton = followForm.querySelector('button'); | ||
followButton.disabled = true; | ||
followButtonRefs.push(followButton) | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only button that should be disabled is the one that is in the submitted form. Otherwise, the patron, if following multiple people, would have to wait until one follow request resolves before they can follow another person.
This is also a nested loop on the same collection, which should be avoided.
throw new Error('Network response was not ok'); | ||
} | ||
|
||
followForms.forEach(followForm => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another nested loop on the same collection. We need only change the button that is in the submitted form -- there's no need to update all follow buttons on the page.
}); | ||
}) | ||
.catch(() => { | ||
new PersistentToast('Failed to update followers. Please try again in a few moments.').show(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new PersistentToast('Failed to update followers. Please try again in a few moments.').show(); | |
new PersistentToast(i18nStrings.errorMsg).show(); |
Use localized strings when communicating to patrons.
Note: errorMsg
must first be added to the i18n dict in follows.html
.
I'm going to go ahead and merge this one. @cdrini notes that my commit will fail to update multiple follow buttons for a single person (e.g. two follow buttons for patron |
Closes #10945
Refactor
Technical
@ragipidavid:
The main changes that were made in this PR:
Change I did not make:
Testing
Copied from #10926:
Screenshot
https://www.loom.com/share/ecca57f343a34bd1a6185f8cd6266acb?sid=0d6b52ad-93fa-4483-bd30-58e8a1b07324
Stakeholders
@mekarpeles @jimchamp