Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions _contract/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,21 @@
"type": "string",
"description": "Raw prestiti.stato value."
},
"status_label": {
"type": [
"string",
"null"
],
"description": "Server-localized label for status (since API 1.4.3, absent/null on older servers). Fallback for states this client has no local mapping for; known states keep device-localized resources."
},
"requested_at": {
"type": [
"string",
"null"
],
"format": "date",
"description": "Date the loan request was created (since API 1.4.3). The honest date for cancelled/expired loans, which never went out."
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"loaned_at": {
"type": "string",
"format": "date",
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/java/com/pinakes/app/data/model/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,13 @@ data class LoanItem(
@SerialName("book_id") val bookId: Int = 0,
val title: String = "",
@SerialName("cover_url") val coverUrl: String? = null,
val status: String = "", // in_corso | concluso | in_scadenza | scaduto | prenotato | in_attesa
val status: String = "", // raw prestiti.stato value (see StatusMapping.loan)
// Server-localized label for `status` (API >= 1.4.3, null on older servers).
// Used as the chip fallback for states this app version doesn't know yet.
@SerialName("status_label") val statusLabel: String? = null,
// Date the loan request was created (API >= 1.4.3, null on older servers).
// The honest date for cancelled/expired loans, which never went out.
@SerialName("requested_at") val requestedAt: String? = null,
@SerialName("loaned_at") val loanedAt: String? = null,
@SerialName("due_at") val dueAt: String? = null,
@SerialName("returned_at") val returnedAt: String? = null,
Expand Down
14 changes: 11 additions & 3 deletions app/src/main/java/com/pinakes/app/ui/common/StatusMapping.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ data class StatusLabel(@param:StringRes val resId: Int?, val fallback: String? =
*/
object StatusMapping {

fun loan(stato: String): Pair<AvailabilityStatus, StatusLabel> = when (stato) {
/**
* @param serverLabel the server-localized `status_label` (API >= 1.4.3), used as the
* fallback text for states this app version doesn't know yet — the server's own
* wording beats a humanized snake_case guess. Null/blank on older servers.
*/
fun loan(stato: String, serverLabel: String? = null): Pair<AvailabilityStatus, StatusLabel> = when (stato) {
// Active, on time — available-green (this is the good state).
"in_corso" -> AvailabilityStatus.Available to StatusLabel(R.string.loan_status_on_loan)
// Overdue — RED, the most important alert state.
Expand All @@ -42,8 +47,11 @@ object StatusMapping {
"in_scadenza" -> AvailabilityStatus.DueSoon to StatusLabel(R.string.loan_status_due_soon)
"concluso" -> AvailabilityStatus.Returned to StatusLabel(R.string.loan_status_returned)
"in_attesa" -> AvailabilityStatus.DueSoon to StatusLabel(R.string.loan_status_pending_approval)
else -> AvailabilityStatus.LoanActive to
StatusLabel(null, stato.replace('_', ' ').replaceFirstChar { it.uppercase() })
else -> AvailabilityStatus.LoanActive to StatusLabel(
null,
serverLabel?.takeIf { it.isNotBlank() }
?: stato.replace('_', ' ').replaceFirstChar { it.uppercase() },
)
}

fun reservation(stato: String): Pair<AvailabilityStatus, StatusLabel> = when (stato) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,17 @@ private fun androidx.compose.foundation.lazy.LazyItemScope.LoanRow(
loan: LoanItem,
onBookClick: (Int) -> Unit,
) {
val (status, statusLabel) = StatusMapping.loan(loan.status)
val (status, statusLabel) = StatusMapping.loan(loan.status, loan.statusLabel)
val label = statusLabel.resId?.let { stringResource(it) } ?: statusLabel.fallback
val overdue = StatusMapping.loanGroup(loan.status) == StatusMapping.LoanGroup.Overdue
// Cancelled/expired loans never went out: "Due <date>" or "Borrowed <date>"
// lines would be misleading, so show the request date (API >= 1.4.3) —
// loanedAt is the *requested start*, not a borrow date, for these states.
val neverWentOut = loan.status in setOf("annullato", "scaduto")
val dateLine = when {
neverWentOut -> loan.requestedAt?.let {
stringResource(R.string.library_requested_on, DateFormat.date(it))
}
overdue && loan.dueAt != null ->
stringResource(R.string.library_overdue_since, DateFormat.date(loan.dueAt))
overdue -> stringResource(R.string.library_overdue_label)
Expand Down
22 changes: 22 additions & 0 deletions app/src/test/java/com/pinakes/app/StatusMappingMoreTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,28 @@ class StatusMappingMoreTest {
assertEquals("Qualche stato strano", label.fallback)
}

@Test fun loanUnknownStatePrefersServerLabelOverHumanizedRaw() {
// API >= 1.4.3 ships a server-localized status_label: for states this app
// version doesn't know, the server's wording beats the snake_case guess.
val label = StatusMapping.loan("stato_futuro", serverLabel = "Etichetta dal server").second
assertNull(label.resId)
assertEquals("Etichetta dal server", label.fallback)
}

@Test fun loanUnknownStateIgnoresBlankServerLabel() {
val label = StatusMapping.loan("stato_futuro", serverLabel = " ").second
assertEquals("Stato futuro", label.fallback)
}

@Test fun loanKnownStateKeepsLocalizedResourceEvenWithServerLabel() {
// Known states keep the app's own translations (they follow the DEVICE
// language, while status_label follows the server's language).
val (status, label) = StatusMapping.loan("annullato", serverLabel = "Cancelled")
assertEquals(AvailabilityStatus.Returned, status)
assertNull(label.fallback)
assertTrue(label.resId != null)
}

@Test fun reservationMapsEnglishAndItalianSpellings() {
assertEquals(AvailabilityStatus.Available, StatusMapping.reservation("attiva").first)
assertEquals(AvailabilityStatus.Available, StatusMapping.reservation("active").first)
Expand Down
1 change: 1 addition & 0 deletions i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"library_returned_on": "Zurückgegeben am %1$s",
"library_due_on": "Fällig %1$s",
"library_borrowed_on": "Ausgeliehen am %1$s",
"library_requested_on": "Angefragt am %1$s",
"library_queue_position": "Warteschlange #%1$d",
"library_cancel": "Abbrechen",
"library_confirm_cancel_title": "Reservierung stornieren?",
Expand Down
1 change: 1 addition & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"library_returned_on": "Returned %1$s",
"library_due_on": "Due %1$s",
"library_borrowed_on": "Borrowed %1$s",
"library_requested_on": "Requested %1$s",
"library_queue_position": "Queue #%1$d",
"library_cancel": "Cancel",
"library_confirm_cancel_title": "Cancel reservation?",
Expand Down
1 change: 1 addition & 0 deletions i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"library_returned_on": "Rendu le %1$s",
"library_due_on": "Échéance %1$s",
"library_borrowed_on": "Emprunté le %1$s",
"library_requested_on": "Demandé le %1$s",
"library_queue_position": "File d'attente #%1$d",
"library_cancel": "Annuler",
"library_confirm_cancel_title": "Annuler la réservation ?",
Expand Down
1 change: 1 addition & 0 deletions i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"library_returned_on": "Restituito il %1$s",
"library_due_on": "Scadenza %1$s",
"library_borrowed_on": "Preso in prestito il %1$s",
"library_requested_on": "Richiesto il %1$s",
"library_queue_position": "In coda #%1$d",
"library_cancel": "Annulla",
"library_confirm_cancel_title": "Annullare la prenotazione?",
Expand Down
Loading