Skip to content

Fix/mobiles#102

Merged
FloChehab merged 9 commits into
mainfrom
fix/mobiles
Jun 5, 2026
Merged

Fix/mobiles#102
FloChehab merged 9 commits into
mainfrom
fix/mobiles

Conversation

@FloChehab

@FloChehab FloChehab commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Fixes to make the Mobile App recording experience more robust.
Mostly the files were stored in cache and never deleted.

Summary by CodeRabbit

  • New Features

    • Alerts users when recorded audio files are missing and lists affected recordings
    • Added local file existence check and deletion support for recordings
  • Improvements

    • App updated to version 1.2.3
    • Improved audio recorder stability, lifecycle handling, and cleanup during start/pause/stop

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

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

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ 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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d75157fc-e112-4ba3-bbde-a00296a1d965

📥 Commits

Reviewing files that changed from the base of the PR and between a4ba622 and 4d9f77a.

📒 Files selected for processing (16)
  • src/mobile/App.tsx
  • src/mobile/android/app/build.gradle
  • src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/FileUploadModule.kt
  • src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/MainActivity.kt
  • src/mobile/ios/AssistantTranscripts.xcodeproj/project.pbxproj
  • src/mobile/ios/FileUploadModule.m
  • src/mobile/ios/FileUploadModule.swift
  • src/mobile/package.json
  • src/mobile/src/components/AudioRecorder.tsx
  • src/mobile/src/features/auth/api/useUser.tsx
  • src/mobile/src/i18n/locales/en.json
  • src/mobile/src/i18n/locales/fr.json
  • src/mobile/src/navigation/types.ts
  • src/mobile/src/services/authService.ts
  • src/mobile/src/services/storage.ts
  • src/mobile/src/utils/deleteLocalRecordingFile.ts
📝 Walkthrough

Walkthrough

This PR adds file cleanup for orphaned recordings, native file-operation methods on Android and iOS, a TypeScript wrapper for those methods, and refactors the AudioRecorder component from boolean-based UI state to a lifecycle-safe state machine that prevents stale async mutations after unmount or phase changes.

Changes

Recording File Cleanup and AudioRecorder Refactor

Layer / File(s) Summary
Version bumps across platforms
src/mobile/android/app/build.gradle, src/mobile/ios/AssistantTranscripts.xcodeproj/project.pbxproj, src/mobile/package.json
versionCode/versionName incremented to 14/1.2.3 on Android, CURRENT_PROJECT_VERSION/MARKETING_VERSION updated on iOS, and package version updated to 1.2.3.
Native file deletion and existence-check methods
src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/FileUploadModule.kt, src/mobile/ios/FileUploadModule.m, src/mobile/ios/FileUploadModule.swift
Android and iOS native modules expose deleteLocalFile and localFileExists methods that normalize file paths and report success/failure via promises; Android shareAudioFile now normalizes paths before constructing File.
TypeScript native wrappers
src/mobile/src/utils/deleteLocalRecordingFile.ts
Exports localRecordingFileExists and deleteLocalRecordingFile that validate the native module presence and delegate calls, throwing descriptive errors if unavailable.
Recording file cleanup after hydration and deletion
src/mobile/src/services/storage.ts, src/mobile/src/i18n/locales/en.json, src/mobile/src/i18n/locales/fr.json
After storage rehydration, a cleanup function checks persisted recordings' file existence, removes orphaned entries and writes a localized bullet list to missingFilesPending; deleteRecording attempts native file deletion before removing the recording; i18n keys added for missing-files alerts.
App-level missing-files alert and AuthCallback logout
src/mobile/App.tsx
Adds MissingFilesAlertHandler to show a localized alert when the app becomes active (and on mount) using missingFilesPending, clears the pending flag after showing; AuthCallback handling gains an early logout branch that clears auth state and resets navigation to Login.
Android MainActivity intent handling
src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/MainActivity.kt
Adds onNewIntent override that updates the stored intent with setIntent(intent) and includes commented debug/deep-link lines.
Auth logout flow order and navigation types
src/mobile/src/features/auth/api/useUser.tsx, src/mobile/src/navigation/types.ts, src/mobile/src/services/authService.ts
Reorders logout to clear cached user and await clearAuthState() before opening in-app browser; narrows finally to only queryClient.resetQueries(); adds optional logout?: string to AuthCallback route params; uses void to ignore queryClient promise in clearAuthState.
AudioRecorder state machine, lifecycle refs, and notification wrapper
src/mobile/src/components/AudioRecorder.tsx (multiple ranges)
Replaces boolean UI flags with RecorderPhase state machine and lifecycle tokens to avoid stale async updates; adds Android-only null-safe notification wrapper and initializes cue buffer promise cache.
Cancellable cue-sound playback with promise caching
src/mobile/src/components/AudioRecorder.tsx (multiple ranges)
Adds cancellable wait(durationMs, shouldContinue), caches decode promises per cue, and refactors playCueSound to accept a shouldContinue predicate and use cancellable wait.
Lifecycle-safe clearRecording, mount/unmount, and action queueing
src/mobile/src/components/AudioRecorder.tsx (multiple ranges)
Implements queued clearRecording stop+cleanup flow that increments lifecycle, optionally deletes the file, disables recorder output, clears intervals/haptics/notifications and deactivates audio session; mount/unmount requests permissions and ensures cleanup on unmount.
Updated start/pause/resume/stop flows with queued actions
src/mobile/src/components/AudioRecorder.tsx (multiple ranges)
Start/pause/resume/stop use serialized action queue and lifecycle tokens; start requests permissions/activates session, plays cancellable start cue and shows notifications only if lifecycle is current; stop waits a frame, stops recorder, optionally plays end cue, deactivates session, creates recording, and updates phase; max-duration alert shown only when stop succeeded and component is mounted; UI press handlers void async promises.

