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

feat(acms): Adds RECAP links and the RECAP button #374

Merged
merged 5 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Features:
- Introduces a new upload type for Dockets from ACMS and enhances code maintainability by refactoring common upload logic into reusable functions.([#368](https://github.com/freelawproject/recap-chrome/pull/371))
- Docket reports from ACMS are now uploaded to CourtListener.([#372](https://github.com/freelawproject/recap-chrome/pull/372))
- Adds logic to upload ACMS PDF documents to CourtListener. ([#373](https://github.com/freelawproject/recap-chrome/pull/373))
- Inserts the RECAP button and [R] icons to the ACMS Docket report. ([#374](https://github.com/freelawproject/recap-chrome/pull/374))

Changes:
- None yet
Expand Down
112 changes: 109 additions & 3 deletions src/appellate/appellate.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,100 @@ AppellateDelegate.prototype.handleAcmsDocket = async function () {
);
};

const insertRecapButton = () => {
// Query the first table with case data and insert the RECAP actions button
let caseInformationTable = document.querySelector('table.case-information');
// Get a reference to the parent node
const parentDiv = caseInformationTable.parentNode;
const existingActionButton = document.getElementById('recap-action-button');
if (!existingActionButton) {
let button = recapActionsButton(this.court, this.pacer_case_id, false);
parentDiv.insertBefore(button, caseInformationTable);
}

this.recap.getAvailabilityForDocket(
this.court,
this.pacer_case_id,
null,
(result) => {
if (result.count === 0) {
console.warn('RECAP: Zero results found for docket lookup.');
} else if (result.count > 1) {
console.error(
'RECAP: More than one result found for docket lookup. Found' +
`${result.count}`
);
} else {
addAlertButtonInRecapAction(this.court, this.pacer_case_id);
let cl_id = getClIdFromAbsoluteURL(result.results[0].absolute_url);
addSearchDocketInRecapAction(cl_id);
}
}
);
};

const attachLinkToDocs = async () => {
Copy link
Member

Choose a reason for hiding this comment

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

A better docstring would be helpful here to future readers.

// Get the docket info from the sessionStorage obj
const caseSummary = JSON.parse(sessionStorage.caseSummary);
const docketEntries = caseSummary.docketInfo.docketEntries;

// Get all the entry links on the page. We use the "entry-link"
// class as a selector because we observed that all non-restricted
// entries consistently use this class.
this.links = document.body.querySelectorAll('.entry-link');
if (!links.length) {
return;
}

// Go through the array of links and collect the doc IDs of
// the entries that are not restricted.
let docIds = [];
for (link of this.links) {
const docketEntryText = link.innerHTML.trim();
const docketEntryData = docketEntries.find(
(entry) => entry.entryNumber == parseInt(docketEntryText)
);

// Embed the pacer_doc_id as a data attribute within the anchor tag
// to facilitate subsequent retrieval based on this identifier.
link.dataset.pacerDocId = docketEntryData.docketEntryId;

// add the id to the array of doc ids
docIds.push(docketEntryData.docketEntryId);
}

// Ask the server whether any of these documents are available from RECAP.
this.recap.getAvailabilityForDocuments(docIds, this.court, (response) => {
for (result of response.results) {
let doc_id = result.pacer_doc_id;
// Query the docket entry link using the data attribute
// attached previously
let anchor = document.querySelector(`[data-pacer-doc-id="${doc_id}"]`);
// Create the RECAP icon
let href = `https://storage.courtlistener.com/${result.filepath_local}`;
let recap_link = $('<a/>', {
title: 'Available for free from the RECAP Archive.',
href: href,
});
recap_link.append(
$('<img/>').attr({
src: chrome.extension.getURL('assets/images/icon-16.png'),
})
);
let recap_div = $('<div>', {
class: 'recap-inline-appellate',
});
recap_div.append(recap_link);
// Insert the RECAP icon next to the docket entry link
recap_div.insertAfter(anchor);
}
let spinner = document.getElementById('recap-button-spinner');
if (spinner) {
spinner.classList.add('recap-btn-spinner-hidden');
}
});
};

// Since this page uses Vue.js for dynamic data rendering and shows a loader
// during API requests, an observer is necessary to monitor DOM changes and
// update the component accordingly.
Expand All @@ -114,6 +208,8 @@ AppellateDelegate.prototype.handleAcmsDocket = async function () {
if (a.localName === 'footer') {
if ('caseSummary' in sessionStorage) {
processDocket();
attachLinkToDocs();
insertRecapButton();
observer.disconnect();
} else {
console.log(
Expand All @@ -126,9 +222,19 @@ AppellateDelegate.prototype.handleAcmsDocket = async function () {
}
};

const body = document.querySelector('body');
const observer = new MutationObserver(footerObserver);
observer.observe(body, { subtree: true, childList: true });
const footer = document.querySelector('footer');
// Checks whether the footer is rendered or not, indicating that the page
// has fully loaded. Once confirmed, proceed with reloading the RECAP icons.
// This check is particularly useful when users click the 'Refresh RECAP
// links' option in the RECAP button, because the page is not reloaded and
// there are no changes being made to the DOM.
if (footer){
attachLinkToDocs();
} else {
const body = document.querySelector('body');
const observer = new MutationObserver(footerObserver);
observer.observe(body, { subtree: true, childList: true });
}
};

AppellateDelegate.prototype.handleAcmsDownloadPage = async function () {
Expand Down
Loading