@symbiote-native/image-picker 0.1.0

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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/build/angular/index.d.ts +3 -0
  4. package/build/angular/index.js +3 -0
  5. package/build/angular/services/camera-permissions.service/index.d.ts +5 -0
  6. package/build/angular/services/camera-permissions.service/index.js +60 -0
  7. package/build/angular/services/media-library-permissions.service/index.d.ts +5 -0
  8. package/build/angular/services/media-library-permissions.service/index.js +60 -0
  9. package/build/core/image-picker.d.ts +9 -0
  10. package/build/core/image-picker.js +52 -0
  11. package/build/core/index.d.ts +3 -0
  12. package/build/core/index.js +3 -0
  13. package/build/core/media-library-permission-api.d.ts +11 -0
  14. package/build/core/media-library-permission-api.js +9 -0
  15. package/build/core/native-module.d.ts +13 -0
  16. package/build/core/native-module.js +3 -0
  17. package/build/core/types.d.ts +127 -0
  18. package/build/core/types.js +54 -0
  19. package/build/core/utils.d.ts +4 -0
  20. package/build/core/utils.js +24 -0
  21. package/build/react/index.d.ts +3 -0
  22. package/build/react/index.js +3 -0
  23. package/build/react/use-camera-permissions.d.ts +8 -0
  24. package/build/react/use-camera-permissions.js +10 -0
  25. package/build/react/use-media-library-permissions.d.ts +10 -0
  26. package/build/react/use-media-library-permissions.js +7 -0
  27. package/build/solid/index.d.ts +3 -0
  28. package/build/solid/index.js +3 -0
  29. package/build/solid/use-camera-permissions.d.ts +9 -0
  30. package/build/solid/use-camera-permissions.js +10 -0
  31. package/build/solid/use-media-library-permissions.d.ts +11 -0
  32. package/build/solid/use-media-library-permissions.js +7 -0
  33. package/build/svelte/index.d.ts +3 -0
  34. package/build/svelte/index.js +3 -0
  35. package/build/svelte/use-camera-permissions.svelte.d.ts +8 -0
  36. package/build/svelte/use-camera-permissions.svelte.js +10 -0
  37. package/build/svelte/use-media-library-permissions.svelte.d.ts +10 -0
  38. package/build/svelte/use-media-library-permissions.svelte.js +7 -0
  39. package/build/vue/index.d.ts +3 -0
  40. package/build/vue/index.js +3 -0
  41. package/build/vue/use-camera-permissions.d.ts +9 -0
  42. package/build/vue/use-camera-permissions.js +10 -0
  43. package/build/vue/use-media-library-permissions.d.ts +11 -0
  44. package/build/vue/use-media-library-permissions.js +7 -0
  45. package/build-ngc/angular/index.d.ts +3 -0
  46. package/build-ngc/angular/index.js +4 -0
  47. package/build-ngc/angular/index.js.map +1 -0
  48. package/build-ngc/angular/services/camera-permissions.service/index.d.ts +8 -0
  49. package/build-ngc/angular/services/camera-permissions.service/index.js +17 -0
  50. package/build-ngc/angular/services/camera-permissions.service/index.js.map +1 -0
  51. package/build-ngc/angular/services/media-library-permissions.service/index.d.ts +8 -0
  52. package/build-ngc/angular/services/media-library-permissions.service/index.js +17 -0
  53. package/build-ngc/angular/services/media-library-permissions.service/index.js.map +1 -0
  54. package/build-ngc/core/image-picker.d.ts +9 -0
  55. package/build-ngc/core/image-picker.js +53 -0
  56. package/build-ngc/core/image-picker.js.map +1 -0
  57. package/build-ngc/core/index.d.ts +3 -0
  58. package/build-ngc/core/index.js +4 -0
  59. package/build-ngc/core/index.js.map +1 -0
  60. package/build-ngc/core/media-library-permission-api.d.ts +11 -0
  61. package/build-ngc/core/media-library-permission-api.js +10 -0
  62. package/build-ngc/core/media-library-permission-api.js.map +1 -0
  63. package/build-ngc/core/native-module.d.ts +13 -0
  64. package/build-ngc/core/native-module.js +4 -0
  65. package/build-ngc/core/native-module.js.map +1 -0
  66. package/build-ngc/core/types.d.ts +127 -0
  67. package/build-ngc/core/types.js +55 -0
  68. package/build-ngc/core/types.js.map +1 -0
  69. package/build-ngc/core/utils.d.ts +4 -0
  70. package/build-ngc/core/utils.js +25 -0
  71. package/build-ngc/core/utils.js.map +1 -0
  72. package/native-link.json +20 -0
  73. package/package.json +150 -0
  74. package/src/angular/index.ts +3 -0
  75. package/src/angular/services/camera-permissions.service/index.ts +16 -0
  76. package/src/angular/services/media-library-permissions.service/index.ts +19 -0
  77. package/src/core/image-picker.ts +90 -0
  78. package/src/core/index.ts +11 -0
  79. package/src/core/media-library-permission-api.ts +23 -0
  80. package/src/core/native-module.ts +34 -0
  81. package/src/core/types.ts +145 -0
  82. package/src/core/utils.ts +36 -0
  83. package/src/react/index.ts +6 -0
  84. package/src/react/use-camera-permissions.ts +25 -0
  85. package/src/react/use-media-library-permissions.ts +26 -0
  86. package/src/solid/index.ts +6 -0
  87. package/src/solid/use-camera-permissions.ts +26 -0
  88. package/src/solid/use-media-library-permissions.ts +27 -0
  89. package/src/svelte/index.ts +6 -0
  90. package/src/svelte/svelte-ambient.d.ts +3 -0
  91. package/src/svelte/svelte-compile.test-helper.ts +7 -0
  92. package/src/svelte/use-camera-permissions.svelte.ts +25 -0
  93. package/src/svelte/use-media-library-permissions.svelte.ts +26 -0
  94. package/src/vue/index.ts +6 -0
  95. package/src/vue/use-camera-permissions.ts +26 -0
  96. package/src/vue/use-media-library-permissions.ts +27 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # @symbiote-native/image-picker
