fix: route photo-backup into configured folders and skip overlapping sources#54
Open
grigorii-horos wants to merge 2 commits into
Open
fix: route photo-backup into configured folders and skip overlapping sources#54grigorii-horos wants to merge 2 commits into
grigorii-horos wants to merge 2 commits into
Conversation
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>
This was referenced Jun 19, 2026
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.
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:content://tree URI): resolve the destination to its owning SAF tree + relative path and write via the SAF bridge.foldersRoot(e.g.DCIM), not registered asexternalRootson Android: treat every configured folder's real filesystem path as an extra sandbox root.safTreeForPathis 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 likeDCIM/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 —/sdcardvs/storage/emulated/0(symlink), trailing/doubled slashes.Now both paths are canonicalized before comparing (resolve
/sdcardand/storage/self/primaryaliases, 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 fromlocalUritoasset.uri.Test
go build ./...(backend) ✅tsc --noEmit(mobile-app) ✅DCIMand a nestedDCIM/Archiveno longer re-copies existing media.🤖 Generated with Claude Code