Skip to content

fix: route photo-backup into configured folders and skip overlapping sources#54

Open
grigorii-horos wants to merge 2 commits into
siddarthkay:masterfrom
grigorii-horos:pr/photo-backup-overlap-fix
Open

fix: route photo-backup into configured folders and skip overlapping sources#54
grigorii-horos wants to merge 2 commits into
siddarthkay:masterfrom
grigorii-horos:pr/photo-backup-overlap-fix

Conversation

@grigorii-horos

Copy link
Copy Markdown

What

Two related photo-backup correctness fixes.

Backend — write into the right folders

Photo backup copies through the Go bridge's CopyFile, which sandboxes the destination to known roots. Two folder kinds were unreachable:

  • SAF folders (Path is an opaque content:// tree URI): resolve the destination to its owning SAF tree + relative path and write via the SAF bridge.
  • All-Files-Access folders outside foldersRoot (e.g. DCIM), not registered as externalRoots on Android: treat every configured folder's real filesystem path as an extra sandbox root.

safTreeForPath is unexported so gomobile doesn't try to bind its three-value return.

JS — skip assets already inside the backup folder

When the target overlaps the media source (user picks DCIM, Pictures, or a nested album like DCIM/Archive), assets were copied back in, creating duplicates. The old guard compared raw strings and missed nested assets when the two sides disagreed on surface form — /sdcard vs /storage/emulated/0 (symlink), trailing/doubled slashes.

Now both paths are canonicalized before comparing (resolve /sdcard and /storage/self/primary aliases, collapse slashes), content:// SAF tree URIs are decoded to their real path, and any asset whose source lives inside the target is skipped. A second check runs against the actual candidate URI right before the copy, since expo may fall back from localUri to asset.uri.

Test

  • go build ./... (backend) ✅
  • tsc --noEmit (mobile-app) ✅
  • Manually: picking DCIM and a nested DCIM/Archive no longer re-copies existing media.

🤖 Generated with Claude Code

grigorii-horos and others added 2 commits June 19, 2026 14:40
Photo backup writes through the Go bridge's CopyFile, which sandboxes the
destination to known roots. Two folder kinds were unreachable:

  - SAF folders, whose Path is an opaque content:// tree URI rather than a
    POSIX path: resolve the destination to its owning SAF tree and the
    path relative to it, then write via the SAF bridge.
  - All-Files-Access folders that live outside foldersRoot (e.g. DCIM) and
    aren't registered as externalRoots on Android: treat every configured
    folder's real filesystem path as an extra sandbox root.

safTreeForPath is unexported so gomobile doesn't try to bind its
three-value return (bind allows at most one value plus error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the backup target overlaps the media source (the user picks DCIM,
Pictures, or a nested album like DCIM/Archive), gallery assets were
copied back into it, creating duplicates. The previous guard compared raw
strings and missed nested assets whenever the two sides disagreed on
surface form — /sdcard vs /storage/emulated/0 (symlink), trailing or
doubled slashes.

Canonicalize both paths before comparing (resolve the /sdcard and
/storage/self/primary aliases, collapse slashes), decode content:// SAF
tree URIs to their real path, and skip any asset whose source lives inside
the target. A second check runs against the actual candidate URI right
before the copy, since expo may fall back from localUri to asset.uri.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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