feat(mobile): wire Activity History to GET /my/activity#83
Merged
Conversation
The Activity History screen (bottom-nav "History") was permanently empty — it hardcoded `val sections = emptyList()` and never called the API. Wire it to the EXISTING user-scoped endpoint GET /api/v1/my/activity (the current user's own audit events; no admin authority required). The admin GET /api/v1/audit-logs path is left untouched (it 403s for normal users). - AuditLogApi/AuditLogApiImpl: add getMyActivity(page, size) → GET my/activity, reusing the existing AuditLogPageDto response type. getAuditLogs (admin) unchanged. - AuditLogRepository(+Impl): add getMyActivity wrapped in runCatching; extract the shared AuditLogDto→AuditLog mapping into a toDomain() helper. - New ActivityHistoryViewModel + ActivityHistoryUiState: loads page 0 (size 50) on init, exposes isLoading / errorMessage / events. Registered in DI via factoryOf(::ActivityHistoryViewModel). - ActivityHistoryScreen: replace the hardcoded empty list with the VM state. Map each AuditLog.action → chip category: contains "VERIF" → Verifications, contains "ENROLL" → Enrollments, everything else shows only under All. Group by date (YYYY-MM-DD from the ISO timestamp) for the section headers. Adds a loading spinner and a distinct error state with Retry (errors are no longer silently rendered as an empty list). - AppNavigation: inject the VM via koinInject().disposeOnLeave() and pass state, matching the existing per-screen ViewModel wiring. - i18n: new ACTHIST_LOAD_ERROR key with EN + TR; reuse existing RETRY/empty/chip keys. - Update FakeAuditLogRepository test mock with getMyActivity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ahmetabdullahgultekin
added a commit
that referenced
this pull request
Jun 3, 2026
…ed; scope-B done) (#84) - CHANGELOG: add the Activity History wiring (#83) under [Unreleased]. - docs/MOBILE_TRIAGE_2026-06-03.md: mark #2 (Activity History), #5 (approve-login) FIXED and #7 (cross-device QR) PARTIAL; replace 'Deferred follow-ups' with a Resolution section (client-apps #82/#83, web-app #199 merged + deployed) + the remaining backend-deferred items. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ahmetabdullahgultekin
added a commit
that referenced
this pull request
Jun 3, 2026
…#85) The operator APK rebuild is done — CI-built release-signed APK from origin/main (CN=FIVUCSAS, upgrades in place), carrying #82 + #83 to the device. Adds the signing-via-CI note and a pointer to the parent USER_FINDINGS_2026-06-03.md sweep. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ahmetabdullahgultekin
added a commit
that referenced
this pull request
Jun 3, 2026
…crash) ActivityHistoryViewModel and AuditLogDashboardViewModel depend on AuditLogRepository, but #83 wired the ViewModel without registering AuditLogApi (NetworkModule) or AuditLogRepository (RepositoryModule). Opening Activity History / the audit-log dashboard on the release build throws Koin NoDefinitionFoundException -> FATAL crash. Verified on-device. Adds the two missing single<> bindings (matches the existing pattern). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ahmetabdullahgultekin
added a commit
that referenced
this pull request
Jun 3, 2026
…y, NFC, hide Add-card (#86) * fix(mobile): pre-demo fixes — QR/approve swallowed-401, session expiry, NFC, hide Add-card Four additive, reversible pre-demo fixes (demo 2026-06-03). Verified each against origin/main before changing. #1 + approve-login silent-approve swallow - QrLoginApiImpl.approveSession and ApproveLoginApiImpl.decide POSTed without reading the response, so a non-2xx (e.g. expired bearer → 401/403) was swallowed: the phone flipped to APPROVED while the server session stayed PENDING and the originating (web) login never advanced. Both now throw a ResponseException on a non-success status (status code included in the message so ErrorMapper maps it). The 401 path now also triggers the transparent refresh+retry below, so a stale-but-refreshable bearer recovers automatically. #2 session ~15-min logout - NetworkModule: the refresh-on-401 HttpResponseValidator could not re-fire the failed request (its own TODO) → first call after the 15-min access-token lifetime surfaced as a logout. Replaced it with an HttpSend interceptor (part of ktor-client-core — no new dependency; the project deliberately avoids ktor-client-auth, see desktopApp RefreshInterceptor) that refreshes under the existing rotation mutex AND retries the original request once with the fresh bearer. Refresh logic extracted to a reusable refreshAccessToken() helper. - TokenManager.isAuthenticated() checked token PRESENCE only; now tolerates an expired access token when a refresh token exists (decodes the JWT exp claim via base64url + kotlinx.serialization; fail-open for non-JWT tokens), so a returning user is not bounced to login when a silent refresh is possible. #7 NFC won't read (OS detects card, app stays "ready to scan") - Removed the foreground TECH_DISCOVERED intent-filter + nfc_tech_filter meta-data from AndroidManifest so reader mode (enableReaderMode) wins instead of competing with the system tag-dispatcher. - Relaxed AndroidNfcService.processTag: previously dropped a tap unless state was exactly WaitingForCard; now only rejects a tap while a read is already in flight (Reading), so an Idle/Completed/Error tap starts a fresh read. - NOTE: NOT verifiable without a physical device — needs on-device `adb logcat` confirmation before being trusted. Fully reversible. #8 "Add card" dead-end button - Hidden the DashboardScreen "Add card" QuickAction: CardScanScreen is a camera photo wizard that never uploads/persists anything. Screen + nav route left intact (reversible — uncomment to restore once OCR/upload backend exists). Tests: +10 (QrApproveLoginStatusCheckTest 6, TokenManagerAuthExpiryTest 4). Compile: :shared:compileDebugKotlinAndroid + :androidApp:compileDebugKotlin + processDebugMainManifest all green. Full shared unit suite: 489 tests, 0 failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mobile,di): register AuditLogApi + AuditLogRepository in Koin (#9 crash) ActivityHistoryViewModel and AuditLogDashboardViewModel depend on AuditLogRepository, but #83 wired the ViewModel without registering AuditLogApi (NetworkModule) or AuditLogRepository (RepositoryModule). Opening Activity History / the audit-log dashboard on the release build throws Koin NoDefinitionFoundException -> FATAL crash. Verified on-device. Adds the two missing single<> bindings (matches the existing pattern). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(mobile): add 'Login requests' home tile (#3) Surfaces the approve-login approver screen (Screen.ApproveLogin) on the Dashboard Quick Actions grid (was reachable only via Profile). New StringKey.DASH_LOGIN_REQUESTS (EN 'Login Requests' / TR 'Giriş İstekleri'). Additive nav tile; opens the already-registered ApproveLoginScreen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mobile,nfc): clarify NFC reader buttons + friendly passive-auth-unavailable message (#5) - Relabel the 3 confusingly-similar buttons into an ordered flow (camera MRZ fill / read chip / read any card) - Show a friendly message for the known NO_TRUST_STORE passive-auth case instead of a raw error - Clarify YYMMDD date fields Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ahmet Abdullah Gultekin <rollingcat.help@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ahmetabdullahgultekin
added a commit
that referenced
this pull request
Jun 6, 2026
…otes (#89) - CHANGELOG: promote [Unreleased] → [5.3.1] - 2026-06-06; add the MFA transport/IO retry fix (#87, headline) and cross-device/session fixes (#86) that had landed post-5.3.0 without changelog entries; keep the #82/#83 items. - CLAUDE.md: refresh stale "Latest: v5.2.3 / versionCode 10" → "v5.3.1 / versionCode 13"; document the CI signed-build command (host has no keystore pw); add a "v5.3.1 — MFA stale-connection retry" section. Co-authored-by: Ahmet Abdullah Gultekin <rollingcat.help@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The mobile Activity History screen (bottom-nav "History" tab) was permanently empty — it hardcoded
val sections = emptyList()and never called the API. This wires it to the existing user-scoped endpointGET /api/v1/my/activity(the current user's own audit events; the web dashboard already uses it). The adminGET /api/v1/audit-logspath (which 403s for normal users) is left untouched.Changes
Data layer (
shared/commonMain)AuditLogApi/AuditLogApiImpl: addgetMyActivity(page, size)→GET my/activity?page=&size=, reusing the existingAuditLogPageDtoresponse type.getAuditLogs(admin) unchanged.AuditLogRepository(+Impl): addgetMyActivitywrapped inrunCatchinglike the existing method; extracted the sharedAuditLogDto→AuditLogmapping into a privatetoDomain()helper.Presentation
ActivityHistoryViewModel+ActivityHistoryUiState: loads page 0 (size 50) oninit, exposesisLoading/errorMessage/events. Registered in DI viafactoryOf(::ActivityHistoryViewModel).ActivityHistoryScreen: replaced the hardcoded empty list with the VM state. Adds a loading spinner and a distinct error state with a Retry button (load errors are no longer silently rendered as an empty list).AppNavigation: injects the VM viakoinInject<ActivityHistoryViewModel>().disposeOnLeave()and passes state down — same pattern as the other per-screen ViewModels.action → chip mapping
AuditLog.actioncontainsVERIF(e.g.BIOMETRIC_VERIFIED)ENROLL(e.g.ENROLLMENT_*,BIOMETRIC_ENROLLED)Entries are grouped by date (
YYYY-MM-DDparsed from the ISO timestamp) for the section headers the screen already renders.i18n
StringKey.ACTHIST_LOAD_ERRORwith bothenStrings("Couldn't load your activity history.") andtrStrings("Etkinlik geçmişiniz yüklenemedi.") entries. Reuses existingRETRY,ACTHIST_EMPTY, and chip keys.Tests
FakeAuditLogRepositorymock updated withgetMyActivity.Verification
Both
:shared:compileDebugKotlinAndroidand:androidApp:compileDebugKotlincompile cleanly (only pre-existing deprecation warnings remain).🤖 Generated with Claude Code