Skip to content

Conversation

jimchamp
Copy link
Collaborator

@jimchamp jimchamp commented Jul 2, 2025

Closes #10582

Makes the following changes:

  1. Decouples yearly reading goal and check-in code
  2. Creates new check-in forms from a single template
  3. Consolidates client-side check-in code in the my-books module
  4. Check-in form now displayed in a colorbox
  5. Replace check-in $.ajax calls with fetch
  6. Updates check-in unit tests

Technical

Testing

Screenshot

Stakeholders

Copy link

@accesslint accesslint bot left a comment

Choose a reason for hiding this comment

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

There are accessibility issues in these changes.

this.checkInDisplay.hide()
this.checkInPrompt.show()
})
.catch(err => {
Copy link
Collaborator Author

@jimchamp jimchamp Jul 2, 2025

Choose a reason for hiding this comment

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

Suggested change
.catch(err => {
.catch(() => {

err is unused, and this is causing our CI/CD checks to fail.

data = json.loads(web.data())
if not self.validate_data(data):
raise web.HTTPError(
"404 Bad Request", headers={"Content-Type": "application/json"}
Copy link
Collaborator Author

@jimchamp jimchamp Jul 2, 2025

Choose a reason for hiding this comment

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

Suggested change
"404 Bad Request", headers={"Content-Type": "application/json"}
"400 Bad Request", headers={"Content-Type": "application/json"}

Fix copy-paste oversight.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This might be best as a 400 Bad Request than a 404

@jimchamp jimchamp marked this pull request as ready for review July 2, 2025 16:07
@jimchamp jimchamp force-pushed the read-dates-for-legacy-browsers branch 2 times, most recently from 648fbfa to 8d74f4f Compare July 3, 2025 16:56
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should probably be renamed to yearly_reading_goals.py, to match the name of the DB API for this feature.

@cdrini cdrini added the Priority: 1 Do this week, receiving emails, time sensitive, . [managed] label Jul 3, 2025
Copy link
Collaborator

@cdrini cdrini left a comment

Choose a reason for hiding this comment

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

Woohoo great refactor! Separating out the yearly reading goal logic makes a ton of sense. And this fixes the bug about not being able to update the date! 🥳

No blockers, a few suggestions on code/naming/arch.

I tested:

  • Moving a book to "Already read" shows the prompt
  • I can click "2025" and it saves
  • I can then later edit it to add month/year
  • Today link works
  • Edition persisted when on books page
    • Existing bug: not persisted when from search page.

Merge at your discretion!

Comment on lines +70 to +71
const splitKey = this.workKey ? this.workKey.split('/') : ['']
const workOlid = splitKey[splitKey.length - 1]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const splitKey = this.workKey ? this.workKey.split('/') : ['']
const workOlid = splitKey[splitKey.length - 1]
const workOlid = this.workKey ? this.workKey.split('/')[2] : '';


const splitKey = this.workKey ? this.workKey.split('/') : ['']
const workOlid = splitKey[splitKey.length - 1]
this.checkInComponents = new CheckInComponents(document.querySelector(`#check-in-container-${workOlid}`))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note this could cause some subtle bugs if a book appears twice on a page; can we avoid having it being a global id? Something like this.dropper.querySelector('.checkin-form') ? Although it seems like this exists outside the my-books div. I think that makes the dividing line between our components a bit messy; it might make more sense to either have checkin-form be its own independent component outside the book dropper.

But those are kind of bigger changes; maybe quicker might be this.dropper.parentElement.querySelector('.last-read-date') with a comment about the assumptions.


const splitKey = this.workKey ? this.workKey.split('/') : ['']
const workOlid = splitKey[splitKey.length - 1]
this.checkInComponents = new CheckInComponents(document.querySelector(`#check-in-container-${workOlid}`))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Regardless, a bit odd this is plural, confused me for a bit whether this was initializing all the check in components on the page.

Suggested change
this.checkInComponents = new CheckInComponents(document.querySelector(`#check-in-container-${workOlid}`))
this.checkInComponent = new CheckInComponents(document.querySelector(`#check-in-container-${workOlid}`))

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A CheckInComponents object is comprised of three individual components:

  1. The check-in form, which is displayed in a modal.
  2. The check-in prompt, which appears below the dropper and prompts the patron for a last-read date.
  3. A display that shows the last check-in date, and provided an affordance to edit the date.

*
* @class
*/
export class CheckInComponents {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ditto here, the plural is a little misleading.

Suggested change
export class CheckInComponents {
export class CheckInComponent {

Comment on lines +109 to +120
let hiddenModalContentContainer = document.querySelector('#hidden-modal-content-container')
if (!hiddenModalContentContainer) {
hiddenModalContentContainer = document.createElement('div')
hiddenModalContentContainer.classList.add('hidden')
hiddenModalContentContainer.id = 'hidden-modal-content-container'
document.body.appendChild(hiddenModalContentContainer)
}

const modalContent = this.createModalContentFromTemplate()
hiddenModalContentContainer.appendChild(modalContent)

this.modalContent = hiddenModalContentContainer.querySelector(`#modal-content-${this.config.workOlid}`)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I understand correctly, this is creating one shared #hidden-modal-content-container used by all the CheckIn components, and putting inside it CheckInForms from all check-ins on the page? And then only getting things with the given work olid to apply to this.

Could we make the form generate dynamically only when needed? The current approach will create a lot of DOM, the majority of which is very unlikely to be used, which has a memory impact.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will create follow-up issue to track this.

data = json.loads(web.data())
if not self.validate_data(data):
raise web.HTTPError(
"404 Bad Request", headers={"Content-Type": "application/json"}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This might be best as a 400 Bad Request than a 404

Comment on lines +30 to +38
<span>
<label class="check-in__year-label">$_('Year:')</label>
<select class="check-in__select" name="year">
<option value="">$_('Year')</option>
<option class="hidden show-if-local-year" value="$(year + 1)">$(year + 1)</option>
$for i in range(120):
<option value="$(year - i)">$(year - i)</option>
</select>
</span>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh and another thing: we can avoid having to align these via JS by using this HTML trick. This automatically associated the label without having to specify for.

Suggested change
<span>
<label class="check-in__year-label">$_('Year:')</label>
<select class="check-in__select" name="year">
<option value="">$_('Year')</option>
<option class="hidden show-if-local-year" value="$(year + 1)">$(year + 1)</option>
$for i in range(120):
<option value="$(year - i)">$(year - i)</option>
</select>
</span>
<label>
$_('Year:')
<select class="check-in__select" name="year">
<option value="">$_('Year')</option>
<option class="hidden show-if-local-year" value="$(year + 1)">$(year + 1)</option>
$for i in range(120):
<option value="$(year - i)">$(year - i)</option>
</select>
</label>

@jimchamp jimchamp added the State: Blocked Work has stopped, waiting for something (Info, Dependent fix, etc. See comments). [managed] label Jul 15, 2025
@jimchamp
Copy link
Collaborator Author

Blocked on necessary rebase.

@cdrini cdrini assigned jimchamp and unassigned cdrini Jul 17, 2025
jimchamp added 14 commits July 28, 2025 17:07
- Handler will fail fast if called while unauthenticated
- Errors are returned as `application/json`
- Client-side check-in code has been consolidated in `ReadDateComponents.js`
- Check-in templates have been moved to `templates/mybooks/`
- Check-in form is now displayed in a `colorbox` component
- Check-in forms are now created client-side, from a template rendered in `check_in_prompt.html`
The `ajax` `error` callbacks were not being executed when responses were not `OK`.
These calls have been replaced with `fetch`.  Any errors are handled by the caller.
Adds `dialog--open` class and `aria-controls` to the
dialog opener affordances (the "Edit" and "Other" links).
These changes cause the dialog openers to be initialized
in the main `index.js` flow, in the typical manner.

`open-modal` custom events and the `openModal` method
were removed from `CheckInComponents.js, as the dialog
openers are now initialized in `index.js``.

Adds `initDialogClosers` function to `dialog.js` for
adding listeners to elements that were created
client-side.
@jimchamp jimchamp force-pushed the read-dates-for-legacy-browsers branch from 37e3a6c to dc9211e Compare July 30, 2025 15:46
@jimchamp jimchamp removed the State: Blocked Work has stopped, waiting for something (Info, Dependent fix, etc. See comments). [managed] label Jul 30, 2025
@codecov-commenter
Copy link

codecov-commenter commented Jul 30, 2025

Codecov Report

❌ Patch coverage is 20.67183% with 307 lines in your changes missing coverage. Please review.
✅ Project coverage is 17.85%. Comparing base (e027195) to head (d1ffc6a).
⚠️ Report is 700 commits behind head on master.

Files with missing lines Patch % Lines
...ry/js/my-books/MyBooksDropper/CheckInComponents.js 29.85% 174 Missing and 14 partials ⚠️
...rary/plugins/openlibrary/js/reading-goals/index.js 0.00% 80 Missing and 13 partials ⚠️
...rary/js/my-books/MyBooksDropper/ReadingLogForms.js 0.00% 7 Missing and 4 partials ⚠️
.../plugins/openlibrary/js/my-books/MyBooksDropper.js 0.00% 7 Missing and 3 partials ⚠️
openlibrary/plugins/openlibrary/js/dialog.js 0.00% 3 Missing ⚠️
openlibrary/plugins/openlibrary/js/index.js 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10974      +/-   ##
==========================================
+ Coverage   17.14%   17.85%   +0.71%     
==========================================
  Files          91       92       +1     
  Lines        4981     5096     +115     
  Branches      867      887      +20     
==========================================
+ Hits          854      910      +56     
- Misses       3588     3639      +51     
- Partials      539      547       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jimchamp jimchamp force-pushed the read-dates-for-legacy-browsers branch from eb0911d to 3e15cb3 Compare July 30, 2025 15:53
@jimchamp jimchamp merged commit baf7303 into internetarchive:master Jul 30, 2025
5 checks passed
@jimchamp jimchamp deleted the read-dates-for-legacy-browsers branch July 30, 2025 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: 1 Do this week, receiving emails, time sensitive, . [managed]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Half the lines in search results are made from repeated check-in forms

3 participants