Sequence Diagram(s)

sequenceDiagram
  participant TSUtil as deleteLocalRecordingFile (TypeScript)
  participant Native as FileUploadModule (Native)
  participant FS as File System
  TSUtil->>Native: call deleteLocalFile(filePath)
  Native->>FS: normalizePath / unlink
  FS-->>Native: success / error
  Native-->>TSUtil: resolve(null) / reject(DELETE_ERROR)
  TSUtil->>Native: call localFileExists(filePath)
  Native->>FS: normalizePath / stat
  FS-->>Native: exists: boolean
  Native-->>TSUtil: resolve(boolean)
Loading
sequenceDiagram
  participant Store as Recordings Store
  participant Hydration as rehydrate()
  participant Cleanup as removeMissingLocalRecordingsAfterHydration
  participant FileCheck as localFileExists()
  participant Alert as MissingFilesAlertHandler / Alert
  Hydration->>Store: restore persisted recordings
  Hydration->>Cleanup: invoke after rehydrate
  Cleanup->>FileCheck: check each recording file
  FileCheck-->>Cleanup: exists: boolean[]
  Cleanup->>Store: remove recordings with missing files
  Cleanup->>Alert: set missingFilesPending (localized list)
  Alert->>User: show alert on active app / mount
  User-->>Alert: dismiss
Loading
stateDiagram-v2
  [*] --> idle
  idle --> loading: start requested
  loading --> recording: permissions granted & session active
  recording --> paused: pause requested
  paused --> recording: resume requested
  recording --> stopping: stop requested
  paused --> stopping: stop requested
  stopping --> idle: stop completed & cleanup done
  loading --> idle: lifecycle invalidated or permissions denied
  recording --> idle: lifecycle invalidated
  paused --> idle: lifecycle invalidated
