@symbiote-native/media-library 0.1.1 → 0.1.3
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.
- package/README.md +55 -38
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# @symbiote-native/media-library
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
14
|
-
(
|
|
15
|
-
|
|
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
|
|
32
|
+
automatically - see [`@symbiote-native/cli`](../cli).
|
|
33
33
|
|
|
34
34
|
<details>
|
|
35
|
-
<summary>Manual install (no CLI
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
204
|
-
| `Asset.getMediaSubtypes()`/`.getLivePhotoVideoUri()`/`.getIsInCloud()`/`.getOrientation()` on Android | `UnavailabilityError`
|
|
205
|
-
| `Album.removeAssets()` on Android | Native `Exception`
|
|
206
|
-
| `getLocation()` on Android without `ACCESS_MEDIA_LOCATION` | Rejects
|
|
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
|
|
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
|
-
|
|
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
|
|
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**
|
|
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
|
|
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`)
|
|
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.
|
|
3
|
+
"version": "0.1.3",
|
|
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.
|
|
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.
|
|
88
|
-
"@symbiote-native/engine": "^1.
|
|
89
|
-
"@symbiote-native/react": "^3.0
|
|
90
|
-
"@symbiote-native/solid": "^3.0
|
|
91
|
-
"@symbiote-native/svelte": "^3.0
|
|
92
|
-
"@symbiote-native/vue": "^3.0
|
|
87
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
88
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
89
|
+
"@symbiote-native/react": "^3.2.0",
|
|
90
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
91
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
92
|
+
"@symbiote-native/vue": "^3.2.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.
|
|
141
|
-
"@symbiote-native/engine": "1.
|
|
142
|
-
"@symbiote-native/react": "3.0
|
|
143
|
-
"@symbiote-native/solid": "3.0
|
|
144
|
-
"@symbiote-native/svelte": "3.0
|
|
145
|
-
"@symbiote-native/test-utils": "0.4.
|
|
146
|
-
"@symbiote-native/vue": "3.0
|
|
140
|
+
"@symbiote-native/angular": "3.2.0",
|
|
141
|
+
"@symbiote-native/engine": "1.5.0",
|
|
142
|
+
"@symbiote-native/react": "3.2.0",
|
|
143
|
+
"@symbiote-native/solid": "3.1.0",
|
|
144
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
145
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
146
|
+
"@symbiote-native/vue": "3.2.0"
|
|
147
147
|
},
|
|
148
148
|
"scripts": {
|
|
149
149
|
"typecheck": "tsc --build",
|