Skip to content

feat(mobile): align with the Mobile API contract (register/forgot UI, feature-flag gating)#5

Merged
fabiodalez-dev merged 3 commits into
mainfrom
feat/mobile-api-android-coherence
Jun 25, 2026
Merged

feat(mobile): align with the Mobile API contract (register/forgot UI, feature-flag gating)#5
fabiodalez-dev merged 3 commits into
mainfrom
feat/mobile-api-android-coherence

Conversation

@fabiodalez-dev

@fabiodalez-dev fabiodalez-dev commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Companion to the backend's mobile-API coherence PR. Supersedes #3 and #4.

  • Register + Forgot-password screens (new RegisterScreen, ForgotPasswordScreen) wired into login/nav, sending the full backend payload (telefono, indirizzo, password_confirm, privacy_acceptance) with matching validation.
  • Feature-flag gating — Profile/nav reflect the instance's messages/notifications/registration_enabled flags.
  • Contract alignment — Models / AuthRepository / FeatureStore updated; availability calendar states.
  • i18n strings added to all four locales (it/en/fr/de).

Verification: ./gradlew :app:compileDebugKotlin → BUILD SUCCESSFUL.

Summary by CodeRabbit

  • Nuove funzionalità

    • Aggiunte le schermate e il flusso di navigazione per registrazione e recupero password.
    • La login mostra ora le azioni “crea account” e “password dimenticata” quando disponibili.
    • Introdotto un nuovo dettaglio di disponibilità per i libri, con calendario giornaliero.
  • Miglioramenti

    • Aggiornate registrazione, cambio password e alcune aree profilo con nuovi campi e conferme.
    • Notifiche e contatti ora vengono mostrati solo se abilitati.
  • Correzioni

    • Riviste le traduzioni e i messaggi di stato per i nuovi flussi e le funzionalità disattivate.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fabiodalez-dev, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 13 minutes and 12 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 035db345-ca00-4cf5-b6d6-cd0be7e8a3bf

📥 Commits

Reviewing files that changed from the base of the PR and between b69611b and 2ba6b0f.

📒 Files selected for processing (9)
  • _contract/openapi.json
  • app/src/main/java/com/pinakes/app/data/repository/ProfileRepository.kt
  • app/src/main/java/com/pinakes/app/ui/screens/login/ForgotPasswordScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/profile/ProfileViewModel.kt
  • i18n/de.json
  • i18n/en.json
  • i18n/fr.json
  • i18n/it.json

Walkthrough

Aggiornati i contratti API e i modelli condivisi per registrazione, cambio password, catalogo, prestiti, prenotazioni, notifiche e disponibilità libri. Nell’app sono stati aggiunti i flussi di registrazione e recupero password, nuove route, e schermate che reagiscono ai feature flag e alle nuove localizzazioni.

Changes

Aggiornamenti API e flussi auth mobile