Loading
sequenceDiagram
  participant UI as UI Press
  participant Queue as enqueueRecorderAction
  participant Lifecycle as beginLifecycleAction
  participant Recorder as Recorder API
  participant Storage as addRecording()
  UI->>Queue: onStartRecord pressed
  Queue->>Lifecycle: begin action with token
  Lifecycle->>Recorder: requestAudioSession, startRecording
  Recorder-->>Lifecycle: success
  Lifecycle->>Lifecycle: setRecorderPhaseIfCurrent(recording)
  UI->>Queue: onStopRecord pressed
  Queue->>Lifecycle: begin action with token
  Lifecycle->>Recorder: stopRecording
  Recorder-->>Lifecycle: file path
  Lifecycle->>Storage: addRecording(title, filePath)
  Storage-->>Lifecycle: recording id
  Lifecycle->>Lifecycle: setRecorderPhaseIfCurrent(idle)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • suitenumerique/dictaphone#87: Modifies AudioRecorder.tsx cue-sound and notification handling, which the main PR refactors as part of its lifecycle-safety improvements.
  • suitenumerique/dictaphone#85: Updates shareAudioFile path normalization in FileUploadModule.kt, which this PR extends with new file deletion and existence-check methods.
  • suitenumerique/dictaphone#86: Touches AudioRecorder.tsx cue/start-end sound handling, overlapping with the cue-sound refactor in this PR.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix/mobiles' is vague and generic, using a branch name pattern rather than a descriptive summary of the changes. Replace with a more descriptive title that clearly summarizes the main fix, such as 'Add local recording file cleanup on app startup' or 'Fix uncleaned cache files in mobile recordings'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


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 and usage tips.

@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: 1

🤖 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 `@src/mobile/src/services/storage.ts`:
- Around line 70-73: The Alert.alert call inside
removeMissingLocalRecordingsAfterHydration (triggered from
useRecordingsStore.persist.onRehydrateStorage in storage.ts) must be removed and
replaced with a deferred notification mechanism: have
removeMissingLocalRecordingsAfterHydration record the removedFileList into a
persisted/in-memory flag or emit an app-level event (e.g., via an EventEmitter
or store flag like recordings.missingFilesPending) instead of calling
Alert.alert directly; then update the UI layer (a mounted screen or root
navigator) to listen for that flag/event and show Alert.alert only when the app
is foregrounded/active (AppState === 'active') or when a screen gains focus.
Ensure the unique symbols referenced are
removeMissingLocalRecordingsAfterHydration,
useRecordingsStore.persist.onRehydrateStorage, and the Alert.alert call in
storage.ts so reviewers can locate and remove the immediate alert invocation.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 7eebd6e8-5956-428c-a93d-7ed67d9d2457

📥 Commits

Reviewing files that changed from the base of the PR and between 3e22d5b and cb62874.

📒 Files selected for processing (11)
  • src/mobile/android/app/build.gradle
  • src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/FileUploadModule.kt
  • src/mobile/ios/AssistantTranscripts.xcodeproj/project.pbxproj
  • src/mobile/ios/FileUploadModule.m
  • src/mobile/ios/FileUploadModule.swift
  • src/mobile/package.json
  • src/mobile/src/components/AudioRecorder.tsx
  • src/mobile/src/i18n/locales/en.json
  • src/mobile/src/i18n/locales/fr.json
  • src/mobile/src/services/storage.ts
  • src/mobile/src/utils/deleteLocalRecordingFile.ts

Comment thread src/mobile/src/services/storage.ts Outdated
FloChehab added 2 commits June 5, 2026 16:20
The OS can clear the cache sometimes.
This change makes it that now we store in the document
directory and not the cache directory.
For some reason I forgot to code the delete audio files part.
Sorry.

@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

🤖 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
`@src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/FileUploadModule.kt`:
- Around line 135-148: In deleteLocalFile, avoid deleting directories by
checking that the resolved File is a regular file before calling delete(): after
creating val file = File(normalizePath(filePath)) and verifying file.exists(),
add a guard using file.isFile (or !file.isDirectory) and if it’s not a file call
promise.reject("INVALID_TARGET", "Target is not a file") (or similar) and
return; only call file.delete() for actual files to prevent removing directories
outside the API contract.

In
`@src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/MainActivity.kt`:
- Around line 30-32: Remove the commented deep-link debug lines or gate them
behind a debug flag: either delete the three commented Log/Uri lines in onCreate
and the corresponding ones in onNewIntent, or replace them with conditional
logging that runs only when BuildConfig.DEBUG is true; target the commented
blocks associated with onCreate and onNewIntent (the lines that reference
"DEEPLINK", Uri, and intent) so the deep-link debug output is either removed or
enabled only in debug builds.