2
+
3
+ Let users choose a photo or video from their library, or take one with the camera. One API for
4
+ every [SymbioteNative](../../README.md) adapter (React, Vue, Svelte, Solid and Angular).
5
+
6
+ It wraps [`expo-image-picker`](https://github.com/expo/expo/tree/main/packages/expo-image-picker),
7
+ which opens the system picker UI, the same way
8
+ [`@symbiote-native/document-picker`](../document-picker) wraps its upstream: `expo-modules-core` is
9
+ a direct dependency, never the `expo` meta-package, and the upstream JS is hand-ported into
10
+ `core/`. The mechanics live in the `symbiote-expo-native-module` project skill.
11
+
12
+ ## Install
13
+
14
+ **New app:**
15
+
16
+ ```bash
17
+ npx @symbiote-native/cli new my-app --image-picker
18
+ ```
19
+
20
+ **Existing SymbioteNative app:**
21
+
22
+ ```bash
23
+ npx @symbiote-native/cli add --image-picker
24
+ ```
25
+
26
+ Either way: installs `@symbiote-native/image-picker` and wires the native autolinking
27
+ automatically - see [`@symbiote-native/cli`](../cli).
28
+
29
+ <details>
30
+ <summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
31
+
32
+ ```bash
33
+ npm install @symbiote-native/image-picker
34
+ ```
35
+
36
+ `expo-image-picker` and `expo-modules-core` come along as regular, pinned dependencies - never
37
+ install either yourself, and never add the `expo` meta-package to this project (it bundles its
38
+ own Metro/Babel pipeline that conflicts with this project's own).
39
+
40
+ ### Required one-time step: native autolinking wiring
41
+
42
+ Unlike a plain RN native module, `expo-image-picker`'s native code is discovered by
43
+ `expo-modules-autolinking` - this needs wiring into the native host app **once**, covering this
44
+ package and every other `expo-modules-core` package with zero further changes:
45
+
46
+ | Platform | Touches |
47
+ | -------- | ------------------------------------------------------------------------------------- |
48
+ | iOS | `ios/Podfile` - add `use_expo_modules!` |
49
+ | iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
50
+ | Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
51
+ | Android | `MainApplication.kt` - Expo's bootstrap hook, plus a native-module name map |
52
+
53
+ Full mechanics live in the `symbiote-expo-native-module` skill. The per-package half of that
54
+ table - the Gradle dependency and the module map entry - is generated by
55
+ [`@symbiote-native/expo-modules-link`](../expo-modules-link) from this package's
56
+ `native-link.json` on every install.
57
+
58
+ ### Permissions this package requests
59
+
60
+ - **iOS `Info.plist` keys** (generated automatically): `NSPhotoLibraryUsageDescription`,
61
+ `NSCameraUsageDescription`, `NSMicrophoneUsageDescription` - reword the strings in
62
+ `native-link.json` if the defaults don't fit your app.
63
+ - **Android `RECORD_AUDIO`** (generated automatically) - needed because the camera picker can
64
+ record video with sound. `CAMERA`, `READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`, and the pre-33
65
+ storage permissions ship in `expo-image-picker`'s own `AndroidManifest.xml` and merge in
66
+ automatically once the Gradle project is included - nothing to declare for those.
67
+
68
+ </details>
69
+
70
+ ## Shape
71
+
72
+ ```
73
+ src/core/ launchCameraAsync + launchImageLibraryAsync + getPendingResultAsync +
74
+ the permission functions + the shared permission-hook runtime.
75
+ native-module.ts resolves ExponentImagePicker through
76
+ expo-modules-core's requireNativeModule.
77
+ src/react/ useCameraPermissions + useMediaLibraryPermissions (hooks)
78
+ src/vue/ useCameraPermissions + useMediaLibraryPermissions (composables)
79
+ src/solid/ useCameraPermissions + useMediaLibraryPermissions (primitives)
80
+ src/svelte/ useCameraPermissions + useMediaLibraryPermissions (runes)
81
+ src/angular/ CameraPermissionsService + MediaLibraryPermissionsService (DI)
82
+ ```
83
+
84
+ `launchCameraAsync`/`launchImageLibraryAsync`/`getPendingResultAsync` stay framework-agnostic free
85
+ functions, re-exported by every adapter's own barrel. Upstream's `useCameraPermissions`/
86
+ `useMediaLibraryPermissions` are ported to **every** adapter, not just React: React/Vue/Solid
87
+ bind `createPermissionHook` from `@symbiote-native/{react,vue,solid}` (the method-dispatch
88
+ plumbing lives once in `@symbiote-native/engine`'s `permission-hook-runtime.ts`), Svelte binds it
89
+ from `@symbiote-native/svelte/runes/create-permission-hook`, Angular ships
90
+ `CameraPermissionsService`/`MediaLibraryPermissionsService`.
91
+
92
+ ## Use it
93
+
94
+ ```ts
95
+ import { launchImageLibraryAsync, requestMediaLibraryPermissionsAsync } from '@symbiote-native/image-picker';
96
+
97
+ await requestMediaLibraryPermissionsAsync();
98
+ const result = await launchImageLibraryAsync({ mediaTypes: 'images', quality: 0.8 });
99
+ if (!result.canceled) {
100
+ console.log(result.assets[0].uri);
101
+ }
102
+ ```
103
+
104
+ Taking a photo:
105
+
106
+ ```ts
107
+ import { launchCameraAsync, requestCameraPermissionsAsync } from '@symbiote-native/image-picker';
108
+
109
+ await requestCameraPermissionsAsync();
110
+ const result = await launchCameraAsync({ allowsEditing: true });
111
+ ```
112
+
113
+ ## API
114
+
115
+ | Export | Signature | Notes |
116
+ | ------------------------------------ | -------------------------------------------------------------------- | ------------------------------------------ |
117
+ | `launchCameraAsync` | `(options?: IImagePickerOptions) => Promise<IImagePickerResult>` | Throws `UnavailabilityError` if absent. |
118
+ | `launchImageLibraryAsync` | `(options?: IImagePickerOptions) => Promise<IImagePickerResult>` | |
119
+ | `getCameraPermissionsAsync` | `() => Promise<ICameraPermissionResponse>` | |
120
+ | `requestCameraPermissionsAsync` | `() => Promise<ICameraPermissionResponse>` | |
121
+ | `getMediaLibraryPermissionsAsync` | `(writeOnly?: boolean) => Promise<IMediaLibraryPermissionResponse>` | |
122
+ | `requestMediaLibraryPermissionsAsync` | `(writeOnly?: boolean) => Promise<IMediaLibraryPermissionResponse>` | |
123
+ | `getPendingResultAsync` | `() => Promise<IImagePickerResult \| IImagePickerErrorResult \| null>` | Android only - resolves `null` on iOS. |
124
+
125
+ `IImagePickerOptions` carries the full upstream surface: `allowsEditing`, `aspect`, `shape`,
126
+ `quality`, `mediaTypes`, `exif`, `base64`, `videoExportPreset`, `videoQuality`,
127
+ `allowsMultipleSelection`, `selectionLimit`, `orderedSelection`, `defaultTab`, `videoMaxDuration`,
128
+ `presentationStyle`, `cameraType`, `preferredAssetRepresentationMode`, `legacy`,
129
+ `shouldDownloadFromNetwork`.
130
+
131
+ ## Declarative permission hooks
132
+
133
+ ```
134
+ useCameraPermissions() -> [status, requestPermission, getPermission] (React/Vue/Solid)
135
+ useMediaLibraryPermissions({ writeOnly? }) -> same shape, plus writeOnly forwarding
136
+ ```
137
+
138
+ React/Vue/Solid match upstream's tuple return; Svelte returns `{ status, requestPermission,
139
+ getPermission }` (a boxed getter, matching this repo's own rune idiom); Angular exposes
140
+ `CameraPermissionsService`/`MediaLibraryPermissionsService` (`connect()` returns a signal,
141
+ `get()`/`request()` are imperative), same DI shape as `@symbiote-native/brightness`'s own
142
+ `PermissionsService`.
143
+
144
+ ## Notes
145
+
146
+ - **`allowsEditing` + `allowsMultipleSelection` together only warns**, matching upstream: the
147
+ native side silently ignores `allowsEditing` in that case rather than throwing.
148
+ - **Deprecated `MediaTypeOptions` enum still works** (ported for parity) but logs a console
149
+ warning pointing at the string/array `mediaTypes` form upstream now prefers.
150
+ - **Check `canceled` before reading `assets`.** The result is a union: `assets` is `null` when the
151
+ user backs out.
152
+ - **Android can kill your app while the camera is open.** Call `getPendingResultAsync()` on launch to
153
+ recover a result that arrived while the activity was destroyed.
154
+ - **Web-only fields dropped**: `ImagePickerAsset.file`, `ImagePickerResult.output`, and the
155
+ `base64`/`capture` fields of the internal web-only options type - this package targets
156
+ iOS/Android only, matching `expo-module.config.json`'s `platforms: ["apple", "android"]`.
157
+
158
+ ## Common questions
159
+
160
+ - **Permission before opening the library?** None needed to launch it. Ask only when needed, for
161
+ example for videos on iOS when `allowsEditing` is `false` and the export preset is the default.
162
+ - **Camera does not open on the iOS Simulator.** It has no camera; use a device.
163
+ - **Android: result never arrives after the camera.** Android can destroy the activity; call
164
+ `getPendingResultAsync()` on launch.
165
+ - **Smaller file.** Lower `quality`, and resize with
166
+ [`@symbiote-native/image-manipulator`](../image-manipulator).
167
+ - **Base64 or EXIF.** `base64: true` / `exif: true` add them to each asset.
168
+ - **Wrong crop rectangle on iOS.** An upstream `UIImagePickerController` bug.
169
+
170
+ Sources: [Expo docs: ImagePicker](https://docs.expo.dev/versions/latest/sdk/imagepicker/),
171
+ [expo/expo#20482](https://github.com/expo/expo/issues/20482),
172
+ [expo/expo#11752](https://github.com/expo/expo/issues/11752).
173
+
174
+ ## Test it
175
+
176
+ ```bash
177
+ pnpm vitest run packages/image-picker
178
+ ```
179
+
180
+ The core tests fake the native module in place of `requireNativeModule`'s runtime resolution -
181
+ `ExponentImagePicker` only exists on a device, so a headless run would otherwise throw at import.
182
+ Two cases are ported directly from upstream's `src/__tests__/ImagePicker-test.native.ts`
183
+ (`launchCameraAsync`/`launchImageLibraryAsync` default to an empty options object); the rest is
184
+ net-new coverage of option validation, permission delegation, and the Android-only
185
+ `getPendingResultAsync` branch. Upstream's other test suite
186
+ (`plugin/src/__tests__/withImagePicker-test.ts`) exercises the config-plugin's own internals -
187
+ build-time code this project doesn't carry over (native config here is static
188
+ `native-link.json`, not a plugin we run), so it isn't ported.
@@ -0,0 +1,3 @@
1
+ export * from '../core';
2
+ export { CameraPermissionsService } from './services/camera-permissions.service';
3
+ export { MediaLibraryPermissionsService } from './services/media-library-permissions.service';
@@ -0,0 +1,3 @@
1
+ export * from '../core/index.js';
2
+ export { CameraPermissionsService } from './services/camera-permissions.service/index.js';
3
+ export { MediaLibraryPermissionsService } from './services/media-library-permissions.service/index.js';
@@ -0,0 +1,5 @@
1
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
2
+ import type { ICameraPermissionResponse } from '../../../core';
3
+ export declare class CameraPermissionsService extends PermissionsServiceBase<ICameraPermissionResponse> {
4
+ constructor();
5
+ }
@@ -0,0 +1,60 @@
1
+ // Angular twin of `../../react`'s `useCameraPermissions`
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
36
+ import { Injectable } from '@angular/core';
37
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
38
+ import { getCameraPermissionsAsync, requestCameraPermissionsAsync, } from '../../../core/index.js';
39
+ let CameraPermissionsService = (() => {
40
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
41
+ let _classDescriptor;
42
+ let _classExtraInitializers = [];
43
+ let _classThis;
44
+ let _classSuper = PermissionsServiceBase;
45
+ var CameraPermissionsService = class extends _classSuper {
46
+ static { _classThis = this; }
47
+ static {
48
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
49
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
50
+ CameraPermissionsService = _classThis = _classDescriptor.value;
51
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
52
+ __runInitializers(_classThis, _classExtraInitializers);
53
+ }
54
+ constructor() {
55
+ super(getCameraPermissionsAsync, requestCameraPermissionsAsync);
56
+ }
57
+ };
58
+ return CameraPermissionsService = _classThis;
59
+ })();
60
+ export { CameraPermissionsService };
@@ -0,0 +1,5 @@
1
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
2
+ import type { IMediaLibraryPermissionResponse } from '../../../core';
3
+ export declare class MediaLibraryPermissionsService extends PermissionsServiceBase<IMediaLibraryPermissionResponse, boolean> {
4
+ constructor();
5
+ }
@@ -0,0 +1,60 @@
1
+ // Angular twin of `../../react`'s `useMediaLibraryPermissions`, `get`/`request` take a `writeOnly` flag
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
36
+ import { Injectable } from '@angular/core';
37
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
38
+ import { getMediaLibraryPermissionsAsync, requestMediaLibraryPermissionsAsync, } from '../../../core/index.js';
39
+ let MediaLibraryPermissionsService = (() => {
40
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
41
+ let _classDescriptor;
42
+ let _classExtraInitializers = [];
43
+ let _classThis;
44
+ let _classSuper = PermissionsServiceBase;
45
+ var MediaLibraryPermissionsService = class extends _classSuper {
46
+ static { _classThis = this; }
47
+ static {
48
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
49
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
50
+ MediaLibraryPermissionsService = _classThis = _classDescriptor.value;
51
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
52
+ __runInitializers(_classThis, _classExtraInitializers);
53
+ }
54
+ constructor() {
55
+ super(getMediaLibraryPermissionsAsync, requestMediaLibraryPermissionsAsync);
56
+ }
57
+ };
58
+ return MediaLibraryPermissionsService = _classThis;
59
+ })();
60
+ export { MediaLibraryPermissionsService };
@@ -0,0 +1,9 @@
1
+ import type { ICameraPermissionResponse, IImagePickerErrorResult, IImagePickerOptions, IImagePickerResult, IMediaLibraryPermissionResponse } from './types';
2
+ export declare function getCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
3
+ export declare function getMediaLibraryPermissionsAsync(writeOnly?: boolean): Promise<IMediaLibraryPermissionResponse>;
4
+ export declare function requestCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
5
+ export declare function requestMediaLibraryPermissionsAsync(writeOnly?: boolean): Promise<IMediaLibraryPermissionResponse>;
6
+ /** Android only — recovers a result lost to `MainActivity` being killed mid-pick. */
7
+ export declare function getPendingResultAsync(): Promise<IImagePickerResult | IImagePickerErrorResult | null>;
8
+ export declare function launchCameraAsync(options?: IImagePickerOptions): Promise<IImagePickerResult>;
9
+ export declare function launchImageLibraryAsync(options?: IImagePickerOptions): Promise<IImagePickerResult>;
@@ -0,0 +1,52 @@
1
+ import { CodedError, UnavailabilityError } from 'expo-modules-core';
2
+ import { expoImagePicker } from './native-module.js';
3
+ import { mapDeprecatedOptions } from './utils.js';
4
+ function validateOptions(options) {
5
+ const { aspect, quality, videoMaxDuration } = options;
6
+ if (aspect != null) {
7
+ const [x, y] = aspect;
8
+ if (x <= 0 || y <= 0) {
9
+ throw new CodedError('ERR_INVALID_ARGUMENT', `Invalid aspect ratio values ${x}:${y}. Provide positive numbers.`);
10
+ }
11
+ }
12
+ if (quality != null && (quality < 0 || quality > 1)) {
13
+ throw new CodedError('ERR_INVALID_ARGUMENT', `Invalid 'quality' value ${quality}. Provide a value between 0 and 1.`);
14
+ }
15
+ if (videoMaxDuration != null && videoMaxDuration < 0) {
16
+ throw new CodedError('ERR_INVALID_ARGUMENT', `Invalid 'videoMaxDuration' value ${videoMaxDuration}. Provide a non-negative number.`);
17
+ }
18
+ return options;
19
+ }
20
+ export async function getCameraPermissionsAsync() {
21
+ return expoImagePicker.getCameraPermissionsAsync();
22
+ }
23
+ export async function getMediaLibraryPermissionsAsync(writeOnly = false) {
24
+ return expoImagePicker.getMediaLibraryPermissionsAsync(writeOnly);
25
+ }
26
+ export async function requestCameraPermissionsAsync() {
27
+ return expoImagePicker.requestCameraPermissionsAsync();
28
+ }
29
+ export async function requestMediaLibraryPermissionsAsync(writeOnly = false) {
30
+ return expoImagePicker.requestMediaLibraryPermissionsAsync(writeOnly);
31
+ }
32
+ /** Android only — recovers a result lost to `MainActivity` being killed mid-pick. */
33
+ export async function getPendingResultAsync() {
34
+ if (expoImagePicker.getPendingResultAsync) {
35
+ return expoImagePicker.getPendingResultAsync();
36
+ }
37
+ return null;
38
+ }
39
+ export async function launchCameraAsync(options = {}) {
40
+ if (!expoImagePicker.launchCameraAsync) {
41
+ throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');
42
+ }
43
+ const mappedOptions = mapDeprecatedOptions(options);
44
+ return await expoImagePicker.launchCameraAsync(validateOptions(mappedOptions));
45
+ }
46
+ export async function launchImageLibraryAsync(options = {}) {
47
+ const mappedOptions = mapDeprecatedOptions(options);
48
+ if (mappedOptions.allowsEditing && mappedOptions.allowsMultipleSelection) {
49
+ console.warn('[image-picker] `allowsEditing` is ignored when `allowsMultipleSelection` is enabled.');
50
+ }
51
+ return await expoImagePicker.launchImageLibraryAsync(mappedOptions);
52
+ }
@@ -0,0 +1,3 @@
1
+ export { PermissionStatus } from 'expo-modules-core';
2
+ export { getCameraPermissionsAsync, getMediaLibraryPermissionsAsync, getPendingResultAsync, launchCameraAsync, launchImageLibraryAsync, requestCameraPermissionsAsync, requestMediaLibraryPermissionsAsync, } from './image-picker';
3
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ export { PermissionStatus } from 'expo-modules-core';
2
+ export { getCameraPermissionsAsync, getMediaLibraryPermissionsAsync, getPendingResultAsync, launchCameraAsync, launchImageLibraryAsync, requestCameraPermissionsAsync, requestMediaLibraryPermissionsAsync, } from './image-picker.js';
3
+ export * from './types.js';
@@ -0,0 +1,11 @@
1
+ import type { IMediaLibraryPermissionResponse } from './types';
2
+ import { splitWriteOnlyPermissionOptions } from '@symbiote-native/engine';
3
+ export type IMediaLibraryPermissionMethodOptions = {
4
+ writeOnly?: boolean;
5
+ };
6
+ export declare const mediaLibraryPermissionMethods: {
7
+ getMethod: (options?: IMediaLibraryPermissionMethodOptions) => Promise<IMediaLibraryPermissionResponse>;
8
+ requestMethod: (options?: IMediaLibraryPermissionMethodOptions) => Promise<IMediaLibraryPermissionResponse>;
9
+ };
10
+ export declare const splitMediaLibraryPermissionOptions: typeof splitWriteOnlyPermissionOptions;
11
+ export type { IMediaLibraryPermissionResponse };
@@ -0,0 +1,9 @@
1
+ // `methods` every adapter's `useMediaLibraryPermissions` binds to the shared
2
+ // `@symbiote-native/{react,vue,solid}` `createPermissionHook` factory
3
+ import { getMediaLibraryPermissionsAsync, requestMediaLibraryPermissionsAsync, } from './index.js';
4
+ import { splitWriteOnlyPermissionOptions } from '@symbiote-native/engine';
5
+ export const mediaLibraryPermissionMethods = {
6
+ getMethod: (options) => getMediaLibraryPermissionsAsync(options?.writeOnly),
7
+ requestMethod: (options) => requestMediaLibraryPermissionsAsync(options?.writeOnly),
8
+ };
9
+ export const splitMediaLibraryPermissionOptions = splitWriteOnlyPermissionOptions;
@@ -0,0 +1,13 @@
1
+ import type { ICameraPermissionResponse, IImagePickerErrorResult, IImagePickerOptions, IImagePickerResult, IMediaLibraryPermissionResponse } from './types';
2
+ export type INativeImagePickerModule = {
3
+ getCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
4
+ getMediaLibraryPermissionsAsync(writeOnly: boolean): Promise<IMediaLibraryPermissionResponse>;
5
+ requestCameraPermissionsAsync(): Promise<ICameraPermissionResponse>;
6
+ requestMediaLibraryPermissionsAsync(writeOnly: boolean): Promise<IMediaLibraryPermissionResponse>;
7
+ /** Android only — absent on iOS's native module. */
8
+ getPendingResultAsync?(): Promise<IImagePickerResult | IImagePickerErrorResult>;
9
+ /** Absent on web's native module. */
10
+ launchCameraAsync?(options: IImagePickerOptions): Promise<IImagePickerResult>;
11
+ launchImageLibraryAsync(options: IImagePickerOptions): Promise<IImagePickerResult>;
12
+ };
13
+ export declare const expoImagePicker: INativeImagePickerModule;
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_IMAGE_PICKER_MODULE_NAME = 'ExponentImagePicker';
3
+ export const expoImagePicker = requireNativeModule(EXPO_IMAGE_PICKER_MODULE_NAME);
@@ -0,0 +1,127 @@
1
+ import type { PermissionResponse } from 'expo-modules-core';
2
+ export type ICameraPermissionResponse = PermissionResponse;
3
+ export type IMediaLibraryPermissionResponse = PermissionResponse & {
4
+ /** `'all'` | `'limited'` (Android 13+/iOS 14+) | `'none'`. */
5
+ accessPrivileges?: 'all' | 'limited' | 'none';
6
+ };
7
+ /** @deprecated use an array of `IMediaType` instead. */
8
+ export declare enum MediaTypeOptions {
9
+ All = "All",
10
+ Videos = "Videos",
11
+ Images = "Images"
12
+ }
13
+ export type IMediaType = 'images' | 'videos' | 'livePhotos';
14
+ /** Android only. */
15
+ export type IDefaultTab = 'photos' | 'albums';
16
+ export declare enum VideoExportPreset {
17
+ Passthrough = 0,
18
+ LowQuality = 1,
19
+ MediumQuality = 2,
20
+ HighestQuality = 3,
21
+ H264_640x480 = 4,
22
+ H264_960x540 = 5,
23
+ H264_1280x720 = 6,
24
+ H264_1920x1080 = 7,
25
+ H264_3840x2160 = 8,
26
+ HEVC_1920x1080 = 9,
27
+ HEVC_3840x2160 = 10
28
+ }
29
+ export declare enum UIImagePickerControllerQualityType {
30
+ High = 0,
31
+ Medium = 1,
32
+ Low = 2,
33
+ VGA640x480 = 3,
34
+ IFrame1280x720 = 4,
35
+ IFrame960x540 = 5
36
+ }
37
+ /** iOS only — maps to `UIModalPresentationStyle`. */
38
+ export declare enum UIImagePickerPresentationStyle {
39
+ FULL_SCREEN = "fullScreen",
40
+ PAGE_SHEET = "pageSheet",
41
+ FORM_SHEET = "formSheet",
42
+ CURRENT_CONTEXT = "currentContext",
43
+ OVER_FULL_SCREEN = "overFullScreen",
44
+ OVER_CURRENT_CONTEXT = "overCurrentContext",
45
+ POPOVER = "popover",
46
+ AUTOMATIC = "automatic"
47
+ }
48
+ /** iOS only — maps to `PHPickerConfigurationAssetRepresentationMode`. */
49
+ export declare enum UIImagePickerPreferredAssetRepresentationMode {
50
+ Automatic = "automatic",
51
+ Compatible = "compatible",
52
+ Current = "current"
53
+ }
54
+ export declare enum CameraType {
55
+ back = "back",
56
+ front = "front"
57
+ }
58
+ export type IImagePickerAsset = {
59
+ uri: string;
60
+ /** Usable with `@symbiote-native/media-library`. Null if unavailable or access is limited. */
61
+ assetId?: string | null;
62
+ width: number;
63
+ height: number;
64
+ type?: 'image' | 'video' | 'livePhoto' | 'pairedVideo' | null;
65
+ fileName?: string | null;
66
+ fileSize?: number;
67
+ exif?: Record<string, unknown> | null;
68
+ /** Base64 JPEG data — set only when the `base64` option is truthy. */
69
+ base64?: string | null;
70
+ duration?: number | null;
71
+ mimeType?: string;
72
+ /** Set only when `livePhotos` was requested and a live photo was picked. iOS only. */
73
+ pairedVideoAsset?: IImagePickerAsset | null;
74
+ };
75
+ export type IImagePickerErrorResult = {
76
+ code: string;
77
+ message: string;
78
+ exception?: string;
79
+ };
80
+ export type IImagePickerSuccessResult = {
81
+ canceled: false;
82
+ assets: IImagePickerAsset[];
83
+ };
84
+ export type IImagePickerCanceledResult = {
85
+ canceled: true;
86
+ assets: null;
87
+ };
88
+ export type IImagePickerResult = IImagePickerSuccessResult | IImagePickerCanceledResult;
89
+ export type ICropShape = 'rectangle' | 'oval';
90
+ export type IImagePickerOptions = {
91
+ /** Crop/rotate after picking. Exclusive with `allowsMultipleSelection`. Default `false`. */
92
+ allowsEditing?: boolean;
93
+ /** `[x, y]` crop aspect ratio. Android only. */
94
+ aspect?: [number, number];
95
+ /** Android only. Default `'rectangle'`. */
96
+ shape?: ICropShape;
97
+ /** `0`–`1`. Default `1.0`. */
98
+ quality?: number;
99
+ /** Default `'images'`. */
100
+ mediaTypes?: IMediaType | IMediaType[] | MediaTypeOptions;
101
+ exif?: boolean;
102
+ base64?: boolean;
103
+ /** @deprecated iOS 11+ only. */
104
+ videoExportPreset?: VideoExportPreset;
105
+ /** iOS only. Default `High`. */
106
+ videoQuality?: UIImagePickerControllerQualityType;
107
+ /** Ignored when `allowsEditing` is set. Default `false`. */
108
+ allowsMultipleSelection?: boolean;
109
+ /** `0` = system maximum. Android + iOS 14+. Default `0`. */
110
+ selectionLimit?: number;
111
+ /** iOS 15+ only. Default `false`. */
112
+ orderedSelection?: boolean;
113
+ /** Android only. Default `'photos'`. */
114
+ defaultTab?: IDefaultTab;
115
+ /** Seconds. `0` = no limit (default). No effect on web. */
116
+ videoMaxDuration?: number;
117
+ /** iOS only. Default `AUTOMATIC`. */
118
+ presentationStyle?: UIImagePickerPresentationStyle;
119
+ /** Default `back`. */
120
+ cameraType?: CameraType;
121
+ /** iOS 14+ only. Default `Automatic`. */
122
+ preferredAssetRepresentationMode?: UIImagePickerPreferredAssetRepresentationMode;
123
+ /** Android only — allows picking from outside the photo library. Default `false`. */
124
+ legacy?: boolean;
125
+ /** iOS only — download from iCloud if not stored locally. Default `false`. */
126
+ shouldDownloadFromNetwork?: boolean;
127
+ };