@symbiote-native/sharing 0.0.1
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/LICENSE +21 -0
- package/README.md +139 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +4 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +1 -0
- package/build/core/native-module.d.ts +6 -0
- package/build/core/native-module.js +3 -0
- package/build/core/sharing.d.ts +18 -0
- package/build/core/sharing.js +40 -0
- package/build/core/types.d.ts +38 -0
- package/build/core/types.js +1 -0
- package/build/react/index.d.ts +1 -0
- package/build/react/index.js +6 -0
- package/build/vue/index.d.ts +1 -0
- package/build/vue/index.js +4 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/index.d.ts +2 -0
- package/build-ngc/core/index.js +2 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +6 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/sharing.d.ts +18 -0
- package/build-ngc/core/sharing.js +41 -0
- package/build-ngc/core/sharing.js.map +1 -0
- package/build-ngc/core/types.d.ts +38 -0
- package/build-ngc/core/types.js +2 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +4 -0
- package/src/core/index.ts +2 -0
- package/src/core/native-module.ts +22 -0
- package/src/core/sharing.test.ts +105 -0
- package/src/core/sharing.ts +46 -0
- package/src/core/types.ts +39 -0
- package/src/react/index.ts +6 -0
- package/src/vue/index.ts +4 -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,139 @@
|
|
|
1
|
+
# @symbiote-native/sharing
|
|
2
|
+
|
|
3
|
+
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
+
[`expo-sharing`](https://github.com/expo/expo/tree/main/packages/expo-sharing) — the platform
|
|
5
|
+
share sheet for a local file — usable from **every** adapter, React, Vue, and Angular, not just
|
|
6
|
+
React. Built the same way as [`@symbiote-native/secure-store`](../secure-store): an
|
|
7
|
+
`expo-modules-core`-based wrapper (see the `symbiote-expo-native-module` project skill for the
|
|
8
|
+
full mechanism — why `expo-modules-core` is depended on directly and never the `expo`
|
|
9
|
+
meta-package, why the upstream JS is hand-ported into `core/` rather than imported, and how
|
|
10
|
+
autolinking picks up the native module).
|
|
11
|
+
|
|
12
|
+
## Scope: outgoing share only
|
|
13
|
+
|
|
14
|
+
`expo-sharing` has two halves. This package ships one of them.
|
|
15
|
+
|
|
16
|
+
| Half | Upstream API | Here |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| **Outgoing** — hand a local file to another app | `shareAsync`, `isAvailableAsync` | ✅ ported in full |
|
|
19
|
+
| **Incoming** — receive files other apps share *into* your app | `useIncomingShare`, `getSharedPayloads`, `getResolvedSharedPayloadsAsync`, `clearSharedPayloads` | ❌ not ported |
|
|
20
|
+
|
|
21
|
+
The incoming half is not a thinner JS surface — it needs a whole **iOS Share Extension target**.
|
|
22
|
+
Upstream's config plugin (`plugin/src/ios/*`) generates one: a second Xcode target with its own
|
|
23
|
+
build phases, its own `Info.plist`, an entitlements file, and an App Group ID shared between the
|
|
24
|
+
app and the extension, plus Android intent filters on the main activity. That is native
|
|
25
|
+
app-extension scaffolding, the same category this repo already parks `expo-widgets` in — no
|
|
26
|
+
JS-reachable runtime module can stand in for it, and SymbioteNative has no native-target
|
|
27
|
+
scaffolding story yet. So it is deliberately out of scope for this pass rather than shipped
|
|
28
|
+
half-working. If you need incoming share today, use `expo-sharing` directly in a React-only app
|
|
29
|
+
with Expo's own prebuild pipeline.
|
|
30
|
+
|
|
31
|
+
Everything below describes the outgoing half.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @symbiote-native/sharing
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`expo-sharing` and `expo-modules-core` come along as regular, pinned dependencies — never install
|
|
40
|
+
either yourself, and never add the `expo` meta-package to this project (it bundles its own
|
|
41
|
+
Metro/Babel pipeline that conflicts with this project's own).
|
|
42
|
+
|
|
43
|
+
### Required one-time step: native autolinking wiring
|
|
44
|
+
|
|
45
|
+
Unlike a plain RN native module, `expo-sharing`'s native code is discovered by
|
|
46
|
+
`expo-modules-autolinking` — this needs wiring into the native host app **once**, covering this
|
|
47
|
+
package and every other `expo-modules-core` package with zero further changes:
|
|
48
|
+
|
|
49
|
+
| Platform | Touches |
|
|
50
|
+
|---|---|
|
|
51
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
52
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
53
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
54
|
+
| Android | `MainApplication.kt` — Expo's bootstrap hook, plus a native-module name map |
|
|
55
|
+
|
|
56
|
+
Full mechanics live in the `symbiote-expo-native-module` skill. The per-package half of that
|
|
57
|
+
table — the Gradle dependency and the module map entry — is generated by
|
|
58
|
+
[`@symbiote-native/expo-modules-link`](../expo-modules-link) from this package's
|
|
59
|
+
`native-link.json` on every install.
|
|
60
|
+
|
|
61
|
+
There is no `Info.plist` usage description to add: opening the share sheet needs no iOS
|
|
62
|
+
permission. On Android, the `SharingFileProvider` and the `<queries>` block the chooser needs on
|
|
63
|
+
API 30+ ship inside `expo-sharing`'s own `AndroidManifest.xml` and merge into your app
|
|
64
|
+
automatically once the Gradle project is included — nothing to declare by hand.
|
|
65
|
+
|
|
66
|
+
## Shape
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
src/core/ the whole API: isAvailableAsync + shareAsync. native-module.ts
|
|
70
|
+
resolves ExpoSharing through expo-modules-core's requireNativeModule.
|
|
71
|
+
src/react/ @symbiote-native/sharing/react
|
|
72
|
+
src/vue/ @symbiote-native/sharing/vue
|
|
73
|
+
src/angular/ @symbiote-native/sharing/angular
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
All three adapter entries are plain re-exports of `core/`. Both exports are stateless free
|
|
77
|
+
functions — no per-instance state, no event stream — so there is nothing for a hook, composable,
|
|
78
|
+
or service to wrap, the same reason [`@symbiote-native/secure-store`](../secure-store) re-exports
|
|
79
|
+
rather than wraps. (The incoming-share half above is exactly the part that *would* have needed
|
|
80
|
+
one; it is the reason this package has no `hooks/`, `composables/`, or `services/` folder.)
|
|
81
|
+
Import from `@symbiote-native/sharing` directly if you don't care which adapter you're on; the
|
|
82
|
+
per-adapter subpaths exist so every wrapper package has the same import surface.
|
|
83
|
+
|
|
84
|
+
## Use it
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { isAvailableAsync, shareAsync } from '@symbiote-native/sharing';
|
|
88
|
+
|
|
89
|
+
if (await isAvailableAsync()) {
|
|
90
|
+
await shareAsync(localFileUri, {
|
|
91
|
+
mimeType: 'application/pdf',
|
|
92
|
+
dialogTitle: 'Send the report',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
On iPad, iOS presents the sheet as a popover and needs somewhere to point it:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
await shareAsync(localFileUri, {
|
|
101
|
+
anchor: { x: 40, y: 120, width: 1, height: 1 },
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## API
|
|
106
|
+
|
|
107
|
+
| Export | Signature | Notes |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `isAvailableAsync` | `() => Promise<boolean>` | `true` on Android and iOS. Reports on the native module, not on any device capability. |
|
|
110
|
+
| `shareAsync` | `(url, options?) => Promise<void>` | Opens the share sheet for a local file. Resolves when the sheet is dismissed. |
|
|
111
|
+
|
|
112
|
+
`ISharingOptions`: `mimeType` (Android), `UTI` (iOS), `dialogTitle`, `anchor` (iOS iPad).
|
|
113
|
+
`ISharingAnchor`: `x`, `y`, `width`, `height`, all optional, all in points.
|
|
114
|
+
|
|
115
|
+
## Notes
|
|
116
|
+
|
|
117
|
+
- **`url` must be local.** A `file://` URI or a path from a file-system API. A remote `http(s)`
|
|
118
|
+
URL is not downloaded first — fetch it to a local file yourself, then share that.
|
|
119
|
+
- **A resolved promise is not a delivery receipt.** Neither platform reports which app the user
|
|
120
|
+
picked, or whether they picked one at all: the promise resolves when the sheet closes. iOS
|
|
121
|
+
resolves on every dismissal path, including "picked Print, then cancelled the print dialog".
|
|
122
|
+
- **`UTI` is accepted but unused.** Both native modules declare the field in their options record
|
|
123
|
+
and neither reads it in `expo-sharing@57.0.8`. It stays in the type for forward compatibility
|
|
124
|
+
with upstream, not because it changes behavior.
|
|
125
|
+
- **Android runs one share at a time.** A second `shareAsync` while a chooser is open rejects
|
|
126
|
+
rather than queueing.
|
|
127
|
+
- **The url is validated before the native call** — non-empty string — so a bad argument fails
|
|
128
|
+
with a readable message instead of as an argument-conversion failure on iOS or a generic
|
|
129
|
+
"Failed to share the file" on Android. Upstream has no such guard.
|
|
130
|
+
|
|
131
|
+
## Test it
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pnpm vitest run packages/sharing
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The core tests fake the native module in place of `requireNativeModule`'s runtime resolution —
|
|
138
|
+
`ExpoSharing` only exists on a device, so a headless run would otherwise throw at import. The
|
|
139
|
+
share sheet itself can only be verified on a device or simulator.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { isAvailableAsync, shareAsync } from './sharing';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ISharingOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the share sheet can be opened on this device. Resolves `true` on Android and iOS.
|
|
4
|
+
*
|
|
5
|
+
* Both native modules leave the check to the JS side, so this reports on the presence of the
|
|
6
|
+
* native module rather than on any device capability.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isAvailableAsync(): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* Open the platform share sheet for a local file, letting the user hand it to any app that can
|
|
11
|
+
* accept it.
|
|
12
|
+
*
|
|
13
|
+
* `url` must point at a file the app can read — a `file://` URI, or a path from a file-system
|
|
14
|
+
* API. Remote `http(s)` URLs are not downloaded first; share their text via the target app
|
|
15
|
+
* instead. Resolves once the sheet is dismissed, whether or not the user picked anything: no
|
|
16
|
+
* platform reports which app received the file, so a resolved promise is not a delivery receipt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function shareAsync(url: string, options?: ISharingOptions): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { expoSharing } from './native-module';
|
|
3
|
+
const NATIVE_MODULE_NAME = 'expo-sharing';
|
|
4
|
+
/**
|
|
5
|
+
* Whether the share sheet can be opened on this device. Resolves `true` on Android and iOS.
|
|
6
|
+
*
|
|
7
|
+
* Both native modules leave the check to the JS side, so this reports on the presence of the
|
|
8
|
+
* native module rather than on any device capability.
|
|
9
|
+
*/
|
|
10
|
+
export async function isAvailableAsync() {
|
|
11
|
+
if (expoSharing.isAvailableAsync) {
|
|
12
|
+
return expoSharing.isAvailableAsync();
|
|
13
|
+
}
|
|
14
|
+
return !!expoSharing.shareAsync;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Open the platform share sheet for a local file, letting the user hand it to any app that can
|
|
18
|
+
* accept it.
|
|
19
|
+
*
|
|
20
|
+
* `url` must point at a file the app can read — a `file://` URI, or a path from a file-system
|
|
21
|
+
* API. Remote `http(s)` URLs are not downloaded first; share their text via the target app
|
|
22
|
+
* instead. Resolves once the sheet is dismissed, whether or not the user picked anything: no
|
|
23
|
+
* platform reports which app received the file, so a resolved promise is not a delivery receipt.
|
|
24
|
+
*/
|
|
25
|
+
export async function shareAsync(url, options = {}) {
|
|
26
|
+
ensureShareableUrl(url);
|
|
27
|
+
if (!expoSharing.shareAsync) {
|
|
28
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'shareAsync');
|
|
29
|
+
}
|
|
30
|
+
await expoSharing.shareAsync(url, options);
|
|
31
|
+
}
|
|
32
|
+
// Not in upstream, which hands anything straight to the native module. An empty or non-string
|
|
33
|
+
// url surfaces there as an argument-conversion failure on iOS and a generic "Failed to share the
|
|
34
|
+
// file" on Android — both far enough from the call site to be worth failing early with the
|
|
35
|
+
// caller's own argument named.
|
|
36
|
+
function ensureShareableUrl(url) {
|
|
37
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
38
|
+
throw new Error('Invalid url provided to Sharing. Pass a non-empty local file URI or path.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the iPad popover points. Every field is in points, relative to the presenting view;
|
|
3
|
+
* omitted fields fall back to the bottom-center of that view.
|
|
4
|
+
* @platform ios
|
|
5
|
+
*/
|
|
6
|
+
export type ISharingAnchor = {
|
|
7
|
+
x?: number;
|
|
8
|
+
y?: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
};
|
|
12
|
+
export type ISharingOptions = {
|
|
13
|
+
/**
|
|
14
|
+
* MIME type of the shared file, used to pick which apps the chooser offers. Guessed from the
|
|
15
|
+
* file name when omitted, falling back to a wildcard type that offers every app.
|
|
16
|
+
* @platform android
|
|
17
|
+
*/
|
|
18
|
+
mimeType?: string;
|
|
19
|
+
/**
|
|
20
|
+
* [Uniform Type Identifier](https://developer.apple.com/documentation/uniformtypeidentifiers)
|
|
21
|
+
* of the shared file.
|
|
22
|
+
*
|
|
23
|
+
* Both native modules accept the field, but neither reads it in `expo-sharing@57.0.8` — it is
|
|
24
|
+
* carried through for forward compatibility with upstream, not because it changes behavior.
|
|
25
|
+
* @platform ios
|
|
26
|
+
*/
|
|
27
|
+
UTI?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Title of the share dialog. Android renders it as the chooser's header; iOS assigns it to the
|
|
30
|
+
* activity controller, where most share sheets ignore it.
|
|
31
|
+
*/
|
|
32
|
+
dialogTitle?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Anchor rectangle for the popover iOS requires on iPad. Ignored on iPhone and on Android.
|
|
35
|
+
* @platform ios
|
|
36
|
+
*/
|
|
37
|
+
anchor?: ISharingAnchor;
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/sharing/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// Both exports are stateless free functions — no per-instance state, no event stream, nothing a
|
|
3
|
+
// hook could own or clean up — so this is a plain re-export, the same shape
|
|
4
|
+
// packages/secure-store's React entry has for the same reason. The one part of expo-sharing that
|
|
5
|
+
// WOULD need a hook (incoming share) is out of scope here; see the README.
|
|
6
|
+
export * from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @symbiote-native/sharing/angular: the Angular entry over the framework-agnostic core. Same
|
|
2
|
+
// reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
|
|
3
|
+
// service, so this is a plain re-export.
|
|
4
|
+
export * from '../core';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,0FAA0F;AAC1F,yCAAyC;AACzC,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAkB/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAuB,wBAAwB,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ISharingOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the share sheet can be opened on this device. Resolves `true` on Android and iOS.
|
|
4
|
+
*
|
|
5
|
+
* Both native modules leave the check to the JS side, so this reports on the presence of the
|
|
6
|
+
* native module rather than on any device capability.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isAvailableAsync(): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* Open the platform share sheet for a local file, letting the user hand it to any app that can
|
|
11
|
+
* accept it.
|
|
12
|
+
*
|
|
13
|
+
* `url` must point at a file the app can read — a `file://` URI, or a path from a file-system
|
|
14
|
+
* API. Remote `http(s)` URLs are not downloaded first; share their text via the target app
|
|
15
|
+
* instead. Resolves once the sheet is dismissed, whether or not the user picked anything: no
|
|
16
|
+
* platform reports which app received the file, so a resolved promise is not a delivery receipt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function shareAsync(url: string, options?: ISharingOptions): Promise<void>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { expoSharing } from './native-module';
|
|
3
|
+
const NATIVE_MODULE_NAME = 'expo-sharing';
|
|
4
|
+
/**
|
|
5
|
+
* Whether the share sheet can be opened on this device. Resolves `true` on Android and iOS.
|
|
6
|
+
*
|
|
7
|
+
* Both native modules leave the check to the JS side, so this reports on the presence of the
|
|
8
|
+
* native module rather than on any device capability.
|
|
9
|
+
*/
|
|
10
|
+
export async function isAvailableAsync() {
|
|
11
|
+
if (expoSharing.isAvailableAsync) {
|
|
12
|
+
return expoSharing.isAvailableAsync();
|
|
13
|
+
}
|
|
14
|
+
return !!expoSharing.shareAsync;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Open the platform share sheet for a local file, letting the user hand it to any app that can
|
|
18
|
+
* accept it.
|
|
19
|
+
*
|
|
20
|
+
* `url` must point at a file the app can read — a `file://` URI, or a path from a file-system
|
|
21
|
+
* API. Remote `http(s)` URLs are not downloaded first; share their text via the target app
|
|
22
|
+
* instead. Resolves once the sheet is dismissed, whether or not the user picked anything: no
|
|
23
|
+
* platform reports which app received the file, so a resolved promise is not a delivery receipt.
|
|
24
|
+
*/
|
|
25
|
+
export async function shareAsync(url, options = {}) {
|
|
26
|
+
ensureShareableUrl(url);
|
|
27
|
+
if (!expoSharing.shareAsync) {
|
|
28
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'shareAsync');
|
|
29
|
+
}
|
|
30
|
+
await expoSharing.shareAsync(url, options);
|
|
31
|
+
}
|
|
32
|
+
// Not in upstream, which hands anything straight to the native module. An empty or non-string
|
|
33
|
+
// url surfaces there as an argument-conversion failure on iOS and a generic "Failed to share the
|
|
34
|
+
// file" on Android — both far enough from the call site to be worth failing early with the
|
|
35
|
+
// caller's own argument named.
|
|
36
|
+
function ensureShareableUrl(url) {
|
|
37
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
38
|
+
throw new Error('Invalid url provided to Sharing. Pass a non-empty local file URI or path.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=sharing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharing.js","sourceRoot":"","sources":["../../src/core/sharing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC;QACjC,OAAO,WAAW,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,UAA2B,EAAE;IACzE,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,WAAW,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,8FAA8F;AAC9F,iGAAiG;AACjG,2FAA2F;AAC3F,+BAA+B;AAC/B,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the iPad popover points. Every field is in points, relative to the presenting view;
|
|
3
|
+
* omitted fields fall back to the bottom-center of that view.
|
|
4
|
+
* @platform ios
|
|
5
|
+
*/
|
|
6
|
+
export type ISharingAnchor = {
|
|
7
|
+
x?: number;
|
|
8
|
+
y?: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
};
|
|
12
|
+
export type ISharingOptions = {
|
|
13
|
+
/**
|
|
14
|
+
* MIME type of the shared file, used to pick which apps the chooser offers. Guessed from the
|
|
15
|
+
* file name when omitted, falling back to a wildcard type that offers every app.
|
|
16
|
+
* @platform android
|
|
17
|
+
*/
|
|
18
|
+
mimeType?: string;
|
|
19
|
+
/**
|
|
20
|
+
* [Uniform Type Identifier](https://developer.apple.com/documentation/uniformtypeidentifiers)
|
|
21
|
+
* of the shared file.
|
|
22
|
+
*
|
|
23
|
+
* Both native modules accept the field, but neither reads it in `expo-sharing@57.0.8` — it is
|
|
24
|
+
* carried through for forward compatibility with upstream, not because it changes behavior.
|
|
25
|
+
* @platform ios
|
|
26
|
+
*/
|
|
27
|
+
UTI?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Title of the share dialog. Android renders it as the chooser's header; iOS assigns it to the
|
|
30
|
+
* activity controller, where most share sheets ignore it.
|
|
31
|
+
*/
|
|
32
|
+
dialogTitle?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Anchor rectangle for the popover iOS requires on iPad. Ignored on iPhone and on Android.
|
|
35
|
+
* @platform ios
|
|
36
|
+
*/
|
|
37
|
+
anchor?: ISharingAnchor;
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
package/native-link.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbiote-native/sharing",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "expo-sharing wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Opens the platform share sheet for a local file.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
9
|
+
"directory": "packages/sharing"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/sharing#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./build/core/index.js",
|
|
18
|
+
"module": "./build/core/index.js",
|
|
19
|
+
"types": "./build/core/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./build/core/index.d.ts",
|
|
23
|
+
"default": "./build/core/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./vue": {
|
|
26
|
+
"types": "./build/vue/index.d.ts",
|
|
27
|
+
"default": "./build/vue/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./build/react/index.d.ts",
|
|
31
|
+
"default": "./build/react/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./angular": {
|
|
34
|
+
"types": "./build-ngc/angular/index.d.ts",
|
|
35
|
+
"react-native": "./build-ngc/angular/index.js",
|
|
36
|
+
"default": "./src/angular/index.ts"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"build",
|
|
42
|
+
"build-ngc",
|
|
43
|
+
"native-link.json"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"expo-sharing": "57.0.8",
|
|
50
|
+
"expo-modules-core": "57.0.5"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@symbiote-native/engine": ">=0.1.7",
|
|
54
|
+
"@angular/core": ">=20",
|
|
55
|
+
"@vue/runtime-core": "^3.5.13",
|
|
56
|
+
"react": ">=19.0.0",
|
|
57
|
+
"react-native": ">=0.86",
|
|
58
|
+
"vue": ">=3.5.0",
|
|
59
|
+
"@symbiote-native/angular": "0.6.1",
|
|
60
|
+
"@symbiote-native/react": "0.2.8",
|
|
61
|
+
"@symbiote-native/vue": "0.3.8"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"@symbiote-native/angular": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@symbiote-native/react": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"@symbiote-native/vue": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"@angular/core": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"@vue/runtime-core": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"react": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"vue": {
|
|
83
|
+
"optional": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@angular/compiler": "^22",
|
|
88
|
+
"@angular/compiler-cli": "^22",
|
|
89
|
+
"@angular/core": "^22",
|
|
90
|
+
"@types/node": "^26.0.0",
|
|
91
|
+
"@types/react": "^19.2.0",
|
|
92
|
+
"@vue/runtime-core": "^3.5.13",
|
|
93
|
+
"react": "19.2.3",
|
|
94
|
+
"typescript": "~6.0.0",
|
|
95
|
+
"@symbiote-native/test-utils": "0.1.6",
|
|
96
|
+
"@symbiote-native/react": "0.2.8",
|
|
97
|
+
"@symbiote-native/engine": "0.1.7",
|
|
98
|
+
"@symbiote-native/vue": "0.3.8",
|
|
99
|
+
"@symbiote-native/angular": "0.6.1"
|
|
100
|
+
},
|
|
101
|
+
"scripts": {
|
|
102
|
+
"typecheck": "tsc --build",
|
|
103
|
+
"clean": "rm -rf build-ngc",
|
|
104
|
+
"ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
|
|
105
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
import type { ISharingOptions } from './types';
|
|
3
|
+
|
|
4
|
+
const EXPO_SHARING_MODULE_NAME = 'ExpoSharing';
|
|
5
|
+
|
|
6
|
+
// Every member is optional — each call site checks for its presence before calling through and
|
|
7
|
+
// throws an UnavailabilityError itself, matching upstream's own per-platform capability checks
|
|
8
|
+
// rather than assuming the native module implements the whole surface.
|
|
9
|
+
//
|
|
10
|
+
// `isAvailableAsync` is genuinely absent from both native modules in expo-sharing@57.0.8: only
|
|
11
|
+
// the web module (which this package does not ship) defines it. Presence of the module itself is
|
|
12
|
+
// therefore what availability is read from — see isAvailableAsync in ./sharing.
|
|
13
|
+
//
|
|
14
|
+
// The incoming-share methods (getSharedPayloads, getResolvedSharedPayloadsAsync,
|
|
15
|
+
// clearSharedPayloads) are deliberately absent from this type: reaching them needs an iOS Share
|
|
16
|
+
// Extension target this package does not ship. See the README.
|
|
17
|
+
export type INativeSharingModule = {
|
|
18
|
+
isAvailableAsync?(): Promise<boolean>;
|
|
19
|
+
shareAsync?(url: string, options: ISharingOptions): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const expoSharing = requireNativeModule<INativeSharingModule>(EXPO_SHARING_MODULE_NAME);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const FAKE_NATIVE_SHARING = {
|
|
4
|
+
shareAsync: vi.fn(async () => undefined),
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// The real ExpoSharing native module only exists on device — resolving it via
|
|
8
|
+
// requireNativeModule() at import time would throw in this headless run, so the module-lookup
|
|
9
|
+
// file is faked in place of expo-modules-core's runtime resolution, the same pattern
|
|
10
|
+
// packages/secure-store/src/core/secure-store.test.ts uses.
|
|
11
|
+
vi.mock('./native-module', () => ({
|
|
12
|
+
expoSharing: FAKE_NATIVE_SHARING,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// expo-modules-core's real entry transitively imports 'react-native', whose Flow-typed source
|
|
16
|
+
// Vitest's Oxc transform can't parse — so only the members used as values are faked.
|
|
17
|
+
vi.mock('expo-modules-core', () => ({
|
|
18
|
+
UnavailabilityError: class UnavailabilityError extends Error {
|
|
19
|
+
constructor(moduleName: string, propertyName: string) {
|
|
20
|
+
super(`${propertyName} is not available on ${moduleName}`);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const { isAvailableAsync, shareAsync } = await import('./sharing');
|
|
26
|
+
|
|
27
|
+
const LOCAL_FILE_URL = 'file:///tmp/report.pdf';
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('isAvailableAsync', () => {
|
|
34
|
+
it('reports availability from the presence of the native share method', async () => {
|
|
35
|
+
await expect(isAvailableAsync()).resolves.toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('reports unavailable when the native module cannot share', async () => {
|
|
39
|
+
const { shareAsync: native } = FAKE_NATIVE_SHARING;
|
|
40
|
+
// @ts-expect-error -- simulating a platform where the native module has no such method
|
|
41
|
+
FAKE_NATIVE_SHARING.shareAsync = undefined;
|
|
42
|
+
|
|
43
|
+
await expect(isAvailableAsync()).resolves.toBe(false);
|
|
44
|
+
|
|
45
|
+
FAKE_NATIVE_SHARING.shareAsync = native;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('defers to the native check when the module implements one', async () => {
|
|
49
|
+
const nativeCheck = vi.fn(async () => false);
|
|
50
|
+
Object.assign(FAKE_NATIVE_SHARING, { isAvailableAsync: nativeCheck });
|
|
51
|
+
|
|
52
|
+
await expect(isAvailableAsync()).resolves.toBe(false);
|
|
53
|
+
expect(nativeCheck).toHaveBeenCalled();
|
|
54
|
+
|
|
55
|
+
Reflect.deleteProperty(FAKE_NATIVE_SHARING, 'isAvailableAsync');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('shareAsync', () => {
|
|
60
|
+
it('passes the url and options straight through', async () => {
|
|
61
|
+
await shareAsync(LOCAL_FILE_URL, { mimeType: 'application/pdf', dialogTitle: 'Send report' });
|
|
62
|
+
expect(FAKE_NATIVE_SHARING.shareAsync).toHaveBeenCalledWith(LOCAL_FILE_URL, {
|
|
63
|
+
mimeType: 'application/pdf',
|
|
64
|
+
dialogTitle: 'Send report',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('defaults the options to an empty object', async () => {
|
|
69
|
+
await shareAsync(LOCAL_FILE_URL);
|
|
70
|
+
expect(FAKE_NATIVE_SHARING.shareAsync).toHaveBeenCalledWith(LOCAL_FILE_URL, {});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('forwards the iPad anchor rectangle unflattened', async () => {
|
|
74
|
+
const anchor = { x: 10, y: 20, width: 1, height: 1 };
|
|
75
|
+
await shareAsync(LOCAL_FILE_URL, { anchor });
|
|
76
|
+
expect(FAKE_NATIVE_SHARING.shareAsync).toHaveBeenCalledWith(LOCAL_FILE_URL, { anchor });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
|
|
80
|
+
const { shareAsync: native } = FAKE_NATIVE_SHARING;
|
|
81
|
+
// @ts-expect-error -- simulating a platform where the native module has no such method
|
|
82
|
+
FAKE_NATIVE_SHARING.shareAsync = undefined;
|
|
83
|
+
|
|
84
|
+
await expect(shareAsync(LOCAL_FILE_URL)).rejects.toThrow(
|
|
85
|
+
'shareAsync is not available on expo-sharing',
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
FAKE_NATIVE_SHARING.shareAsync = native;
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Validation happens before the native call so a bad url fails with a readable message here
|
|
93
|
+
// rather than as an argument-conversion failure on iOS or a generic share failure on Android.
|
|
94
|
+
describe('url validation', () => {
|
|
95
|
+
it('rejects an empty url', async () => {
|
|
96
|
+
await expect(shareAsync('')).rejects.toThrow(/Invalid url provided to Sharing/);
|
|
97
|
+
expect(FAKE_NATIVE_SHARING.shareAsync).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('rejects a non-string url before reaching the native module', async () => {
|
|
101
|
+
// @ts-expect-error -- the guard exists precisely for callers without type checking
|
|
102
|
+
await expect(shareAsync(undefined)).rejects.toThrow(/Invalid url provided to Sharing/);
|
|
103
|
+
expect(FAKE_NATIVE_SHARING.shareAsync).not.toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
|
|
3
|
+
import { expoSharing } from './native-module';
|
|
4
|
+
import type { ISharingOptions } from './types';
|
|
5
|
+
|
|
6
|
+
const NATIVE_MODULE_NAME = 'expo-sharing';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Whether the share sheet can be opened on this device. Resolves `true` on Android and iOS.
|
|
10
|
+
*
|
|
11
|
+
* Both native modules leave the check to the JS side, so this reports on the presence of the
|
|
12
|
+
* native module rather than on any device capability.
|
|
13
|
+
*/
|
|
14
|
+
export async function isAvailableAsync(): Promise<boolean> {
|
|
15
|
+
if (expoSharing.isAvailableAsync) {
|
|
16
|
+
return expoSharing.isAvailableAsync();
|
|
17
|
+
}
|
|
18
|
+
return !!expoSharing.shareAsync;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Open the platform share sheet for a local file, letting the user hand it to any app that can
|
|
23
|
+
* accept it.
|
|
24
|
+
*
|
|
25
|
+
* `url` must point at a file the app can read — a `file://` URI, or a path from a file-system
|
|
26
|
+
* API. Remote `http(s)` URLs are not downloaded first; share their text via the target app
|
|
27
|
+
* instead. Resolves once the sheet is dismissed, whether or not the user picked anything: no
|
|
28
|
+
* platform reports which app received the file, so a resolved promise is not a delivery receipt.
|
|
29
|
+
*/
|
|
30
|
+
export async function shareAsync(url: string, options: ISharingOptions = {}): Promise<void> {
|
|
31
|
+
ensureShareableUrl(url);
|
|
32
|
+
if (!expoSharing.shareAsync) {
|
|
33
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'shareAsync');
|
|
34
|
+
}
|
|
35
|
+
await expoSharing.shareAsync(url, options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Not in upstream, which hands anything straight to the native module. An empty or non-string
|
|
39
|
+
// url surfaces there as an argument-conversion failure on iOS and a generic "Failed to share the
|
|
40
|
+
// file" on Android — both far enough from the call site to be worth failing early with the
|
|
41
|
+
// caller's own argument named.
|
|
42
|
+
function ensureShareableUrl(url: string): void {
|
|
43
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
44
|
+
throw new Error('Invalid url provided to Sharing. Pass a non-empty local file URI or path.');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the iPad popover points. Every field is in points, relative to the presenting view;
|
|
3
|
+
* omitted fields fall back to the bottom-center of that view.
|
|
4
|
+
* @platform ios
|
|
5
|
+
*/
|
|
6
|
+
export type ISharingAnchor = {
|
|
7
|
+
x?: number;
|
|
8
|
+
y?: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ISharingOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* MIME type of the shared file, used to pick which apps the chooser offers. Guessed from the
|
|
16
|
+
* file name when omitted, falling back to a wildcard type that offers every app.
|
|
17
|
+
* @platform android
|
|
18
|
+
*/
|
|
19
|
+
mimeType?: string;
|
|
20
|
+
/**
|
|
21
|
+
* [Uniform Type Identifier](https://developer.apple.com/documentation/uniformtypeidentifiers)
|
|
22
|
+
* of the shared file.
|
|
23
|
+
*
|
|
24
|
+
* Both native modules accept the field, but neither reads it in `expo-sharing@57.0.8` — it is
|
|
25
|
+
* carried through for forward compatibility with upstream, not because it changes behavior.
|
|
26
|
+
* @platform ios
|
|
27
|
+
*/
|
|
28
|
+
UTI?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Title of the share dialog. Android renders it as the chooser's header; iOS assigns it to the
|
|
31
|
+
* activity controller, where most share sheets ignore it.
|
|
32
|
+
*/
|
|
33
|
+
dialogTitle?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Anchor rectangle for the popover iOS requires on iPad. Ignored on iPhone and on Android.
|
|
36
|
+
* @platform ios
|
|
37
|
+
*/
|
|
38
|
+
anchor?: ISharingAnchor;
|
|
39
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/sharing/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// Both exports are stateless free functions — no per-instance state, no event stream, nothing a
|
|
3
|
+
// hook could own or clean up — so this is a plain re-export, the same shape
|
|
4
|
+
// packages/secure-store's React entry has for the same reason. The one part of expo-sharing that
|
|
5
|
+
// WOULD need a hook (incoming share) is out of scope here; see the README.
|
|
6
|
+
export * from '../core';
|
package/src/vue/index.ts
ADDED