In `@src/mobile/App.tsx`:
- Around line 43-46: The logout flow currently calls clearAuthState() without
awaiting it before calling resetNavigationHistory('Login'), so failures can
leave secrets persisted while the app navigates; update the handler that checks
params.logout to await clearAuthState() and only call
resetNavigationHistory('Login') after it resolves, wrapping the await in a
try/catch to handle and log any rejection (and optionally surface an error UI)
so failures aren't ignored; modify the block containing clearAuthState(),
resetNavigationHistory, and the params.logout check to implement this change.

In `@src/mobile/ios/FileUploadModule.swift`:
- Around line 116-126: The code currently allows deleting directories because it
only checks file existence; update both deletion sites in FileUploadModule.swift
to enforce "regular-file only" by using
FileManager.fileExists(atPath:isDirectory:) (or FileManager.attributesOfItem) to
ensure isDirectory is false before calling fileManager.removeItem(atPath:). If
the path is a directory, call rejecter with a suitable "DELETE_ERROR" message
(do not perform removeItem) and only call resolver(nil) after a successful
removal of a regular file; reference the existing symbols fileManager,
normalizedPath, resolver, and rejecter when making the changes.

In `@src/mobile/src/components/AudioRecorder.tsx`:
- Line 678: The callbacks handleStartRecording, onPauseRecord, and
onResumeRecord reference showRecordingNotification but do not include it in
their useCallback dependency arrays; add showRecordingNotification to each
callback's dependency array to silence lint warnings and avoid stale closures
(update the dependency lists for handleStartRecording, onPauseRecord, and
onResumeRecord to include showRecordingNotification alongside their existing
deps).

In `@src/mobile/src/features/auth/api/useUser.tsx`:
- Around line 80-83: The local auth cleanup call clearAuthState() is executed
before the guarded remote logout try block and can short-circuit the remote
logout and the finally cleanup if it rejects; move await clearAuthState() (and
any other pre-logout local cleanup such as clearCachedUser() if applicable)
inside the try that contains the remote logout so it executes under the same
error guard, and make the same change for the duplicated occurrence around the
final cleanup (the calls near clearCachedUser() / clearAuthState() at the end)
so both places run inside their respective try blocks and allow the finally
block to always execute.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 1601b126-697b-4347-a849-e633507b468c

📥 Commits

Reviewing files that changed from the base of the PR and between cb62874 and a4ba622.

📒 Files selected for processing (16)
  • src/mobile/App.tsx
  • src/mobile/android/app/build.gradle
  • src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/FileUploadModule.kt
  • src/mobile/android/app/src/main/java/fr/gouv/assistant_transcripts/MainActivity.kt
  • src/mobile/ios/AssistantTranscripts.xcodeproj/project.pbxproj
  • src/mobile/ios/FileUploadModule.m
  • src/mobile/ios/FileUploadModule.swift
  • src/mobile/package.json
  • src/mobile/src/components/AudioRecorder.tsx
  • src/mobile/src/features/auth/api/useUser.tsx
  • src/mobile/src/i18n/locales/en.json
  • src/mobile/src/i18n/locales/fr.json
  • src/mobile/src/navigation/types.ts
  • src/mobile/src/services/authService.ts
  • src/mobile/src/services/storage.ts
  • src/mobile/src/utils/deleteLocalRecordingFile.ts

Comment thread src/mobile/App.tsx
Comment thread src/mobile/ios/FileUploadModule.swift
Comment thread src/mobile/src/components/AudioRecorder.tsx
Comment thread src/mobile/src/features/auth/api/useUser.tsx
FloChehab added 7 commits June 5, 2026 18:14
This avoids some buggy behavior where the auth is not fully cleared
When coming back from the in app browser.
For later if we implement it.
On android, sometimes coming back from
the in app browser login process wouldn't do anything
because the intent was lost before the react native
part of the app would start.
This is an attempt to fix it.
@FloChehab FloChehab merged commit 4d9f77a into main Jun 5, 2026
11 checks passed
@FloChehab FloChehab deleted the fix/mobiles branch June 5, 2026 16:16
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