feat(media-store): backends, phase 4 (dropbox, google drive, icloud)#559
Merged
ericgriffin merged 9 commits intoJul 11, 2026
Merged
Conversation
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
Phase 4 of the Media Store program (design spec, section 17): the three managed backends. Dropbox, Google Drive, and iCloud now implement the same
MediaObjectStoreinterface the S3 adapter shipped in Phase 1, so the upload pipeline, worker, queue, resolver, and cache from Phases 1-3 run unchanged over all four providers.Stacked PR: targets
worktree-media-store-phase3(#557), which targetsworktree-media-store-phase2(#556), which targetsworktree-s3-media-store(#550). Merge bottom-up: #550, then #556, then #557, then this.Changes
dropbox_media_object_store.dart): path-native keys under/submersion-media/<key>in the app folder; single-shot upload below the chunk threshold, upload sessions above it with{sessionId, offset}resume state;incorrect_offsetrestarts fresh; chunkedgetFileviaRangeon/files/download. New public session primitives + ranged download + recursivelistFolderonDropboxApiClient.google_drive_media_object_store.dart): raw REST over the provider's authenticated client; always-resumable upload sessions with aContent-Range: bytes */totalprobe to recover the confirmed offset after interruption; file NAME = full store key inside a single appDataFolder folder; chunks are 256 KiB multiples.icloud_media_object_store.dart+icloud_media_platform.dart): filesystem operations over the ubiquity container behind an injectableICloudMediaPlatform(native implementation + directory-backed test/dev implementation); small writes copy, large writes move; reads calldownloadIfNeededfor evict-from-local files. Resume is OS-managed (documented deviation: the OS transfer daemon owns partial-transfer state, soresumeStateJsonstays unused).MediaStoreAttachStaterecords which provider a device attached with; pre-phase-4 attachments without a stored type read back as S3, so existing S3 attachments migrate for free.mediaStoreRuntimeProviderbuilds the adapter for the attached provider via sharedbuildMediaObjectStore(); an unavailable provider (no Dropbox link, no silent Google session, iCloud off) yields a null runtime without clearing the attachment.MediaStoreService(connectDropbox/connectGoogleDrive/connectICloud): marker ensure/adopt, attach, synced descriptor row; managed providers never touch the S3 keychain entry; unavailable providers throwMediaStoreErrorKind.auth.SegmentedButton(S3 / Dropbox / Google Drive / iCloud, the latter only on Apple platforms) with hint + connect panels for managed providers; the existing S3 form shows only when S3 is selected. 5 new l10n keys across all 11 locales.Testing
FakeDropboxServer,FakeDriveServer(MockClient-based, exercising real auth headers, session endpoints, Range requests, and error shapes).media_object_store_contract.dartbehavioral suite; per-adapter tests cover large-object session paths, kill-and-resume by request counting (Dropbox appends resume mid-session; Drive probesbytes */totalthen resumes; S3 unchanged), stale-resume restart, and chunked ranged downloads.test/features/media_store/+test/core/services/media_store/+test/core/services/cloud_storage/(328),test/features/media/(566),test/features/backup/+test/core/data/+test/features/settings/(865).dart format .clean,flutter analyzeclean.Manual verification pending