Layer / File(s) Summary
Contratti API e schemi
_contract/MOBILE_API_SPEC.md, _contract/endpoint-manifest.spec.js, _contract/health-sample.json, _contract/openapi.json
Gli schemi OpenAPI, la specifica mobile e le fixture del contratto aggiungono i nuovi campi per registrazione, cambio password, catalogo, prestiti, prenotazioni, notifiche, health e la rotta di disponibilità giornaliera del libro.
Modelli e repository
app/src/main/java/com/pinakes/app/data/model/Models.kt, app/src/main/java/com/pinakes/app/data/repository/*.kt
RegisterRequest e ChangePasswordRequest adottano i nuovi campi, e i repository AuthRepository e ProfileRepository serializzano i payload aggiornati verso il backend.
Feature flag e navigazione
app/src/main/java/com/pinakes/app/data/store/FeatureStore.kt, app/src/main/java/com/pinakes/app/ui/navigation/*.kt, app/src/main/java/com/pinakes/app/ui/screens/login/LoginViewModel.kt
FeatureStore persiste registrationEnabled; Routes, PinakesNavHost, MainScaffold e LoginViewModel aggiornano le destinazioni auth, il top bar e il refresh health iniziale.
Schermate auth
app/src/main/java/com/pinakes/app/ui/screens/login/LoginScreen.kt, app/src/main/java/com/pinakes/app/ui/screens/login/ForgotPasswordScreen.kt, app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt, i18n/de.json, i18n/en.json, i18n/fr.json, i18n/it.json
LoginScreen, ForgotPasswordScreen e RegisterScreen espongono nuovi callback, validazione, invio e stati di conferma; le localizzazioni auth aggiungono le stringhe corrispondenti.
Schermate con feature flag
app/src/main/java/com/pinakes/app/ui/screens/contact/ContactScreen.kt, app/src/main/java/com/pinakes/app/ui/screens/notifications/NotificationsScreen.kt, app/src/main/java/com/pinakes/app/ui/screens/profile/ProfileScreen.kt, app/src/main/java/com/pinakes/app/ui/screens/onboarding/OnboardingViewModel.kt, i18n/de.json, i18n/en.json, i18n/fr.json, i18n/it.json
ContactScreen, NotificationsScreen, ProfileScreen e OnboardingViewModel reagiscono ai feature flag; le localizzazioni aggiungono gli stati disabilitati e la chiusura JSON finale.

Sequence Diagram(s)

sequenceDiagram
  participant LoginScreen
  participant PinakesNavHost
  participant RegisterScreen
  participant ForgotPasswordScreen
  participant AuthRepository

  alt create account
    LoginScreen->>PinakesNavHost: onRegister()
    PinakesNavHost->>RegisterScreen: navigate Routes.REGISTER
    RegisterScreen->>AuthRepository: auth.register(...)
  else recover password
    LoginScreen->>PinakesNavHost: onForgotPassword()
    PinakesNavHost->>ForgotPasswordScreen: navigate Routes.FORGOT_PASSWORD
    ForgotPasswordScreen->>AuthRepository: auth.forgotPassword(email)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Hop hop, nel prato ho visto brillare,
nuove route e schemi pronti a saltare.
Password ritrovate, account da creare,
e il coniglio sorride: tutto può funzionare!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Il titolo è chiaro, conciso e descrive correttamente l’allineamento del client Android al contratto Mobile API con UI auth e gating feature flag.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mobile-api-android-coherence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
_contract/endpoint-manifest.spec.js (1)

301-301: 🗄️ Data Integrity & Integration | 🔵 Trivial

Verifica che wishlist.created_at abbia un default lato DB.

La riga 301 utilizza INSERT IGNORE e cattura silenziosamente eventuali errori:
try { dbExec(\INSERT IGNORE INTO wishlist (utente_id, libro_id) VALUES (...)`); } catch {}`

Se la colonna created_at nella tabella wishlist è definita NOT NULL senza un valore di default (es. DEFAULT CURRENT_TIMESTAMP), l'inserimento fallirà (poiché non viene fornito il valore) ma l'errore verrà ignorato.
Ciò porta il seed a diventare un no-op silenzioso: la riga non viene inserita, la tabella wishlist rimane vuota, e le prove successive che si aspettavano un eliminazione non avranno effetti.

Aggiungi DEFAULT CURRENT_TIMESTAMP alla definizione di created_at nel DB o modifica l'insert per includere esplicitamente il timestamp.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@_contract/endpoint-manifest.spec.js` at line 301, The wishlist seed insert is
swallowing failures, so ensure the `wishlist` table’s `created_at` is always
satisfied. Update the DB schema for `wishlist.created_at` to have a default such
as `CURRENT_TIMESTAMP`, or change the insert in the seed logic around the
`dbExec` call to explicitly provide the timestamp so `INSERT IGNORE` cannot turn
a missing value into a silent no-op.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@_contract/openapi.json`:
- Around line 235-236: The openapi contract for privacy_acceptance currently
allows any boolean, so update the schema in the privacy_acceptance property to
constrain it to true only using the existing OpenAPI schema shape (for example
const or enum) so generated clients and docs reflect the required consent.
Locate the property definition inside the contract schema and keep the rest of
the registration payload unchanged.
- Around line 1757-1831: The new protected availability endpoint currently
documents only 401/404/500, so update its response block in the openapi schema
to also include 403 and 429 alongside the existing error responses. Use the same
style as other protected routes in openapi.json and ensure the route’s responses
stay consistent with the rest of the spec so generated clients and tests can
handle authorization and rate-limit failures.

In `@app/src/main/java/com/pinakes/app/data/repository/ProfileRepository.kt`:
- Around line 34-36: The changePassword method in ProfileRepository is
incorrectly hardcoding password_confirm to the new password, which prevents
callers from passing the user-entered confirmation. Update the repository API
and its ChangePasswordRequest call site so changePassword accepts the
confirmation value explicitly and forwards it unchanged to api.changePassword,
keeping it aligned with ChangePasswordRequest and the OpenAPI contract.

In `@app/src/main/java/com/pinakes/app/ui/screens/login/ForgotPasswordScreen.kt`:
- Around line 72-76: The forgot-password failure handling in
ForgotPasswordScreen is leaking backend-specific messages via res.message, which
can expose account existence details. Update the ApiResult.Failure branch in the
state update to always use the generic forgot-password error resource/text
instead of surfacing res.message directly, and keep the flow semantics aligned
with the existing auth.forgotPassword() behavior by normalizing all errors in
this screen.

In `@app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt`:
- Around line 81-117: The submit flow in RegisterScreen.submit can fire multiple
overlapping auth.register calls because it does not guard against re-entry while
loading is true. Add an early return at the start of submit() that checks the
current state and exits when a registration request is already in progress, and
keep the loading flag set/unset around the existing validation and
viewModelScope.launch logic so duplicate taps cannot trigger multiple requests.
- Around line 246-252: The privacy checkbox row in RegisterScreen is missing an
accessible label and the text is not interactive, so update the
Row/Checkbox/Text composition to use a toggleable modifier on the whole row and
bind it to the same privacyAccepted state handler used by Checkbox and
vm::onPrivacyChange. Keep the existing Checkbox and
stringResource(R.string.register_privacy) in RegisterScreen, but make the text
part of the tappable/announced control so screen readers read one combined
element and tapping the label toggles the checkbox.

---

Nitpick comments:
In `@_contract/endpoint-manifest.spec.js`:
- Line 301: The wishlist seed insert is swallowing failures, so ensure the
`wishlist` table’s `created_at` is always satisfied. Update the DB schema for
`wishlist.created_at` to have a default such as `CURRENT_TIMESTAMP`, or change
the insert in the seed logic around the `dbExec` call to explicitly provide the
timestamp so `INSERT IGNORE` cannot turn a missing value into a silent no-op.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4248c1ef-03c6-46b2-836d-d4b812abbb2f

📥 Commits

Reviewing files that changed from the base of the PR and between 845ae1f and b69611b.

📒 Files selected for processing (23)
  • _contract/MOBILE_API_SPEC.md
  • _contract/endpoint-manifest.spec.js
  • _contract/health-sample.json
  • _contract/openapi.json
  • app/src/main/java/com/pinakes/app/data/model/Models.kt
  • app/src/main/java/com/pinakes/app/data/repository/AuthRepository.kt
  • app/src/main/java/com/pinakes/app/data/repository/ProfileRepository.kt
  • app/src/main/java/com/pinakes/app/data/store/FeatureStore.kt
  • app/src/main/java/com/pinakes/app/ui/navigation/MainScaffold.kt
  • app/src/main/java/com/pinakes/app/ui/navigation/PinakesNavHost.kt
  • app/src/main/java/com/pinakes/app/ui/navigation/Routes.kt
  • app/src/main/java/com/pinakes/app/ui/screens/contact/ContactScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/login/ForgotPasswordScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/login/LoginScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/login/LoginViewModel.kt
  • app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/notifications/NotificationsScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/onboarding/OnboardingViewModel.kt
  • app/src/main/java/com/pinakes/app/ui/screens/profile/ProfileScreen.kt
  • i18n/de.json
  • i18n/en.json
  • i18n/fr.json
  • i18n/it.json

Comment thread _contract/openapi.json Outdated
Comment thread _contract/openapi.json
Comment thread app/src/main/java/com/pinakes/app/data/repository/ProfileRepository.kt Outdated
Comment thread app/src/main/java/com/pinakes/app/ui/screens/login/ForgotPasswordScreen.kt Outdated
Comment on lines +246 to +252
Row(modifier = form, verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = state.privacyAccepted, onCheckedChange = vm::onPrivacyChange)
Text(
text = stringResource(R.string.register_privacy),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt | sed -n '240,260p'

Repository: fabiodalez-dev/Pinakes-Android

Length of output: 1412


🏁 Script executed:

head -100 app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt | grep -A 5 "^import"

Repository: fabiodalez-dev/Pinakes-Android

Length of output: 2375


🏁 Script executed:

grep -n "val form\|form =" app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt | head -5

Repository: fabiodalez-dev/Pinakes-Android

Length of output: 233


🏁 Script executed:

cat -n app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt | sed -n '1,50p'

Repository: fabiodalez-dev/Pinakes-Android

Length of output: 2709


Associa semanticamente il testo privacy alla checkbox

Attualmente la Checkbox a riga 247 manca di un'etichetta accessibile per gli screen reader, e il testo a riga 249 non è cliccabile. TalkBack/VoiceOver leggeranno la casella senza contesto e non consentiranno di attivare il controllo cliccando sul testo.

Implementare una soluzione basata su modifier.toggleable() per rendere l'intera riga interattiva e unire il contenuto per la lettura:

💡 Patch proposta
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.semantics
 
-            Row(modifier = form, verticalAlignment = Alignment.CenterVertically) {
-                Checkbox(checked = state.privacyAccepted, onCheckedChange = vm::onPrivacyChange)
+            Row(
+                modifier = form
+                    .toggleable(
+                        value = state.privacyAccepted,
+                        role = Role.Checkbox,
+                        onValueChange = vm::onPrivacyChange,
+                    )
+                    .semantics(mergeDescendants = true) {},
+                verticalAlignment = Alignment.CenterVertically,
+            ) {
+                Checkbox(checked = state.privacyAccepted, onCheckedChange = null)
                 Text(
                     text = stringResource(R.string.register_privacy),
                     style = MaterialTheme.typography.bodySmall,
                     color = MaterialTheme.colorScheme.onSurfaceVariant,
+                    modifier = Modifier.padding(start = Spacing.sm),
                 )
             }

Verificare il comportamento con uno screen reader dopo l'implementazione.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Row(modifier = form, verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = state.privacyAccepted, onCheckedChange = vm::onPrivacyChange)
Text(
text = stringResource(R.string.register_privacy),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Row(
modifier = form
.toggleable(
value = state.privacyAccepted,
role = Role.Checkbox,
onValueChange = vm::onPrivacyChange,
)
.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(checked = state.privacyAccepted, onCheckedChange = null)
Text(
text = stringResource(R.string.register_privacy),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = Spacing.sm),
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/pinakes/app/ui/screens/login/RegisterScreen.kt` around
lines 246 - 252, The privacy checkbox row in RegisterScreen is missing an
accessible label and the text is not interactive, so update the
Row/Checkbox/Text composition to use a toggleable modifier on the whole row and
bind it to the same privacyAccepted state handler used by Checkbox and
vm::onPrivacyChange. Keep the existing Checkbox and
stringResource(R.string.register_privacy) in RegisterScreen, but make the text
part of the tappable/announced control so screen readers read one combined
element and tapping the label toggles the checkbox.

…label (review)

From the contract-coherence review:
- RegisterViewModel.submit() validated only min length (>=8). Mirror the backend
  AuthController rules client-side so the user gets a clear error instead of a
  server 422: 8-72 chars AND at least one uppercase, lowercase and digit.
- The confirm-password field reused profile_confirm_new_password (from the
  change-password flow); add a dedicated register_confirm_password label.

New i18n keys (register_error_password_length, register_error_password_weak,
register_confirm_password) added to all four locales. Verified: compileDebugKotlin
BUILD SUCCESSFUL.
- RegisterScreen: guard submit() against duplicate registration requests
  while a call is in flight or already completed (account creation is not
  idempotent)
- ForgotPasswordScreen: never surface the backend's raw failure message —
  a specific "user not found" enabled account enumeration; always show a
  generic error to preserve the neutral "if that email is registered…"
  semantics
- ProfileRepository: changePassword now forwards the real confirmation
  value instead of hardcoding password_confirm = new, so a client-side
  mismatch is no longer masked at the repository layer
- openapi contract: constrain privacy_acceptance to const:true and document
  403/429 on the book availability route
@fabiodalez-dev

Copy link
Copy Markdown
Owner Author

Addressed the 5 inline review findings (verified each against the code):

  • RegisterScreen (duplicate submit): added a if (loading || sent) return guard at the top of submit() — account creation is not idempotent.
  • ForgotPasswordScreen (account enumeration): stopped surfacing the backend's raw failure message; failures now always show a generic error, keeping the neutral "if that email is registered…" semantics.
  • ProfileRepository (changePassword): now forwards the real confirmation value instead of hardcoding password_confirm = new, so a client-side mismatch is no longer masked at the repository layer.
  • openapi contract privacy_acceptance: constrained to const: true.
  • openapi contract availability route: documented 403/429.

Build green (assembleDebug), Kotlin compiles clean. The CodeRabbit "fail" status is a billing/credits state, not a code issue.

@fabiodalez-dev fabiodalez-dev merged commit 03cc9b3 into main Jun 25, 2026
1 check failed
@fabiodalez-dev fabiodalez-dev deleted the feat/mobile-api-android-coherence branch June 25, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant