Skip to content

feat(mobile): add review mode for remote trash sync - #30275

Open
PeterOmbodi wants to merge 20 commits into
immich-app:refactor/auto-trash-syncfrom
PeterOmbodi:feature/trash-sync-review-v3
Open

feat(mobile): add review mode for remote trash sync#30275
PeterOmbodi wants to merge 20 commits into
immich-app:refactor/auto-trash-syncfrom
PeterOmbodi:feature/trash-sync-review-v3

Conversation

@PeterOmbodi

@PeterOmbodi PeterOmbodi commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds a Review mode for syncing remote trash changes to local assets in the mobile app.

It builds on the automatic trash and restore synchronization introduced in #29922 and reimplements the review flow previously developed in #23992, #27785, and #28280 on top of the new trash sync architecture.

Users can choose how remote trash changes are handled:

Off — ignore remote trash changes
Auto sync — automatically apply supported trash and restore changes locally where supported
Review — collect affected local assets for manual review before applying remote deletions

Android shows the full mode selector. iOS shows a single “Review remote deletions” toggle, since Auto sync is not available there.

Review mode gives users explicit control over whether a remote deletion should also be applied to media on their device.

Review flow

When Review mode is enabled and an asset is moved to trash or permanently deleted remotely:

  1. Immich finds matching local assets in albums currently selected for backup.
  2. A separate pending marker is recorded for each matching local asset.
  3. The affected assets are added to the review timeline.
  4. The user profile displays Review out-of-sync changes with the number of actionable local assets.
  5. The user can review individual assets or select multiple assets.
  6. The user chooses one of the following actions:
    • Keep on device
    • Move to trash
  7. Resolved assets disappear from the review timeline.

Each local asset is reviewed independently. Checksum is used to correlate remote deletion state with local content, while local asset ID identifies the review item and its decision.

Keep on device

Keeps the selected local assets unchanged and marks their pending review items as rejected.

The decision remains valid for those local asset IDs while the matching content stays deleted remotely. If the remote asset is restored, the decisions are cleared so a later deletion can be reviewed again.

Other local copies with the same checksum remain independent review items.

Move to trash

Applies the remote deletion to the selected actionable local assets.

Each asset is processed independently. Successfully handled assets are marked as approved and removed from the local asset database. Assets that the platform media API fails to process remain pending and can be retried.

Backup album selection

Only local assets in albums currently selected for backup are counted, displayed, and affected by review actions.

If an album is removed from backup selection, its unresolved assets disappear from the actionable review list. Re-selecting the album makes them available again while the underlying remote deletion is still active.

Backup selection affects actionability and does not remove the underlying pending marker or review decision.

Platform behaviour

On Android, the available modes are Off, Auto sync, and Review.

Review mode can be enabled without Media Management Access, but moving assets to trash may require additional system confirmation. Remote restores are applied automatically only when the permission is available.

On iOS, the available modes are Off and Review. Auto sync and local trash restore are not offered because iOS does not expose a manageable local trash state equivalent to Android MediaStore.

Review timeline

The review page uses the standard mobile timeline with review-specific behaviour:

  • each actionable local asset is displayed as a separate item
  • assets are grouped using their local creation date
  • both individual and multi-selection actions are supported
  • unrelated viewer actions are hidden
  • resolved items disappear as the underlying timeline updates
  • an empty-state message is shown when there is nothing left to review
Technical details

Configuration

The previous boolean trashSyncEnabled setting is replaced by TrashSyncConfig with a TrashSyncMode:

  • off
  • autoSync
  • review

Existing values are migrated as follows:

  • trashSyncEnabled: trueTrashSyncMode.autoSync
  • trashSyncEnabled: falseTrashSyncMode.off

The legacy setting key remains compatible and maps to Auto sync or Off.

Recording review candidates

Review mode uses the existing TrashSyncStatus.pending status for unresolved assets.

Auto sync and Review share the same pending backlog. Switching from Review to Auto processes unresolved candidates automatically, while switching from Auto to Review exposes unresolved candidates for a user decision.

The implementation adds two decision states:

  • reviewRejected
  • reviewApproved

Review candidates are recorded for:

  • soft-deleted remote assets still present in remoteAssetEntity
  • permanently deleted assets retained in serverDeletedChecksumEntity

Remote deletion state is matched to local content by checksum. A separate marker is then stored for every matching local asset ID in a backup-selected album.

An existing approved or rejected decision excludes only the same local asset ID. Other local copies with the same checksum remain independently reviewable.

Candidate discovery uses correlated queries rather than joining every matching remote row. This prevents duplicate local candidates when the same checksum exists in multiple server libraries.

Permanent deletions

A remote asset row may be removed before the review flow processes a permanent deletion.

serverDeletedChecksumEntity retains the checksum so matching local assets can still be discovered after the remote row is removed.

Timeline placement does not require retained remote metadata. Review items use the local asset creation date.

Review decisions

The review timeline, pending count, and review actions operate on local asset IDs.

Keep on device changes actionable pending rows for the selected local asset IDs to reviewRejected.

Move to trash:

  1. Validates that the selected local asset IDs still have pending markers and belong to backup-selected albums.
  2. Passes the actionable IDs to AssetMediaRepository.deleteAll.
  3. Marks every successfully handled ID as reviewApproved.
  4. Removes successfully processed rows from localAssetEntity.
  5. Leaves failed IDs pending for retry.

The platform result is handled per local asset. A partial success does not approve or remove assets that failed to move to trash.

Approved markers retain the checksum and local asset update time required by the existing restore reconciliation flow.

Cleanup and restore

Pending markers are removed when their saved checksum no longer matches the current local asset content.

Pending and rejected markers are removed when the corresponding remote content becomes active again.

Approved markers are preserved because they represent local work that may need to be reversed after a remote restore.

On supported Android devices, restored assets are marked as restored. After local media synchronization recreates the local asset row, restoreChecksums() restores the saved checksum when the local asset ID and update time still match, then removes the completed marker.

Backup selection is used as an actionability filter and does not by itself remove an unresolved marker or decision.

Timeline integration

The review page uses a dedicated TimelineOrigin.syncTrash.

The timeline query combines:

  • pending trash sync markers
  • local assets with the same asset IDs
  • current backup album selection

It returns one item per actionable local asset and groups items by the local asset creation date.

The profile count reports actionable pending local assets rather than distinct checksums.

Main changes

  • Adds Off, Auto sync, and Review modes
  • Replaces the previous trash-sync switch with a mode selector
  • Uses the same pending backlog for Auto sync and Review
  • Records review candidates for soft and permanent remote deletions
  • Stores one review marker per matching local asset
  • Adds reviewRejected and reviewApproved decision states
  • Adds a dedicated local-asset review timeline and pending count
  • Adds Keep on device and Move to trash actions
  • Applies review decisions by local asset ID
  • Handles partial platform results per local asset
  • Keeps local copies with the same checksum independently reviewable
  • Filters candidates and actions using current backup album selection
  • Preserves local metadata required for checksum restoration
  • Preserves supported Android restore behaviour
  • Hides unrelated actions in the review viewer
  • Adds configuration migration and automated test coverage

How Has This Been Tested?

Automated tests

  • dart analyze
  • flutter test test/services/action.service_test.dart
  • flutter test test/medium/repositories/trash_sync_repository_test.dart
  • flutter test test/medium/repositories/timeline_repository_test.dart
  • flutter test test/drift/main/migration_test.dart

Additional coverage includes:

  • configuration migration
  • switching between Auto sync and Review with unresolved candidates
  • soft and permanent deletion candidate recording
  • pending review count by local asset
  • review timeline queries and local-date grouping
  • identical checksums in multiple server libraries
  • Keep on device and Move to trash actions
  • independent local copies with the same checksum
  • partial platform deletion results per local asset
  • backup-selection filtering
  • approved marker checksum restoration
  • remote restore reconciliation
  • review page and viewer actions

Manual Android test

  1. Use an Android 12+ device.
  2. Select a local album for backup.
  3. Back up an asset from that album.
  4. Open Settings → Advanced → Sync remote deletions.
  5. Select Review.
  6. Grant Media Management Access when prompted.
  7. Move the asset to trash from the Immich web app.
  8. Run or wait for mobile synchronization.
  9. Open the user profile.
  10. Verify that Review out-of-sync changes appears with the correct local asset count.
  11. Open the review timeline.
  12. Choose Keep on device and verify that the local asset remains and the item disappears from the review timeline.
  13. Repeat the flow with another asset.
  14. Choose Move to trash and verify that the local asset is moved to the device trash and disappears from the review timeline.
  15. Restore the asset remotely.
  16. Run synchronization and verify that it is restored locally when Media Management Access is available.

Manual Android test without Media Management Access

  1. Select Review and deny Media Management Access.
  2. Verify that Review mode remains enabled.
  3. Create a remote trash change.
  4. Verify that the local asset appears in the review timeline.
  5. Choose Move to trash.
  6. Verify that the platform confirmation flow is shown.
  7. Verify that successfully processed assets disappear from the timeline.
  8. Verify that remote restore is not applied automatically without the permission.

Manual iOS test

  1. Select a local album for backup.
  2. Back up an asset from that album.
  3. Open Settings → Advanced → Sync remote deletions.
  4. Verify that only Off and Review are available.
  5. Select Review.
  6. Move the asset to trash or permanently delete it from the Immich web app.
  7. Run or wait for mobile synchronization.
  8. Verify that the matching local asset appears in the review timeline.
  9. Test both Keep on device and Move to trash.
  10. Verify that the selected action is applied only to the selected local asset and that the item disappears from the review timeline.

Backup-selection test

  1. Create a pending review item.
  2. Remove the asset's album from backup selection.
  3. Verify that the item is no longer counted or displayed.
  4. Re-select the album.
  5. Verify that the unresolved item becomes available again.

Multiple-copy test

  1. Create multiple local copies with the same checksum in backup-selected albums.
  2. Create the matching remote trash event.
  3. Verify that every local copy appears as a separate review item and contributes to the pending count.
  4. Apply different review decisions to individual copies.
  5. Verify that each decision affects only the selected local asset.
  6. Simulate a partial platform deletion result.
  7. Verify that successfully handled assets disappear while failed assets remain pending.

Mode-switch test

  1. Create an unresolved candidate in Review mode.
  2. Switch to Auto sync.
  3. Verify that the same pending candidate is processed automatically when platform requirements are satisfied.
  4. Create or leave an unresolved candidate in Auto sync.
  5. Switch to Review.
  6. Verify that the candidate becomes visible in the review timeline without requiring a status conversion.
Screenshots
Review remote deletions setting - Android Review remote deletions setting - iOS
image tg_image_4181472176
Out-of-sync changes - Dark theme Out-of-sync changes - Light theme
image image
Review timeline Group selection
image image

Move-to-trash confirmation

Android with MANAGE_MEDIA permission Android without MANAGE_MEDIA permission iOS

Review asset viewer

Checklist

  • I have carefully read CONTRIBUTING.md
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if applicable
  • I have no unrelated changes in the PR
  • I have confirmed that any new dependencies are strictly necessary
  • I have written tests for new code (if applicable)
  • I have followed naming conventions/patterns in the surrounding code
  • All code in src/services/ uses repository implementations for database calls, filesystem operations, etc.
  • All code in src/repositories/ is basic/simple and does not contain Immich-specific business logic.

Please describe to which degree, if any, an LLM was used in creating this pull request.

An LLM was used to review changes and draft this PR description.

@PeterOmbodi
PeterOmbodi marked this pull request as draft July 27, 2026 13:40
@shenlong-tanwen
shenlong-tanwen changed the base branch from main to refactor/auto-trash-sync July 27, 2026 15:44
@immich-push-o-matic immich-push-o-matic Bot added documentation Improvements or additions to documentation 🖥️web 🗄️server cli Tasks related to the Immich CLI labels Jul 27, 2026
Comment thread .github/workflows/prepare-release.yml Fixed
@github-actions

Copy link
Copy Markdown
Contributor

📖 Documentation deployed to docs.pr-30275.preview.immich.app

@PeterOmbodi
PeterOmbodi force-pushed the feature/trash-sync-review-v3 branch from ad79cdc to 58ab96a Compare July 28, 2026 08:12
@shenlong-tanwen
shenlong-tanwen force-pushed the refactor/auto-trash-sync branch from aa6e6f4 to 2fa33ad Compare July 28, 2026 10:41
Peter Ombodi added 6 commits July 28, 2026 15:10
Adds review mode for remote trash changes on top of the auto trash sync flow:
- add trash sync mode config and advanced settings selector
- record pending review markers for soft and hard remote deletions
- add trash review timeline, route, bottom bar, and viewer actions
- resolve review decisions by checksum, including duplicate local copies
- group review timeline by remote/server timeline date
- add i18n strings and coverage for settings, providers, actions, timeline, and repositories
@PeterOmbodi
PeterOmbodi force-pushed the feature/trash-sync-review-v3 branch from 58ab96a to d2ddc12 Compare July 28, 2026 12:17
Peter Ombodi added 5 commits July 28, 2026 18:49
- apply review decisions and partial results per local asset ID
- build the review timeline and counts from local asset markers
- simplify Drift queries and review status updates
- group and count review items by local asset
- remove the unused server deletion timeline timestamp
- add coverage for mode switches, duplicates, partial failures, and restore
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been automatically closed as the description doesn't follow our template. After you edit it to match the template, the PR will automatically be reopened.

@PeterOmbodi
PeterOmbodi marked this pull request as ready for review August 11, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-closed:template changelog:feature cli Tasks related to the Immich CLI documentation Improvements or additions to documentation 📱mobile 🗄️server 🖥️web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants