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

Show status of auto updating lists #47

Merged
merged 3 commits into from
Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions src/components/CustomListEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import TextWithEditMode from "./TextWithEditMode";
import ShareIcon from "./icons/ShareIcon";

type CustomListEditorProps = {
autoUpdateStatus?: string;
collections?: AdminCollectionData[];
entries?: CustomListEditorEntriesData;
entryPoints?: string[];
Expand All @@ -32,6 +33,7 @@ type CustomListEditorProps = {
isFetchingMoreSearchResults: boolean;
isLoaded?: boolean;
isModified?: boolean;
isSearchModified?: boolean;
isOwner?: boolean;
isShared?: boolean;
isSharePending?: boolean;
Expand Down Expand Up @@ -72,6 +74,7 @@ type CustomListEditorProps = {
};

export default function CustomListEditor({
autoUpdateStatus,
collections,
entries,
entryPoints,
Expand All @@ -81,6 +84,7 @@ export default function CustomListEditor({
isFetchingMoreSearchResults,
isLoaded,
isModified,
isSearchModified,
isOwner,
isShared,
isSharePending,
Expand Down Expand Up @@ -254,6 +258,7 @@ export default function CustomListEditor({
<CustomListSearch
autoUpdate={properties.autoUpdate}
isOwner={isOwner}
listId={listId}
searchParams={searchParams}
updateAutoUpdate={(value) => updateProperty?.("autoUpdate", value)}
updateSearchParam={updateSearchParam}
Expand All @@ -272,8 +277,10 @@ export default function CustomListEditor({
</section>

<CustomListEntriesEditor
autoUpdateStatus={autoUpdateStatus}
autoUpdate={properties.autoUpdate}
isOwner={isOwner}
isSearchModified={isSearchModified}
searchResults={searchResults}
entries={entries.current}
loadMoreSearchResults={loadMoreSearchResults}
Expand Down
264 changes: 153 additions & 111 deletions src/components/CustomListEntriesEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import LoadButton from "./LoadButton";

export interface CustomListEntriesEditorProps {
autoUpdate?: boolean;
autoUpdateStatus?: string;
entries?: Entry[];
entryCount?: number;
isFetchingMoreCustomListEntries: boolean;
isFetchingSearchResults: boolean;
isFetchingMoreSearchResults: boolean;
isOwner?: boolean;
isSearchModified?: boolean;
listId?: string | number;
opdsFeedUrl?: string;
searchResults?: CollectionData;
Expand Down Expand Up @@ -55,12 +57,14 @@ const renderCatalogLink = (book, opdsFeedUrl) => {

const CustomListEntriesEditor = ({
autoUpdate,
entries,
entryCount,
autoUpdateStatus,
entries = [],
entryCount = 0,
isFetchingMoreCustomListEntries,
isFetchingSearchResults,
isFetchingMoreSearchResults,
isOwner,
isSearchModified,
listId,
opdsFeedUrl,
searchResults,
Expand Down Expand Up @@ -134,7 +138,7 @@ const CustomListEntriesEditor = ({

const readOnly = !isOwner || autoUpdate;

let searchResultList = null;
let searchResultList: JSX.Element | null = null;

if (isOwner) {
searchResultList = (
Expand Down Expand Up @@ -210,7 +214,7 @@ const CustomListEntriesEditor = ({
<div className="title">{book.title}</div>

<div className="authors">
{book.authors.join(", ")}
{book.authors?.join(", ")}
</div>
</div>

Expand Down Expand Up @@ -256,125 +260,163 @@ const CustomListEntriesEditor = ({
);
}

let entryList = null;

if (!autoUpdate) {
const visibleEntryCount = entries.length;
const startNum = visibleEntryCount > 0 ? 1 : 0;
const endNum = visibleEntryCount;
const booksText = entryCount === 1 ? "book" : "books";

const entryListDisplay =
entryCount > 0
? `Displaying ${startNum} - ${endNum} of ${entryCount} ${booksText}`
: "No books in this list";

entryList = (
<div className="custom-list-entries">
<div className="droppable-header">
<h4>List Entries: {entryListDisplay}</h4>
const visibleEntryCount = entries.length;
const startNum = visibleEntryCount > 0 ? 1 : 0;
const endNum = visibleEntryCount;
const booksText = entryCount === 1 ? "book" : "books";

const entryListDisplay =
entryCount > 0
? `Displaying ${startNum} - ${endNum} of ${entryCount} ${booksText}`
: "No books in this list";

let autoUpdateStatusName = null;
let autoUpdateStatusDescription = null;

if (!listId) {
autoUpdateStatusName = "New";
autoUpdateStatusDescription =
"The system will begin to populate this list using the configured search criteria when it is saved, and will fully populate the list during the first scheduled update that occurs after it has been saved.";
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: the comma is not needed in this sentence.

...or...

possible rewording: "This is a new list. Once the initial search criteria have been saved, the system will begin to populate its entries; however, the list might not be fully populated until the next scheduled update."

} else if (isSearchModified) {
autoUpdateStatusName = "Search criteria modified";
autoUpdateStatusDescription =
"There are unsaved changes to the search criteria for this list. The system will repopulate the list using the new search criteria during the first scheduled update that occurs after it has been saved.";
} else {
if (autoUpdateStatus === "init") {
autoUpdateStatusName = "Initializing";
autoUpdateStatusDescription =
"This list was created recently. The system has partially populated the list using the configured search criteria, and will fully populate the list during the next scheduled update.";
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: comma not needed in second sentence.

...or...

possible rewording: "There are unsaved changes to the search criteria for this list. Once the changes have been saved, the new search criteria will be used to repopulate the list during the next scheduled update."

} else if (autoUpdateStatus === "repopulate") {
autoUpdateStatusName = "Repopulating";
autoUpdateStatusDescription =
"The search criteria for this list were changed recently, but the entries have not yet been updated. The system will repopulate the list using the current search criteria during the next scheduled update.";
Copy link
Contributor

Choose a reason for hiding this comment

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

possible rewording: "The search criteria for this list were changed recently, but the entries have not yet been updated. The new search criteria will be used to repopulate the list during the next scheduled update."

} else if (autoUpdateStatus === "updated") {
autoUpdateStatusName = "Updated";
autoUpdateStatusDescription =
"This list was fully populated during the last scheduled update, using the configured search criteria and the titles that were available at the time. Titles that have been acquired since the last update will be added to the list during the next scheduled update.";
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if the first sentence is entirely accurate: Are all available titles considered when the list is updated? Or just ones that have come in since the last update time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it depends on which update. If it's the first one, then all titles are considered. After that, only the new titles are considered. In either case, it should result in the list being updated to reflect all the titles that are available at the time. I was trying to describe that without getting too far into the details, but I'm open to rewordings.

Copy link
Contributor

Choose a reason for hiding this comment

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

Possible rewording of second sentence: "New titles matching the criteria will be added to the list during the next scheduled update."

} else if (!autoUpdateStatus) {
autoUpdateStatusName = "Changing to automatic";
autoUpdateStatusDescription =
"This list was populated manually, and is being changed to be updated automatically. The system will repopulate the list using the configured search criteria during the first scheduled update that occurs after it has been saved.";
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: comma not needed in first sentence.

...or...

possible rewording: "This list was populated manually, but is being changed to be updated automatically. The configured search criteria will be used to repopulate the list during the next scheduled update."

} else {
autoUpdateStatusName = autoUpdateStatus;
}
}

{!readOnly && entries?.length > 0 && (
<div>
<span>Remove all currently visible items from list:</span>
const entryList = (
<div className="custom-list-entries">
<div className="droppable-header">
<h4>List Entries: {entryListDisplay}</h4>

<Button
className="danger delete-all-button top-align"
callback={deleteAllEntries}
content={
<span>
Delete
<TrashIcon />
</span>
}
/>
{autoUpdate && (
<>
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh. I didn't know this Fragment shorthand. Had me confused for a minute.

<div className="auto-update-status-name">
Status: {autoUpdateStatusName}
</div>
)}
</div>
<aside className="auto-update-status-desc">
{autoUpdateStatusDescription}
</aside>
</>
)}

{!readOnly && <p>Drag search results here to add them to the list.</p>}

<Droppable
droppableId="custom-list-entries"
isDropDisabled={readOnly || draggingFrom !== "search-results"}
>
{(provided, snapshot) => (
<ul
ref={provided.innerRef}
id="custom-list-entries-droppable"
className={
snapshot.isDraggingOver
? " droppable dragging-over"
: "droppable"
{!readOnly && entries?.length > 0 && (
<div>
<span>Remove all currently visible items from list:</span>

<Button
className="danger delete-all-button top-align"
callback={deleteAllEntries}
content={
<span>
Delete
<TrashIcon />
</span>
}
>
{entries?.map((book) => (
<Draggable
key={book.id}
draggableId={book.id}
isDragDisabled={readOnly}
>
{(provided, snapshot) => (
<li>
<div
className={
"custom-list-entry" +
(snapshot.isDragging ? " dragging" : "")
}
ref={provided.innerRef}
style={provided.draggableStyle}
{...provided.dragHandleProps}
>
{!readOnly && <GrabIcon />}

<div>
<div className="title">{book.title}</div>

<div className="authors">
{book.authors.join(", ")}
</div>
</div>
/>
</div>
)}
</div>

{!readOnly && <p>Drag search results here to add them to the list.</p>}

<Droppable
droppableId="custom-list-entries"
isDropDisabled={readOnly || draggingFrom !== "search-results"}
>
{(provided, snapshot) => (
<ul
ref={provided.innerRef}
id="custom-list-entries-droppable"
className={
snapshot.isDraggingOver ? " droppable dragging-over" : "droppable"
}
>
{entries?.map((book) => (
<Draggable
key={book.id}
draggableId={book.id}
isDragDisabled={readOnly}
>
{(provided, snapshot) => (
<li>
<div
className={
"custom-list-entry" +
(snapshot.isDragging ? " dragging" : "")
}
ref={provided.innerRef}
style={provided.draggableStyle}
{...provided.dragHandleProps}
>
{!readOnly && <GrabIcon />}

{getMediumSVG(getMedium(book))}

<div className="links">
{renderCatalogLink(book, opdsFeedUrl)}

{!readOnly && (
<Button
className="small right-align"
callback={() => deleteEntry?.(book.id)}
content={
<span>
Remove from list
<TrashIcon />
</span>
}
/>
)}
<div>
<div className="title">{book.title}</div>

<div className="authors">
{book.authors?.join(", ")}
</div>
</div>

{provided.placeholder}
</li>
)}
</Draggable>
))}
{getMediumSVG(getMedium(book))}

{provided.placeholder}
</ul>
)}
</Droppable>
<div className="links">
{renderCatalogLink(book, opdsFeedUrl)}

{loadMoreEntries && (
<LoadButton
isFetching={isFetchingMoreCustomListEntries}
loadMore={loadMoreEntries}
/>
{!readOnly && (
<Button
className="small right-align"
callback={() => deleteEntry?.(book.id)}
content={
<span>
Remove from list
<TrashIcon />
</span>
}
/>
)}
</div>
</div>

{provided.placeholder}
</li>
)}
</Draggable>
))}

{provided.placeholder}
</ul>
)}
</div>
);
}
</Droppable>

{loadMoreEntries && (
<LoadButton
isFetching={isFetchingMoreCustomListEntries}
loadMore={loadMoreEntries}
/>
)}
</div>
);

return (
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
Expand Down
Loading