@symbiote-native/live-photo 0.0.0-stage → 0.0.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.
- package/LICENSE +21 -0
- package/README.md +46 -2
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +3 -0
- package/build/angular/live-photo-view/index.d.ts +21 -0
- package/build/angular/live-photo-view/index.js +147 -0
- package/build/core/constants.d.ts +2 -0
- package/build/core/constants.js +3 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +2 -0
- package/build/core/live-photo-view.d.ts +8 -0
- package/build/core/live-photo-view.js +57 -0
- package/build/core/types.d.ts +50 -0
- package/build/core/types.js +1 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.js +3 -0
- package/build/react/live-photo-view.d.ts +8 -0
- package/build/react/live-photo-view.js +11 -0
- package/build/solid/index.d.ts +3 -0
- package/build/solid/index.js +3 -0
- package/build/solid/live-photo-view.d.ts +8 -0
- package/build/solid/live-photo-view.js +18 -0
- package/build/svelte/index.d.ts +2 -0
- package/build/svelte/index.js +3 -0
- package/build/svelte/live-photo-view.svelte +31 -0
- package/build/svelte/live-photo-view.svelte.d.ts +7 -0
- package/build/vue/index.d.ts +2 -0
- package/build/vue/index.js +3 -0
- package/build/vue/live-photo-view.d.ts +3 -0
- package/build/vue/live-photo-view.js +18 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +4 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/live-photo-view/index.d.ts +24 -0
- package/build-ngc/angular/live-photo-view/index.js +76 -0
- package/build-ngc/angular/live-photo-view/index.js.map +1 -0
- package/build-ngc/core/constants.d.ts +2 -0
- package/build-ngc/core/constants.js +4 -0
- package/build-ngc/core/constants.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/live-photo-view.d.ts +8 -0
- package/build-ngc/core/live-photo-view.js +58 -0
- package/build-ngc/core/live-photo-view.js.map +1 -0
- package/build-ngc/core/types.d.ts +50 -0
- package/build-ngc/core/types.js +2 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +5 -0
- package/package.json +144 -3
- package/src/angular/index.ts +13 -0
- package/src/angular/live-photo-view/index.ts +59 -0
- package/src/core/constants.ts +4 -0
- package/src/core/index.ts +15 -0
- package/src/core/live-photo-view.ts +95 -0
- package/src/core/types.ts +66 -0
- package/src/react/index.ts +14 -0
- package/src/react/live-photo-view.ts +22 -0
- package/src/solid/index.ts +14 -0
- package/src/solid/live-photo-view.ts +27 -0
- package/src/svelte/index.ts +4 -0
- package/src/svelte/live-photo-view.svelte +31 -0
- package/src/svelte/svelte-ambient.d.ts +3 -0
- package/src/svelte/svelte-compile.test-helper.ts +6 -0
- package/src/vue/index.ts +13 -0
- package/src/vue/live-photo-view.ts +19 -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
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @symbiote-native/live-photo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[`expo-live-photo`](https://docs.expo.dev/versions/latest/sdk/live-photo/) for **every**
|
|
4
|
+
[SymbioteNative](../../README.md) adapter: React, Vue, Svelte, Solid and Angular. Shows a Live
|
|
5
|
+
Photo, iOS only.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @symbiote-native/cli new my-app --live-photo # new app
|
|
11
|
+
npx @symbiote-native/cli add --live-photo # existing app
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Manual: `npm install @symbiote-native/live-photo`, then wire `expo-modules-autolinking` once per
|
|
15
|
+
app. Never install `expo-live-photo` or the `expo` meta-package yourself.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { LivePhotoView, type ILivePhotoViewHandle } from '@symbiote-native/live-photo/react';
|
|
21
|
+
|
|
22
|
+
const ref = useRef<ILivePhotoViewHandle>(null);
|
|
23
|
+
|
|
24
|
+
<LivePhotoView
|
|
25
|
+
ref={ref}
|
|
26
|
+
style={{ width: 300, height: 400 }}
|
|
27
|
+
source={{ photoUri, pairedVideoUri }}
|
|
28
|
+
contentFit="cover"
|
|
29
|
+
onLoadError={error => console.warn(error.message)}
|
|
30
|
+
/>;
|
|
31
|
+
|
|
32
|
+
ref.current?.startPlayback('hint');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The view takes `source`, `isMuted`, `contentFit`, `useDefaultGestureRecognizer`, the load and
|
|
36
|
+
playback callbacks and the View surface. The handle has `startPlayback(style?)` and
|
|
37
|
+
`stopPlayback()`. How the handle is reached follows each framework: a `ref` in React and Solid
|
|
38
|
+
(`ref={fn}`), the exposed methods of a template ref in Vue, `bind:this` in Svelte and a
|
|
39
|
+
`@ViewChild` in Angular.
|
|
40
|
+
|
|
41
|
+
Off iOS the view renders nothing (with a dev-only warning) and the handle throws
|
|
42
|
+
`UnavailabilityError`.
|
|
43
|
+
|
|
44
|
+
## Not ported
|
|
45
|
+
|
|
46
|
+
Nothing. Upstream ships no tests and no config plugin, the suites here are written against its
|
|
47
|
+
behavior.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { LivePhotoView } from './live-photo-view';
|
|
2
|
+
export { LIVE_PHOTO_MODULE_NAME, livePhotoViewName, type ILivePhotoAsset, type ILivePhotoContentFit, type ILivePhotoLoadError, type ILivePhotoPlaybackStyle, type ILivePhotoViewHandle, type ILivePhotoViewProps, } from '../core';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NativeViewBase } from '@symbiote-native/angular';
|
|
2
|
+
import type { IDescriptor } from '@symbiote-native/components';
|
|
3
|
+
import type { ILivePhotoPlaybackStyle, ILivePhotoViewProps } from '../../core';
|
|
4
|
+
/** Angular twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
5
|
+
export declare class LivePhotoView extends NativeViewBase {
|
|
6
|
+
source?: ILivePhotoViewProps['source'];
|
|
7
|
+
isMuted?: ILivePhotoViewProps['isMuted'];
|
|
8
|
+
contentFit?: ILivePhotoViewProps['contentFit'];
|
|
9
|
+
useDefaultGestureRecognizer?: ILivePhotoViewProps['useDefaultGestureRecognizer'];
|
|
10
|
+
onPlaybackStart?: ILivePhotoViewProps['onPlaybackStart'];
|
|
11
|
+
onPlaybackStop?: ILivePhotoViewProps['onPlaybackStop'];
|
|
12
|
+
onLoadStart?: ILivePhotoViewProps['onLoadStart'];
|
|
13
|
+
onPreviewPhotoLoad?: ILivePhotoViewProps['onPreviewPhotoLoad'];
|
|
14
|
+
onLoadComplete?: ILivePhotoViewProps['onLoadComplete'];
|
|
15
|
+
onLoadError?: ILivePhotoViewProps['onLoadError'];
|
|
16
|
+
private readonly handle;
|
|
17
|
+
protected readonly propNames: readonly ["source", "isMuted", "contentFit", "useDefaultGestureRecognizer", "onPlaybackStart", "onPlaybackStop", "onLoadStart", "onPreviewPhotoLoad", "onLoadComplete", "onLoadError"];
|
|
18
|
+
protected renderView(props: object): IDescriptor | null;
|
|
19
|
+
startPlayback(playbackStyle?: ILivePhotoPlaybackStyle): void;
|
|
20
|
+
stopPlayback(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
|
36
|
+
import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
|
|
37
|
+
import { createLivePhotoViewHandle, renderLivePhotoView } from '../../core/index.js';
|
|
38
|
+
/** Angular twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
39
|
+
let LivePhotoView = (() => {
|
|
40
|
+
let _classDecorators = [Component({
|
|
41
|
+
selector: 'LivePhotoView',
|
|
42
|
+
standalone: true,
|
|
43
|
+
imports: [DescriptorOutlet],
|
|
44
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
45
|
+
template: `@if (descriptor; as node) {
|
|
46
|
+
<symbiote-descriptor-outlet [node]="node" />
|
|
47
|
+
}`,
|
|
48
|
+
})];
|
|
49
|
+
let _classDescriptor;
|
|
50
|
+
let _classExtraInitializers = [];
|
|
51
|
+
let _classThis;
|
|
52
|
+
let _classSuper = NativeViewBase;
|
|
53
|
+
let _source_decorators;
|
|
54
|
+
let _source_initializers = [];
|
|
55
|
+
let _source_extraInitializers = [];
|
|
56
|
+
let _isMuted_decorators;
|
|
57
|
+
let _isMuted_initializers = [];
|
|
58
|
+
let _isMuted_extraInitializers = [];
|
|
59
|
+
let _contentFit_decorators;
|
|
60
|
+
let _contentFit_initializers = [];
|
|
61
|
+
let _contentFit_extraInitializers = [];
|
|
62
|
+
let _useDefaultGestureRecognizer_decorators;
|
|
63
|
+
let _useDefaultGestureRecognizer_initializers = [];
|
|
64
|
+
let _useDefaultGestureRecognizer_extraInitializers = [];
|
|
65
|
+
let _onPlaybackStart_decorators;
|
|
66
|
+
let _onPlaybackStart_initializers = [];
|
|
67
|
+
let _onPlaybackStart_extraInitializers = [];
|
|
68
|
+
let _onPlaybackStop_decorators;
|
|
69
|
+
let _onPlaybackStop_initializers = [];
|
|
70
|
+
let _onPlaybackStop_extraInitializers = [];
|
|
71
|
+
let _onLoadStart_decorators;
|
|
72
|
+
let _onLoadStart_initializers = [];
|
|
73
|
+
let _onLoadStart_extraInitializers = [];
|
|
74
|
+
let _onPreviewPhotoLoad_decorators;
|
|
75
|
+
let _onPreviewPhotoLoad_initializers = [];
|
|
76
|
+
let _onPreviewPhotoLoad_extraInitializers = [];
|
|
77
|
+
let _onLoadComplete_decorators;
|
|
78
|
+
let _onLoadComplete_initializers = [];
|
|
79
|
+
let _onLoadComplete_extraInitializers = [];
|
|
80
|
+
let _onLoadError_decorators;
|
|
81
|
+
let _onLoadError_initializers = [];
|
|
82
|
+
let _onLoadError_extraInitializers = [];
|
|
83
|
+
var LivePhotoView = class extends _classSuper {
|
|
84
|
+
static { _classThis = this; }
|
|
85
|
+
static {
|
|
86
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
87
|
+
_source_decorators = [Input()];
|
|
88
|
+
_isMuted_decorators = [Input()];
|
|
89
|
+
_contentFit_decorators = [Input()];
|
|
90
|
+
_useDefaultGestureRecognizer_decorators = [Input()];
|
|
91
|
+
_onPlaybackStart_decorators = [Input()];
|
|
92
|
+
_onPlaybackStop_decorators = [Input()];
|
|
93
|
+
_onLoadStart_decorators = [Input()];
|
|
94
|
+
_onPreviewPhotoLoad_decorators = [Input()];
|
|
95
|
+
_onLoadComplete_decorators = [Input()];
|
|
96
|
+
_onLoadError_decorators = [Input()];
|
|
97
|
+
__esDecorate(null, null, _source_decorators, { kind: "field", name: "source", static: false, private: false, access: { has: obj => "source" in obj, get: obj => obj.source, set: (obj, value) => { obj.source = value; } }, metadata: _metadata }, _source_initializers, _source_extraInitializers);
|
|
98
|
+
__esDecorate(null, null, _isMuted_decorators, { kind: "field", name: "isMuted", static: false, private: false, access: { has: obj => "isMuted" in obj, get: obj => obj.isMuted, set: (obj, value) => { obj.isMuted = value; } }, metadata: _metadata }, _isMuted_initializers, _isMuted_extraInitializers);
|
|
99
|
+
__esDecorate(null, null, _contentFit_decorators, { kind: "field", name: "contentFit", static: false, private: false, access: { has: obj => "contentFit" in obj, get: obj => obj.contentFit, set: (obj, value) => { obj.contentFit = value; } }, metadata: _metadata }, _contentFit_initializers, _contentFit_extraInitializers);
|
|
100
|
+
__esDecorate(null, null, _useDefaultGestureRecognizer_decorators, { kind: "field", name: "useDefaultGestureRecognizer", static: false, private: false, access: { has: obj => "useDefaultGestureRecognizer" in obj, get: obj => obj.useDefaultGestureRecognizer, set: (obj, value) => { obj.useDefaultGestureRecognizer = value; } }, metadata: _metadata }, _useDefaultGestureRecognizer_initializers, _useDefaultGestureRecognizer_extraInitializers);
|
|
101
|
+
__esDecorate(null, null, _onPlaybackStart_decorators, { kind: "field", name: "onPlaybackStart", static: false, private: false, access: { has: obj => "onPlaybackStart" in obj, get: obj => obj.onPlaybackStart, set: (obj, value) => { obj.onPlaybackStart = value; } }, metadata: _metadata }, _onPlaybackStart_initializers, _onPlaybackStart_extraInitializers);
|
|
102
|
+
__esDecorate(null, null, _onPlaybackStop_decorators, { kind: "field", name: "onPlaybackStop", static: false, private: false, access: { has: obj => "onPlaybackStop" in obj, get: obj => obj.onPlaybackStop, set: (obj, value) => { obj.onPlaybackStop = value; } }, metadata: _metadata }, _onPlaybackStop_initializers, _onPlaybackStop_extraInitializers);
|
|
103
|
+
__esDecorate(null, null, _onLoadStart_decorators, { kind: "field", name: "onLoadStart", static: false, private: false, access: { has: obj => "onLoadStart" in obj, get: obj => obj.onLoadStart, set: (obj, value) => { obj.onLoadStart = value; } }, metadata: _metadata }, _onLoadStart_initializers, _onLoadStart_extraInitializers);
|
|
104
|
+
__esDecorate(null, null, _onPreviewPhotoLoad_decorators, { kind: "field", name: "onPreviewPhotoLoad", static: false, private: false, access: { has: obj => "onPreviewPhotoLoad" in obj, get: obj => obj.onPreviewPhotoLoad, set: (obj, value) => { obj.onPreviewPhotoLoad = value; } }, metadata: _metadata }, _onPreviewPhotoLoad_initializers, _onPreviewPhotoLoad_extraInitializers);
|
|
105
|
+
__esDecorate(null, null, _onLoadComplete_decorators, { kind: "field", name: "onLoadComplete", static: false, private: false, access: { has: obj => "onLoadComplete" in obj, get: obj => obj.onLoadComplete, set: (obj, value) => { obj.onLoadComplete = value; } }, metadata: _metadata }, _onLoadComplete_initializers, _onLoadComplete_extraInitializers);
|
|
106
|
+
__esDecorate(null, null, _onLoadError_decorators, { kind: "field", name: "onLoadError", static: false, private: false, access: { has: obj => "onLoadError" in obj, get: obj => obj.onLoadError, set: (obj, value) => { obj.onLoadError = value; } }, metadata: _metadata }, _onLoadError_initializers, _onLoadError_extraInitializers);
|
|
107
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
108
|
+
LivePhotoView = _classThis = _classDescriptor.value;
|
|
109
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
110
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
111
|
+
}
|
|
112
|
+
source = __runInitializers(this, _source_initializers, void 0);
|
|
113
|
+
isMuted = (__runInitializers(this, _source_extraInitializers), __runInitializers(this, _isMuted_initializers, void 0));
|
|
114
|
+
contentFit = (__runInitializers(this, _isMuted_extraInitializers), __runInitializers(this, _contentFit_initializers, void 0));
|
|
115
|
+
useDefaultGestureRecognizer = (__runInitializers(this, _contentFit_extraInitializers), __runInitializers(this, _useDefaultGestureRecognizer_initializers, void 0));
|
|
116
|
+
onPlaybackStart = (__runInitializers(this, _useDefaultGestureRecognizer_extraInitializers), __runInitializers(this, _onPlaybackStart_initializers, void 0));
|
|
117
|
+
onPlaybackStop = (__runInitializers(this, _onPlaybackStart_extraInitializers), __runInitializers(this, _onPlaybackStop_initializers, void 0));
|
|
118
|
+
onLoadStart = (__runInitializers(this, _onPlaybackStop_extraInitializers), __runInitializers(this, _onLoadStart_initializers, void 0));
|
|
119
|
+
onPreviewPhotoLoad = (__runInitializers(this, _onLoadStart_extraInitializers), __runInitializers(this, _onPreviewPhotoLoad_initializers, void 0));
|
|
120
|
+
onLoadComplete = (__runInitializers(this, _onPreviewPhotoLoad_extraInitializers), __runInitializers(this, _onLoadComplete_initializers, void 0));
|
|
121
|
+
onLoadError = (__runInitializers(this, _onLoadComplete_extraInitializers), __runInitializers(this, _onLoadError_initializers, void 0));
|
|
122
|
+
handle = (__runInitializers(this, _onLoadError_extraInitializers), createLivePhotoViewHandle(() => this.hostNode()));
|
|
123
|
+
propNames = [
|
|
124
|
+
'source',
|
|
125
|
+
'isMuted',
|
|
126
|
+
'contentFit',
|
|
127
|
+
'useDefaultGestureRecognizer',
|
|
128
|
+
'onPlaybackStart',
|
|
129
|
+
'onPlaybackStop',
|
|
130
|
+
'onLoadStart',
|
|
131
|
+
'onPreviewPhotoLoad',
|
|
132
|
+
'onLoadComplete',
|
|
133
|
+
'onLoadError',
|
|
134
|
+
];
|
|
135
|
+
renderView(props) {
|
|
136
|
+
return renderLivePhotoView(props);
|
|
137
|
+
}
|
|
138
|
+
startPlayback(playbackStyle) {
|
|
139
|
+
this.handle.startPlayback(playbackStyle);
|
|
140
|
+
}
|
|
141
|
+
stopPlayback() {
|
|
142
|
+
this.handle.stopPlayback();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
return LivePhotoView = _classThis;
|
|
146
|
+
})();
|
|
147
|
+
export { LivePhotoView };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { LIVE_PHOTO_MODULE_NAME } from './constants';
|
|
2
|
+
export { createLivePhotoViewHandle, ensureLivePhotoViewRegistered, livePhotoViewName, renderLivePhotoView, } from './live-photo-view';
|
|
3
|
+
export type { ILivePhotoAsset, ILivePhotoContentFit, ILivePhotoLoadError, ILivePhotoPlaybackStyle, ILivePhotoViewHandle, ILivePhotoViewProps, } from './types';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IDescriptor } from '@symbiote-native/components';
|
|
2
|
+
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
3
|
+
import type { ILivePhotoViewHandle } from './types';
|
|
4
|
+
export declare const livePhotoViewName: () => string;
|
|
5
|
+
export declare function ensureLivePhotoViewRegistered(): boolean;
|
|
6
|
+
/** `null` means the caller renders nothing, live photos exist only on iOS */
|
|
7
|
+
export declare function renderLivePhotoView(props: object): IDescriptor | null;
|
|
8
|
+
export declare function createLivePhotoViewHandle(getNode: () => ISymbioteNode | null | undefined): ILivePhotoViewHandle;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Ядро `LivePhotoView`: нативный view с живыми фото, только iOS
|
|
2
|
+
import { Platform, UnavailabilityError, requireNativeModule, requireNativeViewManager, } from 'expo-modules-core';
|
|
3
|
+
import { el } from '@symbiote-native/components';
|
|
4
|
+
import { defineExpoNativeView, defineExpoViewMethods, getNativeTag, isDevBuild, } from '@symbiote-native/engine';
|
|
5
|
+
import { LIVE_PHOTO_MODULE_NAME, LIVE_PHOTO_PACKAGE_NAME } from './constants.js';
|
|
6
|
+
const LOAD_ERROR_PROP = 'onLoadError';
|
|
7
|
+
// Регистрация идёт при рендере: побочный эффект барреля теряется в release
|
|
8
|
+
const livePhotoView = defineExpoNativeView(requireNativeViewManager, LIVE_PHOTO_MODULE_NAME);
|
|
9
|
+
const callViewFunction = defineExpoViewMethods(requireNativeModule, LIVE_PHOTO_MODULE_NAME);
|
|
10
|
+
export const livePhotoViewName = livePhotoView.name;
|
|
11
|
+
function isAvailable() {
|
|
12
|
+
return Platform.select({ ios: true, default: false });
|
|
13
|
+
}
|
|
14
|
+
export function ensureLivePhotoViewRegistered() {
|
|
15
|
+
return isAvailable() && livePhotoView.ensureRegistered();
|
|
16
|
+
}
|
|
17
|
+
function isLoadError(value) {
|
|
18
|
+
return typeof Reflect.get(Object(value), 'message') === 'string';
|
|
19
|
+
}
|
|
20
|
+
// Нативное событие несёт полезную нагрузку в `nativeEvent`, наружу уходит она сама
|
|
21
|
+
function toLoadErrorHandler(onLoadError) {
|
|
22
|
+
return (event) => {
|
|
23
|
+
if (typeof onLoadError !== 'function')
|
|
24
|
+
return;
|
|
25
|
+
if (isLoadError(event.nativeEvent))
|
|
26
|
+
onLoadError(event.nativeEvent);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** `null` means the caller renders nothing, live photos exist only on iOS */
|
|
30
|
+
export function renderLivePhotoView(props) {
|
|
31
|
+
if (!ensureLivePhotoViewRegistered()) {
|
|
32
|
+
if (isDevBuild())
|
|
33
|
+
console.warn("'LivePhotoView' is not available.");
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return el(livePhotoViewName(), {
|
|
37
|
+
...Object.fromEntries(Object.entries(props)),
|
|
38
|
+
[LOAD_ERROR_PROP]: toLoadErrorHandler(Reflect.get(props, LOAD_ERROR_PROP)),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// A view that has not committed yet has no native tag, the call is dropped like upstream's
|
|
42
|
+
// `nativeRef.current?.` does
|
|
43
|
+
function callIfMounted(getNode, method, args) {
|
|
44
|
+
if (!isAvailable()) {
|
|
45
|
+
throw new UnavailabilityError(LIVE_PHOTO_PACKAGE_NAME, method);
|
|
46
|
+
}
|
|
47
|
+
const node = getNode();
|
|
48
|
+
if (node && getNativeTag(node) !== undefined) {
|
|
49
|
+
callViewFunction(node, method, args);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function createLivePhotoViewHandle(getNode) {
|
|
53
|
+
return {
|
|
54
|
+
startPlayback: (playbackStyle) => callIfMounted(getNode, 'startPlayback', [playbackStyle ?? 'full']),
|
|
55
|
+
stopPlayback: () => callIfMounted(getNode, 'stopPlayback', []),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { IAccessibilityProps, IAriaProps, IResponderProps } from '@symbiote-native/components';
|
|
2
|
+
import type { IStyleProp, ISymbioteEvent, IViewStyle } from '@symbiote-native/engine';
|
|
3
|
+
/**
|
|
4
|
+
* A live photo asset
|
|
5
|
+
*
|
|
6
|
+
* The photo and the video must come from one unaltered live photo file, the pairing lives in
|
|
7
|
+
* their metadata and native cannot rebuild it
|
|
8
|
+
*/
|
|
9
|
+
export type ILivePhotoAsset = {
|
|
10
|
+
/** The URI of the photo part of the live photo */
|
|
11
|
+
photoUri: string;
|
|
12
|
+
/** The URI of the video part of the live photo */
|
|
13
|
+
pairedVideoUri: string;
|
|
14
|
+
};
|
|
15
|
+
export type ILivePhotoLoadError = {
|
|
16
|
+
/** Reason for the load failure */
|
|
17
|
+
message: string;
|
|
18
|
+
};
|
|
19
|
+
/** `contain` fits the larger dimension to the target, `cover` fills the whole target */
|
|
20
|
+
export type ILivePhotoContentFit = 'contain' | 'cover';
|
|
21
|
+
/** `hint` plays a short part of the video, `full` plays all of it */
|
|
22
|
+
export type ILivePhotoPlaybackStyle = 'hint' | 'full';
|
|
23
|
+
export type ILivePhotoViewProps = IAccessibilityProps & IAriaProps & IResponderProps & {
|
|
24
|
+
/** The live photo asset to display */
|
|
25
|
+
source?: ILivePhotoAsset | null;
|
|
26
|
+
/** Whether the live photo plays without audio, `true` by default */
|
|
27
|
+
isMuted?: boolean;
|
|
28
|
+
/** `contain` by default */
|
|
29
|
+
contentFit?: ILivePhotoContentFit;
|
|
30
|
+
/** Playback starts on press and hold when `true`, which is the default */
|
|
31
|
+
useDefaultGestureRecognizer?: boolean;
|
|
32
|
+
onPlaybackStart?: () => void;
|
|
33
|
+
onPlaybackStop?: () => void;
|
|
34
|
+
onLoadStart?: () => void;
|
|
35
|
+
/** The preview photo is loaded */
|
|
36
|
+
onPreviewPhotoLoad?: () => void;
|
|
37
|
+
/** The live photo is loaded and ready to play */
|
|
38
|
+
onLoadComplete?: () => void;
|
|
39
|
+
onLoadError?: (error: ILivePhotoLoadError) => void;
|
|
40
|
+
style?: IStyleProp<IViewStyle>;
|
|
41
|
+
testID?: string;
|
|
42
|
+
nativeID?: string;
|
|
43
|
+
onLayout?: (event: ISymbioteEvent) => void;
|
|
44
|
+
};
|
|
45
|
+
/** What a ref to the view hands out */
|
|
46
|
+
export type ILivePhotoViewHandle = {
|
|
47
|
+
/** Plays the video part, the full video unless `playbackStyle` says `hint` */
|
|
48
|
+
startPlayback: (playbackStyle?: ILivePhotoPlaybackStyle) => void;
|
|
49
|
+
stopPlayback: () => void;
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { LivePhotoView } from './live-photo-view';
|
|
2
|
+
export type { ILivePhotoViewReactProps } from './live-photo-view';
|
|
3
|
+
export { LIVE_PHOTO_MODULE_NAME, livePhotoViewName, type ILivePhotoAsset, type ILivePhotoContentFit, type ILivePhotoLoadError, type ILivePhotoPlaybackStyle, type ILivePhotoViewHandle, type ILivePhotoViewProps, } from '../core';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReactElement, Ref } from 'react';
|
|
2
|
+
import type { ILivePhotoViewHandle, ILivePhotoViewProps } from '../core';
|
|
3
|
+
export type ILivePhotoViewReactProps = ILivePhotoViewProps & {
|
|
4
|
+
ref?: Ref<ILivePhotoViewHandle>;
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/** React twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
8
|
+
export declare function LivePhotoView({ ref, ...props }: ILivePhotoViewReactProps): ReactElement | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useImperativeHandle, useState } from 'react';
|
|
2
|
+
import { descriptorToReact } from '@symbiote-native/react';
|
|
3
|
+
import { createHostNodeHolder } from '@symbiote-native/components';
|
|
4
|
+
import { createLivePhotoViewHandle, renderLivePhotoView } from '../core/index.js';
|
|
5
|
+
/** React twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
6
|
+
export function LivePhotoView({ ref, ...props }) {
|
|
7
|
+
const [host] = useState(createHostNodeHolder);
|
|
8
|
+
useImperativeHandle(ref, () => createLivePhotoViewHandle(host.getNode), [host]);
|
|
9
|
+
const descriptor = renderLivePhotoView(props);
|
|
10
|
+
return descriptor ? descriptorToReact(host.capture(descriptor)) : null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { LivePhotoView } from './live-photo-view';
|
|
2
|
+
export type { ILivePhotoViewSolidProps } from './live-photo-view';
|
|
3
|
+
export { LIVE_PHOTO_MODULE_NAME, livePhotoViewName, type ILivePhotoAsset, type ILivePhotoContentFit, type ILivePhotoLoadError, type ILivePhotoPlaybackStyle, type ILivePhotoViewHandle, type ILivePhotoViewProps, } from '../core';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
2
|
+
import type { ILivePhotoViewHandle, ILivePhotoViewProps } from '../core';
|
|
3
|
+
export type ILivePhotoViewSolidProps = ILivePhotoViewProps & {
|
|
4
|
+
/** Gets the handle that starts and stops the playback */
|
|
5
|
+
ref?: (handle: ILivePhotoViewHandle) => void;
|
|
6
|
+
};
|
|
7
|
+
/** Solid twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
8
|
+
export declare function LivePhotoView(props: ILivePhotoViewSolidProps): ISymbioteNode | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { splitProps } from 'solid-js';
|
|
2
|
+
import { descriptorToSolid } from '@symbiote-native/solid';
|
|
3
|
+
import { createHostNodeHolder } from '@symbiote-native/components';
|
|
4
|
+
import { createLivePhotoViewHandle, renderLivePhotoView } from '../core/index.js';
|
|
5
|
+
/** Solid twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
6
|
+
export function LivePhotoView(props) {
|
|
7
|
+
const [own, viewProps] = splitProps(props, ['ref']);
|
|
8
|
+
const host = createHostNodeHolder();
|
|
9
|
+
const render = () => {
|
|
10
|
+
const descriptor = renderLivePhotoView({ ...viewProps });
|
|
11
|
+
return descriptor && host.capture(descriptor);
|
|
12
|
+
};
|
|
13
|
+
const initial = render();
|
|
14
|
+
if (!initial)
|
|
15
|
+
return null;
|
|
16
|
+
own.ref?.(createLivePhotoViewHandle(host.getNode));
|
|
17
|
+
return descriptorToSolid(() => render() ?? initial);
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// The native view name is mixed-case, which a literal tag would parse as a component reference,
|
|
3
|
+
// so the tag is dynamic
|
|
4
|
+
import {
|
|
5
|
+
hostInstance,
|
|
6
|
+
hostProps,
|
|
7
|
+
} from '@symbiote-native/svelte/native-view-bridge';
|
|
8
|
+
import { createLivePhotoViewHandle, renderLivePhotoView } from '../core';
|
|
9
|
+
import type { ILivePhotoPlaybackStyle, ILivePhotoViewProps } from '../core';
|
|
10
|
+
|
|
11
|
+
const props: ILivePhotoViewProps = $props();
|
|
12
|
+
const descriptor = $derived(renderLivePhotoView({ ...props }));
|
|
13
|
+
let shim: unknown = $state(null);
|
|
14
|
+
const handle = createLivePhotoViewHandle(() => hostInstance(shim));
|
|
15
|
+
|
|
16
|
+
export function startPlayback(playbackStyle?: ILivePhotoPlaybackStyle): void {
|
|
17
|
+
handle.startPlayback(playbackStyle);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function stopPlayback(): void {
|
|
21
|
+
handle.stopPlayback();
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
{#if descriptor}
|
|
26
|
+
<svelte:element
|
|
27
|
+
this={descriptor.type}
|
|
28
|
+
bind:this={shim}
|
|
29
|
+
{@attach hostProps(descriptor.props)}
|
|
30
|
+
/>
|
|
31
|
+
{/if}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ILivePhotoPlaybackStyle, ILivePhotoViewProps } from '../core';
|
|
2
|
+
declare const LivePhotoView: import("svelte").Component<ILivePhotoViewProps, {
|
|
3
|
+
startPlayback: (playbackStyle?: ILivePhotoPlaybackStyle) => void;
|
|
4
|
+
stopPlayback: () => void;
|
|
5
|
+
}, "">;
|
|
6
|
+
type LivePhotoView = ReturnType<typeof LivePhotoView>;
|
|
7
|
+
export default LivePhotoView;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { LivePhotoView } from './live-photo-view';
|
|
2
|
+
export { LIVE_PHOTO_MODULE_NAME, livePhotoViewName, type ILivePhotoAsset, type ILivePhotoContentFit, type ILivePhotoLoadError, type ILivePhotoPlaybackStyle, type ILivePhotoViewHandle, type ILivePhotoViewProps, } from '../core';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type VNode } from '@vue/runtime-core';
|
|
2
|
+
/** Vue twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
3
|
+
export declare const LivePhotoView: import("vue").DefineComponent<{}, () => VNode | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineComponent } from '@vue/runtime-core';
|
|
2
|
+
import { descriptorToVue, normalizeVueAttrs } from '@symbiote-native/vue';
|
|
3
|
+
import { createHostNodeHolder } from '@symbiote-native/components';
|
|
4
|
+
import { createLivePhotoViewHandle, renderLivePhotoView } from '../core/index.js';
|
|
5
|
+
/** Vue twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
6
|
+
export const LivePhotoView = defineComponent({
|
|
7
|
+
name: 'LivePhotoView',
|
|
8
|
+
// Все атрибуты это пропсы нативного view, на обёртку ничего не должно просочиться
|
|
9
|
+
inheritAttrs: false,
|
|
10
|
+
setup(_props, { attrs, expose }) {
|
|
11
|
+
const host = createHostNodeHolder();
|
|
12
|
+
expose(createLivePhotoViewHandle(host.getNode));
|
|
13
|
+
return () => {
|
|
14
|
+
const descriptor = renderLivePhotoView(normalizeVueAttrs(attrs));
|
|
15
|
+
return descriptor ? descriptorToVue(host.capture(descriptor)) : null;
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { LivePhotoView } from './live-photo-view';
|
|
2
|
+
export { LIVE_PHOTO_MODULE_NAME, livePhotoViewName, type ILivePhotoAsset, type ILivePhotoContentFit, type ILivePhotoLoadError, type ILivePhotoPlaybackStyle, type ILivePhotoViewHandle, type ILivePhotoViewProps, } from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EACL,sBAAsB,EACtB,iBAAiB,GAOlB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NativeViewBase } from '@symbiote-native/angular';
|
|
2
|
+
import type { IDescriptor } from '@symbiote-native/components';
|
|
3
|
+
import type { ILivePhotoPlaybackStyle, ILivePhotoViewProps } from '../../core';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/** Angular twin of `expo-live-photo`'s `LivePhotoView`, iOS only */
|
|
6
|
+
export declare class LivePhotoView extends NativeViewBase {
|
|
7
|
+
source?: ILivePhotoViewProps['source'];
|
|
8
|
+
isMuted?: ILivePhotoViewProps['isMuted'];
|
|
9
|
+
contentFit?: ILivePhotoViewProps['contentFit'];
|
|
10
|
+
useDefaultGestureRecognizer?: ILivePhotoViewProps['useDefaultGestureRecognizer'];
|
|
11
|
+
onPlaybackStart?: ILivePhotoViewProps['onPlaybackStart'];
|
|
12
|
+
onPlaybackStop?: ILivePhotoViewProps['onPlaybackStop'];
|
|
13
|
+
onLoadStart?: ILivePhotoViewProps['onLoadStart'];
|
|
14
|
+
onPreviewPhotoLoad?: ILivePhotoViewProps['onPreviewPhotoLoad'];
|
|
15
|
+
onLoadComplete?: ILivePhotoViewProps['onLoadComplete'];
|
|
16
|
+
onLoadError?: ILivePhotoViewProps['onLoadError'];
|
|
17
|
+
private readonly handle;
|
|
18
|
+
protected readonly propNames: readonly ["source", "isMuted", "contentFit", "useDefaultGestureRecognizer", "onPlaybackStart", "onPlaybackStop", "onLoadStart", "onPreviewPhotoLoad", "onLoadComplete", "onLoadError"];
|
|
19
|
+
protected renderView(props: object): IDescriptor | null;
|
|
20
|
+
startPlayback(playbackStyle?: ILivePhotoPlaybackStyle): void;
|
|
21
|
+
stopPlayback(): void;
|
|
22
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LivePhotoView, never>;
|
|
23
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LivePhotoView, "LivePhotoView", never, { "source": { "alias": "source"; "required": false; }; "isMuted": { "alias": "isMuted"; "required": false; }; "contentFit": { "alias": "contentFit"; "required": false; }; "useDefaultGestureRecognizer": { "alias": "useDefaultGestureRecognizer"; "required": false; }; "onPlaybackStart": { "alias": "onPlaybackStart"; "required": false; }; "onPlaybackStop": { "alias": "onPlaybackStop"; "required": false; }; "onLoadStart": { "alias": "onLoadStart"; "required": false; }; "onPreviewPhotoLoad": { "alias": "onPreviewPhotoLoad"; "required": false; }; "onLoadComplete": { "alias": "onLoadComplete"; "required": false; }; "onLoadError": { "alias": "onLoadError"; "required": false; }; }, {}, never, never, true, never>;
|
|
24
|
+
}
|