juice_media 0.6.0
juice_media: ^0.6.0 copied to clipboard
Media acquisition (camera/gallery) and per-item upload state as a Juice bloc, behind swappable seams.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.6.0 - 2026-09-16 #
Changed — every builder now declares its EventConcurrency mode #
- 0.3.0 gave
AcquireMediaEventdroppableand left the other twelve builders bare (silentlyconcurrent). Now explicit:InitializeMediaEvent→droppable; every item / upload / permission mutation →sequential(AddRemoteItems,AddLocalItems,RemoveItem,ClearItems,SetPermissionStatus,UploadItem,UploadAll,CancelUpload,UploadProgress,UploadCompleted,UploadFailed). Every one of those use cases is atomic today (noawait), so this is declarative: it states the intent AGENTS §4 asks for and protects each against a future await. No observable behavior change; no new tests, because there is nothing to gate.
0.5.0 - 2026-06-24 #
From dogfooding (Glean library ingest): the same library photo can arrive by more than one path — a manual pick and a bulk library scan. Deduping them needs the photo's stable identity, which a copied file path can't provide.
Added #
MediaItem.assetId— the optional source library asset id (PHAsset.localIdentifier/MediaStoreid), the stable identity of the underlying photo. AMediaSourcethat has it (e.g. a photo_manager-backed gallery source) sets it; the defaultimage_pickersource leaves it null (image_picker returns a copied file with no asset identity). Carried throughwithSessionandtoString. Consumers persist it to converge identity across ingestion paths.
Additive and backward-compatible.
0.4.0 - 2026-06-10 #
Both features come straight from dogfooding (Glean DOGFOOD.md F1/F2).
Added #
- Pick sessions (F1) —
MediaRequest.session/pickFromGallery(session:)/captureFromCamera(session:)stamp acquired items with a session tag (MediaItem.session);MediaState.inSession(tag)filters. Lets concurrent contexts (e.g. capture drafts) partition one bloc's items — previously apps had to snapshot-and-diff item ids. - Local items (F2) —
MediaItem.local(path: …)+addLocalItems()/AddLocalItemsEvent: inject items rebuilt from persisted paths (e.g. after an app restart) so they can be uploaded normally. Fails loud on remote-origin or contentless items. Mirror of 0.2'saddRemoteItems. MediaItem.withSession(tag)copy helper.
Additive and backward-compatible.
0.3.0 - 2026-06-09 #
Changed #
- Requires
juice ^1.5.0. AcquireMediaEventis registeredEventConcurrency.droppable: a pick fired while one is in flight is dropped at dispatch, replacing the manualstate.pickingentry guard. Behavior unchanged.
0.2.1 - 2026-05-28 #
Fixed #
AcquireMediaUseCasenow no-ops if a pick is already in flight (thepickingguard was decorative — a rapid double-tap could launch two pickers).- A late
UploadProgressEventarriving after cancel/complete no longer revives the progress bar (guards onstatus == uploading).
0.2.0 - 2026-05-28 #
Added #
- First-class remote items — items that arrive already hosted (e.g. existing
images from your backend), for mixed local/remote galleries.
MediaItem.uri+MediaItem.remote(...)constructor +isRemote.MediaConfig.initialItemsto seed remote items at init.addRemoteItems(...)to add them at runtime.- Remote items are seeded as a
completedupload (UploadState.remote), so they render, count inallUploaded, and are skipped byuploadAlluniformly.
This release is additive and backward-compatible with 0.1.0.
0.1.0 - 2026-05-28 #
Added #
- Initial release.
MediaBloc— acquire images/video (camera/gallery) and track per-item upload progress, with selective rebuilds: a widget bound toMediaGroups.item(id)rebuilds only when that item (or its upload) changes.MediaSource— acquisition seam; defaultImagePickerMediaSource(image_picker, camera + gallery, image + video).MediaUploader/MediaUpload— upload seam, handle-based (progress stream + result future +cancel()). Injected; no universal default. The bloc owns progress state, never the transport.- Per-item upload lifecycle —
queued → uploading → completed / failed / cancelled, with progress, remote URL, cancellation.upload/uploadAll/cancelUpload. - Fail-loud — uploading with no uploader configured marks the item failed
and surfaces
lastError(never a silent no-op). - Permissions —
setPermissionStatusentry point (wire camera/photos fromjuice_permissionsviaPermissionBinding); nojuice_permissionsdep. - Rebuild groups —
media:item:<id>,media:any,media:picking,media:permission,media:error.
Not yet included #
- Arbitrary file picking (PDF, etc.) — a file-capable
MediaSourceis planned post-0.1.