@symbiote-native/media-library 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +55 -38
  2. package/package.json +15 -15
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # @symbiote-native/media-library
2
2
 
3
- A wrapper package for [SymbioteNative](../../README.md) that makes
4
- [`expo-media-library`](https://github.com/expo/expo/tree/main/packages/expo-media-library) usable
5
- from **every** adapter — React, Vue, Svelte, Solid, and Angular.
3
+ Read, save and organize the device's photos and videos: query recent assets, save a file to the
4
+ library, build albums, and react to library changes. One API for every
5
+ [SymbioteNative](../../README.md) adapter (React, Vue, Svelte, Solid and Angular).
6
6
 
7
- **Both of upstream's surfaces are ported, matching upstream's own layout.** The default export is
8
- the modern, shared-object API — `Query`/`Asset`/`Album` classes built on JSI shared objects
9
- (`expo-modules-core`'s `SharedObject`), upstream's own default entry as of SDK 57. The legacy,
10
- function-based API is available at the `./legacy` subpath, reachable in the real upstream package
11
- as `expo-media-library/legacy`. See [Legacy API (`/legacy`)](#legacy-api-legacy) below.
7
+ It wraps [`expo-media-library`](https://github.com/expo/expo/tree/main/packages/expo-media-library)
8
+ and ports both of upstream's surfaces, matching upstream's own layout:
12
9
 
13
- Reads, saves, and organizes the device's photo/video library — assets, albums, permissions
14
- (including the granular Android 13+ and limited-access iOS/Android 14+ pickers), and
15
- library-change events.
10
+ - **Default export:** the modern `Query` / `Asset` / `Album` classes, built on JSI shared objects
11
+ (`expo-modules-core`'s `SharedObject`). Upstream's default entry as of SDK 57.
12
+ - **`./legacy` subpath:** the function-based API, reachable upstream as `expo-media-library/legacy`.
13
+ See [Legacy API (`/legacy`)](#legacy-api-legacy) below.
14
+
15
+ Permissions include the granular Android 13+ ones and the limited-access picker (iOS, Android 14+).
16
16
 
17
17
  ## Install
18
18
 
@@ -29,34 +29,34 @@ npx @symbiote-native/cli add --media-library
29
29
  ```
30
30
 
31
31
  Either way: installs `@symbiote-native/media-library` and wires the native autolinking
32
- automatically — see [`@symbiote-native/cli`](../cli).
32
+ automatically - see [`@symbiote-native/cli`](../cli).
33
33
 
34
34
  <details>
35
- <summary>Manual install (no CLI — installing and wiring native autolinking by hand)</summary>
35
+ <summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
36
36
 
37
37
  ```bash
38
38
  npm install @symbiote-native/media-library
39
39
  ```
40
40
 
41
41
  `expo-media-library` and `expo-modules-core` come along as regular dependencies, pinned to exact
42
- versions — never install them yourself, and never add the `expo` meta-package to your project.
42
+ versions - never install them yourself, and never add the `expo` meta-package to your project.
43
43
 
44
44
  ## Required one-time step: native autolinking wiring
45
45
 
46
- Same one-time step as every other `expo-modules-core` package this project ships — see
46
+ Same one-time step as every other `expo-modules-core` package this project ships - see
47
47
  [`@symbiote-native/local-auth`'s README](../local-auth/README.md#required-one-time-step-native-autolinking-wiring)
48
48
  and the `symbiote-expo-native-module` project skill.
49
49
 
50
50
  `native-link.json` declares two iOS `Info.plist` usage-description keys
51
51
  (`NSPhotoLibraryUsageDescription`, `NSPhotoLibraryAddUsageDescription`) with generic default
52
- text — override either by setting the same key yourself before or after install
52
+ text - override either by setting the same key yourself before or after install
53
53
  (`@symbiote-native/expo-modules-link`'s patcher is additive-only). It also sets
54
- `android:requestLegacyExternalStorage="true"` on your app's `<application>` tag — required by
54
+ `android:requestLegacyExternalStorage="true"` on your app's `<application>` tag - required by
55
55
  upstream's own config plugin for scoped-storage compatibility on Android 10+.
56
56
 
57
57
  </details>
58
58
 
59
- **Android runtime permissions are NOT added automatically, by the CLI or otherwise** — add
59
+ **Android runtime permissions are NOT added automatically, by the CLI or otherwise** - add
60
60
  whichever of these your app actually needs to your own `AndroidManifest.xml`, the same opt-in
61
61
  shape [`@symbiote-native/location`](../location) uses for background location:
62
62
 
@@ -72,7 +72,7 @@ shape [`@symbiote-native/location`](../location) uses for background location:
72
72
  ```
73
73
 
74
74
  `READ_MEDIA_IMAGES`/`READ_MEDIA_VIDEO`/`READ_MEDIA_AUDIO` are the granular Android 13+
75
- permissions — pass the matching subset as `requestPermissionsAsync(false, [...])`'s second
75
+ permissions - pass the matching subset as `requestPermissionsAsync(false, [...])`'s second
76
76
  argument; omitting one here means Android silently refuses that grant regardless of what the app
77
77
  asks for at runtime.
78
78
 
@@ -80,17 +80,17 @@ asks for at runtime.
80
80
 
81
81
  ```
82
82
  src/next/ query.ts / asset.ts / album.ts (Query/Asset/Album shared-object classes) /
83
- native-module.ts / types.ts — the default, shared-object surface. See "API" below.
83
+ native-module.ts / types.ts - the default, shared-object surface. See "API" below.
84
84
  src/core/ media-library.ts (every legacy function + the three useXPermissions hooks),
85
85
  native-module.ts, types.ts. See "Legacy API" below.
86
- src/angular/ @symbiote-native/media-library/angular — export * from '../next'
86
+ src/angular/ @symbiote-native/media-library/angular - export * from '../next'
87
87
  ```
88
88
 
89
- `./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto `src/next/`
90
- — `Query`/`Asset`/`Album` carry no children/ref/render fields, so there is nothing to split per
89
+ `./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto `src/next/`:
90
+ `Query`/`Asset`/`Album` carry no children/ref/render fields, so there is nothing to split per
91
91
  framework. `./angular` stays a physical file/subpath since Angular ships through a separate
92
92
  `ngc`/AOT build (`build-ngc/`). `./legacy` is one subpath shared by every adapter, for the same
93
- reason — plain async functions have no framework-specific shape either.
93
+ reason - plain async functions have no framework-specific shape either.
94
94
 
95
95
  ## Use it
96
96
 
@@ -100,6 +100,7 @@ import {
100
100
  Asset,
101
101
  Album,
102
102
  AssetField,
103
+ MediaType,
103
104
  requestPermissionsAsync,
104
105
  } from '@symbiote-native/media-library';
105
106
 
@@ -109,21 +110,21 @@ if (granted) {
109
110
  const album = await Album.create('My Album', [asset]);
110
111
 
111
112
  const recentPhotos = await new Query()
112
- .eq(AssetField.MEDIA_TYPE, 'image' as never)
113
+ .eq(AssetField.MEDIA_TYPE, MediaType.IMAGE)
113
114
  .orderBy(AssetField.CREATION_TIME)
114
115
  .limit(20)
115
116
  .exe();
116
117
  }
117
118
  ```
118
119
 
119
- Identical import surface on every adapter — `@symbiote-native/media-library/react`, `/vue`,
120
+ Identical import surface on every adapter - `@symbiote-native/media-library/react`, `/vue`,
120
121
  `/svelte`, `/solid`, `/angular` all re-export the same classes.
121
122
 
122
123
  ## API
123
124
 
124
125
  ### `Query`
125
126
 
126
- Builder pattern — every filter/sort method returns the same instance for chaining.
127
+ Builder pattern - every filter/sort method returns the same instance for chaining.
127
128
 
128
129
  ```ts
129
130
  new Query()
@@ -195,19 +196,19 @@ upstream's default-entry types with this repo's `I`-prefix convention for export
195
196
 
196
197
  ### Errors
197
198
 
198
- No custom JS error-class hierarchy — every native exception surfaces as an ordinary thrown `Error`
199
+ No custom JS error-class hierarchy - every native exception surfaces as an ordinary thrown `Error`
199
200
  or rejected `Promise`, same as every other native module wrapper in this repo.
200
201
 
201
202
  | Trigger | When |
202
203
  | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
203
- | Calling an `Asset`/`Album` method on an ID that no longer exists on the device | Native `Exception` — "could not be found" |
204
- | `Asset.getMediaSubtypes()`/`.getLivePhotoVideoUri()`/`.getIsInCloud()`/`.getOrientation()` on Android | `UnavailabilityError` — thrown synchronously, iOS only |
205
- | `Album.removeAssets()` on Android | Native `Exception` — an Android asset belongs to one album; delete it or add it to another instead |
206
- | `getLocation()` on Android without `ACCESS_MEDIA_LOCATION` | Rejects — needs that runtime permission |
204
+ | Calling an `Asset`/`Album` method on an ID that no longer exists on the device | Native `Exception` - "could not be found" |
205
+ | `Asset.getMediaSubtypes()`/`.getLivePhotoVideoUri()`/`.getIsInCloud()`/`.getOrientation()` on Android | `UnavailabilityError` - thrown synchronously, iOS only |
206
+ | `Album.removeAssets()` on Android | Native `Exception` - an Android asset belongs to one album; delete it or add it to another instead |
207
+ | `getLocation()` on Android without `ACCESS_MEDIA_LOCATION` | Rejects - needs that runtime permission |
207
208
 
208
209
  ## Legacy API (`/legacy`)
209
210
 
210
- Upstream's original function-based surface — plain async functions over `expo-modules-core`
211
+ Upstream's original function-based surface - plain async functions over `expo-modules-core`
211
212
  instead of JSI shared objects.
212
213
 
213
214
  ```ts
@@ -258,15 +259,31 @@ MediaType, SortBy
258
259
  Plus every `IMediaLibrary*` type, ported from upstream's `legacy/MediaLibrary.ts` with this
259
260
  repo's `I`-prefix convention for exported types (`ts-js-best-practices`).
260
261
 
261
- ### Legacy notes
262
+ ## Common questions
263
+
264
+ - **`saveToLibraryAsync` missing or throws.** The root entry is the modern API: use
265
+ `Asset.create(uri)`, or import the old function from `/legacy`.
266
+ - **Save a downloaded photo.** Download to the cache with `@symbiote-native/file-system`, then
267
+ `Asset.create(localUri)` (optionally with an `Album`).
268
+ - **Android 13+: permission denied.** Add the granular manifest permissions and pass the matching
269
+ subset to `requestPermissionsAsync(false, ['photo'])`.
270
+ - **iOS `uri` is `ph://`.** A Photos identifier: use `asset.getInfo()` for a local file URI.
271
+ - **Wrong orientation on Android in `getAssetsAsync`.** Legacy: pass `resolveWithFullInfo: true`.
272
+ - **Limited access.** `presentPermissionsPicker()` lets the user share more.
273
+
274
+ Sources: [Expo docs: MediaLibrary (legacy)](https://docs.expo.dev/versions/latest/sdk/media-library-legacy/),
275
+ [Expo guide: migrate to the new media library API](https://docs.expo.dev/guides/sdk-libraries-migration/media-library/),
276
+ [expo/expo#50670](https://github.com/expo/expo/pull/50670).
277
+
278
+ ## Legacy API notes
262
279
 
263
- - **`sortBy`'s single-tuple form must be double-nested — an upstream quirk, ported verbatim.**
280
+ - **`sortBy`'s single-tuple form must be double-nested - an upstream quirk, ported verbatim.**
264
281
  `getAssetsAsync({ sortBy: [['creationTime', true]] })` sorts by one key ascending; the
265
282
  unnested `sortBy: ['creationTime', true]` is read as two independent (and here, invalid) sort
266
283
  keys, because upstream's own `arrayize()` helper passes any array through unchanged rather than
267
284
  distinguishing "one tuple" from "several keys". See `media-library.test.ts` and the
268
285
  `UPSTREAM-BUG` comment on `getAssetsAsync`.
269
- - **`getAssetContentUriAsync` is Android-only** — a plain `content://` URI, still useful even
286
+ - **`getAssetContentUriAsync` is Android-only** - a plain `content://` URI, still useful even
270
287
  though the modern `Asset`/`Query` API above ports the shared-object surface it used to bridge to.
271
288
  - **`getMomentsAsync` and `setAssetFavoriteAsync` are iOS-only**; `getAssetContentUriAsync`,
272
289
  `migrateAlbumIfNeededAsync`, and `albumNeedsMigrationAsync` are Android-only or Android-R+-only.
@@ -278,7 +295,7 @@ repo's `I`-prefix convention for exported types (`ts-js-best-practices`).
278
295
 
279
296
  ## Test it
280
297
 
281
- No Fabric/Descriptor angle at all — every function here is a pure async-function surface plus one
298
+ No Fabric/Descriptor angle at all - every function here is a pure async-function surface plus one
282
299
  native change-event listener, never a view or per-instance state. Tests inject a fake native-module
283
300
  object in place of the real `requireNativeModule` resolution and fire the wired listener directly
284
- (`src/core/media-library.test.ts`, `src/next/next.test.ts`) — no `installFabric()`, no ViewConfig.
301
+ (`src/core/media-library.test.ts`, `src/next/next.test.ts`) - no `installFabric()`, no ViewConfig.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/media-library",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "expo-media-library wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Matches upstream's own layout: the modern shared-object Query/Asset/Album API at the package root, and the legacy function-based API at the ./legacy subpath. Reads, saves and organizes the device's photo/video library (assets, albums, permissions, library-change events).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "expo-media-library": "57.0.4",
77
- "expo-modules-core": "57.0.5"
77
+ "expo-modules-core": "57.0.20"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "@angular/core": ">=20",
@@ -84,12 +84,12 @@
84
84
  "solid-js": ">=1.9.0",
85
85
  "svelte": ">=5.56.0",
86
86
  "vue": ">=3.5.0",
87
- "@symbiote-native/angular": "^3.1.2",
88
- "@symbiote-native/engine": "^1.3.1",
89
- "@symbiote-native/react": "^3.0.4",
90
- "@symbiote-native/solid": "^3.0.4",
91
- "@symbiote-native/svelte": "^3.0.4",
92
- "@symbiote-native/vue": "^3.0.4"
87
+ "@symbiote-native/angular": "^3.3.0",
88
+ "@symbiote-native/engine": "^1.6.0",
89
+ "@symbiote-native/react": "^3.3.0",
90
+ "@symbiote-native/solid": "^3.2.0",
91
+ "@symbiote-native/svelte": "^3.2.0",
92
+ "@symbiote-native/vue": "^3.3.0"
93
93
  },
94
94
  "peerDependenciesMeta": {
95
95
  "@symbiote-native/angular": {
@@ -137,13 +137,13 @@
137
137
  "solid-js": "^1.9.14",
138
138
  "svelte": "^5.56.0",
139
139
  "typescript": "~6.0.0",
140
- "@symbiote-native/angular": "3.1.2",
141
- "@symbiote-native/engine": "1.3.1",
142
- "@symbiote-native/react": "3.0.4",
143
- "@symbiote-native/solid": "3.0.4",
144
- "@symbiote-native/svelte": "3.0.4",
145
- "@symbiote-native/test-utils": "0.4.4",
146
- "@symbiote-native/vue": "3.0.4"
140
+ "@symbiote-native/angular": "3.3.0",
141
+ "@symbiote-native/engine": "1.6.0",
142
+ "@symbiote-native/react": "3.3.0",
143
+ "@symbiote-native/solid": "3.2.0",
144
+ "@symbiote-native/svelte": "3.2.0",
145
+ "@symbiote-native/test-utils": "0.4.7",
146
+ "@symbiote-native/vue": "3.3.0"
147
147
  },
148
148
  "scripts": {
149
149
  "typecheck": "tsc --build",