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

EKIRJASTO-88 Add selected books #187

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b021194
Add basic functions and icons for selected
natlibfi-burger Jan 3, 2025
5a5ce0f
Add selection button to single book view
natlibfi-burger Jan 7, 2025
5820da8
Change the selected icon
natlibfi-burger Jan 7, 2025
54b777e
Ensure correct functioning of tasks and visuals
natlibfi-burger Jan 22, 2025
0dc9e0d
Minor UI fixes
natlibfi-burger Jan 23, 2025
8b77244
Move selected location and ensure correct funtions
natlibfi-burger Feb 5, 2025
e16fb29
Remove useless log
natlibfi-burger Feb 5, 2025
84eae2c
Refractor BookSync because of repetetive code
natlibfi-burger Feb 5, 2025
e82637d
Fix value naming
natlibfi-burger Feb 5, 2025
b3104b5
Remove leftover logs and markers
natlibfi-burger Feb 5, 2025
8ae48a5
Minor fix
natlibfi-burger Feb 5, 2025
a39df6b
Merge branch 'main' into EKIR-88-add-selected-books
natlibfi-burger Feb 6, 2025
9af56e5
Add selectedURI to tests and mockProfiles
natlibfi-burger Feb 6, 2025
66254a4
Attempt to fix tests to simulate syncBooks correct
natlibfi-burger Feb 7, 2025
c32845c
Replace selectedURI links with null
natlibfi-burger Feb 7, 2025
59cba8c
Add selectedURI to mock account
natlibfi-burger Feb 7, 2025
950b538
Add selected fetch to 401 test
natlibfi-burger Feb 7, 2025
fa7c95f
Add the handling od 401 to selection functions
natlibfi-burger Feb 7, 2025
62cd884
Adjust the bottom bar and fix book returning
natlibfi-burger Feb 24, 2025
088c3bc
Show login if select while logged out
natlibfi-burger Feb 24, 2025
afd343b
Add same handling to unselect
natlibfi-burger Feb 24, 2025
58f52bd
Add translatable title to selected fragment
natlibfi-burger Feb 25, 2025
1a821df
Minor fixes
natlibfi-burger Mar 6, 2025
35e42be
Refresh views after book's status is reset
natlibfi-burger Mar 6, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ class AccessibilityService private constructor(
is BookStatus.Loanable -> {
val notHoldable = this.previousStatusIsNot(event, BookStatus.Holdable::class.java)
val notLoanable = this.previousStatusIsNot(event, BookStatus.Loanable::class.java)
if (notHoldable && notLoanable) {
val notUnselected = this.previousStatusIsNot(event, BookStatus.Unselected::class.java)
val notSelected = this.previousStatusIsNot(event, BookStatus.Selected::class.java)
if (notHoldable && notLoanable && notUnselected && notSelected) {
this.speak(this.strings.bookReturned(book.entry.title))
} else {
// Nothing to do
Expand Down Expand Up @@ -126,6 +128,20 @@ class AccessibilityService private constructor(
is BookStatus.RequestingDownload,
is BookStatus.DownloadWaitingForExternalAuthentication,
is BookStatus.DownloadExternalAuthenticationInProgress,
is BookStatus.Selected -> {
if (this.previousStatusIsNot(event, BookStatus.Selected::class.java)) {
this.speak(this.strings.bookSelected(book.entry.title))
} else {
// Nothing to do
}
}
is BookStatus.Unselected -> {
if (this.previousStatusIsNot(event, BookStatus.Unselected::class.java)) {
this.speak(this.strings.bookUnselected(book.entry.title))
} else {
// Nothing to do
}
}
is BookStatus.Revoked -> {
}
null -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ class AccessibilityStrings(

override fun bookLoanLimitReached(): String =
this.resources.getString(R.string.reachedLoanLimit)

override fun bookSelected(title: String): String =
this.resources.getString(R.string.bookSelected, title)

override fun bookUnselected(title: String): String =
this.resources.getString(R.string.bookUnselected, title)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ interface AccessibilityStringsType {
fun bookFailedLoan(title: String): String
fun bookFailedDownload(title: String): String
fun bookLoanLimitReached(): String
fun bookSelected(title: String): String
fun bookUnselected(title: String): String
}
2 changes: 2 additions & 0 deletions simplified-accessibility/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
<string name="bookIsOnHold">A reservation has been placed for the book \'%1$s\'</string>
<string name="bookReturned">The book \'%1$s\' has been successfully returned</string>
<string name="reachedLoanLimit">You have reached your loan limit</string>
<string name="bookSelected">The book \'%1$s\' has been added to favorites</string>
<string name="bookUnselected">The book \'%1$s\' has been removed from favorites</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ data class AccountProvider(
override val authenticationAlternatives: List<AccountProviderAuthenticationDescription>,
override val supportsReservations: Boolean,
override val loansURI: URI?,
override val selectedURI: URI?,
override val cardCreatorURI: URI?,
override val authenticationDocumentURI: URI?,
override val catalogURI: URI,
Expand Down Expand Up @@ -56,6 +57,7 @@ data class AccountProvider(
authenticationDocumentURI = other.authenticationDocumentURI,
cardCreatorURI = other.cardCreatorURI,
catalogURI = other.catalogURI,
selectedURI = other.selectedURI,
description = other.description,
displayName = other.displayName,
eula = other.eula,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ interface AccountProviderType : Comparable<AccountProviderType> {

val loansURI: URI?

/**
* @return The URI of the user selected feed, if supported
*/

val selectedURI: URI?

/**
* @return The URI to reset the user's password, if any
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ object AccountProvidersJSON {
this.putConditionally(node, "eula", provider.eula)
this.putConditionally(node, "license", provider.license)
this.putConditionally(node, "loansURI", provider.loansURI)
this.putConditionally(node, "selectedURI", provider.selectedURI)
this.putConditionally(node, "logo", provider.logo)
this.putConditionally(node, "patronSettingsURI", provider.patronSettingsURI)
this.putConditionally(node, "privacyPolicy", provider.privacyPolicy)
Expand Down Expand Up @@ -275,6 +276,8 @@ object AccountProvidersJSON {
JSONParserUtilities.getURIOrNull(obj, "logo")
val loansURI =
JSONParserUtilities.getURIOrNull(obj, "loansURI")
val selectedURI =
JSONParserUtilities.getURIOrNull(obj, "selectedURI")
val patronSettingsURI =
JSONParserUtilities.getURIOrNull(obj, "patronSettingsURI")
val privacyPolicy =
Expand Down Expand Up @@ -318,6 +321,7 @@ object AccountProvidersJSON {
isProduction = isProduction,
license = license,
loansURI = loansURI,
selectedURI = selectedURI,
location = location,
logo = logo,
mainColor = mainColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class AccountProviderResolution(
isProduction = this.description.isProduction,
license = authDocument?.licenseURI,
loansURI = authDocument?.loansURI,
selectedURI = authDocument?.selectedURI,
logo = authDocument?.logoURI ?: this.description.logoURI?.hrefURI,
mainColor = authDocument?.mainColor ?: "red",
patronSettingsURI = authDocument?.patronSettingsURI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ class AccountProviderResolution(
supportsReservations = supportsReservations,
updated = updated,
location = this.description.location,
alternateURI = alternateURI
alternateURI = alternateURI,
selectedURI = authDocument?.selectedURI
)

taskRecorder.finishSuccess(accountProvider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class EkirjastoAccountFallback : AccountProviderFallbackType {
isProduction = true,
license = null,
loansURI = null,
selectedURI = null,
logo = null,
mainColor = "orange",
patronSettingsURI = null,
Expand Down
2 changes: 1 addition & 1 deletion simplified-app-ekirjasto/src/main/res/values/drawables.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!-- Overrides to original Simplified / Palace drawable resources not exposed in theme variables -->
<drawable name="tab_catalog">@drawable/ic_elibrary_catalog_active</drawable>
<drawable name="tab_books">@drawable/ic_elibrary_mybooks_active</drawable>
<drawable name="tab_holds">@drawable/ic_elibrary_reservation_active</drawable>
<drawable name="tab_selected">@drawable/ic_elibrary_reservation_active</drawable>
<drawable name="tab_settings">@drawable/ic_elibrary_settings_active</drawable>
<drawable name="ic_baseline_arrow_back_24">@drawable/ic_caret_left</drawable>
<drawable name="main_icon">@drawable/ekirjasto_logo_smaller</drawable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,23 @@ class BorrowTask private constructor(

try {
val database = this.account.bookDatabase
val dbEntry = database.createOrUpdate(book.id, entry)
this.databaseEntry = dbEntry

//If the book is already in the database, it might be selected
//And just overwriting the existing entry with the new one would not carry that information over
if (database.books().contains(book.id)) {
//If book already in db, get the entry and insert the selected information already available
val dbEntry = database.entry(book.id)
//Build a new feed entry
val adjustedEntry = OPDSAcquisitionFeedEntry.newBuilderFrom(entry)
.setSelectedOption(dbEntry.book.entry.selected)
.build()
//Create the database entry
this.databaseEntry = database.createOrUpdate(book.id, adjustedEntry)
} else {
this.databaseEntry = database.createOrUpdate(book.id, entry)
}
this.taskRecorder.currentStepSucceeded("Book database updated.")
return dbEntry.book
return this.databaseEntry!!.book
} catch (e: Exception) {
this.error("[{}]: failed to set up book database: ", book.id.brief(), e)
this.taskRecorder.currentStepFailed(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,26 @@ interface BooksControllerType {
accountID: AccountID,
bookID: BookID
): FluentFuture<TaskResult<Unit>>

/**
* Add the chosen book to a list of selected books.
*
* @param accountID The account that selected the book
* @param feedEntry The FeedEntry for the book
*/
fun bookAddToSelected(
accountID: AccountID,
feedEntry: FeedEntry.FeedEntryOPDS
) : FluentFuture<TaskResult<*>>

/**
* Remove the chosen book to a list of selected books.
*
* @param accountID The account that removed the book
* @param bookID The ID of the book
*/
fun bookRemoveFromSelected(
accountID: AccountID,
feedEntry: FeedEntry.FeedEntryOPDS
) : FluentFuture<TaskResult<*>>
}
1 change: 1 addition & 0 deletions simplified-books-controller/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation(project(":simplified-parser-api"))
implementation(project(":simplified-patron-api"))
implementation(project(":simplified-presentableerror-api"))
implementation(project(":simplified-futures"))
implementation(project(":simplified-profiles-api"))
implementation(project(":simplified-profiles-controller-api"))
implementation(project(":simplified-services-api"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package org.nypl.simplified.books.controller

import com.io7m.jfunctional.Some
import org.joda.time.DateTime
import org.librarysimplified.mdc.MDCKeys
import org.nypl.simplified.accounts.api.AccountID
import org.nypl.simplified.accounts.database.api.AccountType
import org.nypl.simplified.books.api.BookID
import org.nypl.simplified.books.book_database.api.BookDatabaseException
import org.nypl.simplified.books.book_registry.BookRegistryType
import org.nypl.simplified.books.book_registry.BookStatus
import org.nypl.simplified.books.book_registry.BookWithStatus
import org.nypl.simplified.profiles.api.ProfileID
import org.nypl.simplified.profiles.api.ProfilesDatabaseType
import org.nypl.simplified.taskrecorder.api.TaskRecorder
Expand Down Expand Up @@ -44,8 +48,16 @@ class BookDeleteTask(
MDC.put(MDCKeys.BOOK_TITLE, entry.book.entry.title)
MDCKeys.put(MDCKeys.BOOK_PUBLISHER, entry.book.entry.publisher)

entry.delete()
this.bookRegistry.clearFor(entry.book.id)
//If the book is still selected, don't delete the book, just update
if (entry.book.entry.selected is Some<DateTime>) {
logger.debug("Book is selected, don't delete, just update")
this.bookRegistry.update(BookWithStatus(entry.book, BookStatus.fromBook(entry.book)))
} else {
//Otherwise delete the db and registry entries
logger.debug("Book not selected delete from database and register")
entry.delete()
this.bookRegistry.clearFor(entry.book.id)
}
this.taskRecorder.finishSuccess(Unit)
} catch (e: Exception) {
this.taskRecorder.currentStepFailed(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.nypl.simplified.books.controller

import com.io7m.jfunctional.None
import com.io7m.jfunctional.OptionType
import com.io7m.jfunctional.Some
import com.io7m.junreachable.UnreachableCodeException
import org.joda.time.DateTime
Expand Down Expand Up @@ -86,12 +88,41 @@ class BookRevokeTask(

this.debug("revoke")
this.taskRecorder.beginNewStep(this.revokeStrings.revokeStarted)
//Set the status in bookRegistry to Requesting revoke
this.publishRequestingRevokeStatus()
//Setup the database, by looking up the current database entry for the book
//And publishing the revoke status again
this.setupBookDatabaseEntry(account)
//Revoke the book based on its format
this.revokeFormatHandle(account)
//Revoke request is sent to server, we set the revoke status again
//and from the answer we form a new entry,
//That we store to the db and register
//HERE
this.revokeNotifyServer(account)
this.revokeNotifyServerDeleteBook()
this.bookRegistry.clearFor(this.bookID)

//Get the registry entry
val revokeBook = bookRegistry.books()[this.bookID]

//If there is a book in the registry that is selected, just publish revoked status and then
//Update the database entry with the selected info
// And then update the registry from the database
if (revokeBook != null && revokeBook.book.entry.selected is Some<DateTime>) {
this.publishRevokedStatus()
//Update the database entry with the selected info
val updatedEntry = OPDSAcquisitionFeedEntry.newBuilderFrom(this.databaseEntry.book.entry)
.setSelectedOption(revokeBook.book.entry.selected)
.build()
//Write the entry to the database
this.databaseEntry.writeOPDSEntry(updatedEntry)
//Update the book registry, based on the book that we just updated to database
this.publishStatusFromDatabase()
} else {
//If not selected, we delete the book from database
//And we clear the registry too
this.revokeNotifyServerDeleteBook()
this.bookRegistry.clearFor(this.bookID)
}
return this.taskRecorder.finishSuccess(Unit)
}

Expand Down
Loading