@oxyhq/bloom 1.1.0 → 1.2.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 (150) hide show
  1. package/docs/media-flight.mdx +272 -0
  2. package/docs/migrating-the-media-gallery.mdx +103 -0
  3. package/docs/migrating-to-1.0.mdx +1 -1
  4. package/docs/zoomable-media-gallery.mdx +116 -0
  5. package/lib/commonjs/image-aspect-ratio-cache/aspect-ratio-cache.js +1 -1
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/index.web.js.map +1 -1
  8. package/lib/commonjs/media-flight/MediaFlightLayer.js +151 -0
  9. package/lib/commonjs/media-flight/MediaFlightLayer.js.map +1 -0
  10. package/lib/commonjs/media-flight/MediaSurface.js +188 -0
  11. package/lib/commonjs/media-flight/MediaSurface.js.map +1 -0
  12. package/lib/commonjs/media-flight/constants.js +39 -0
  13. package/lib/commonjs/media-flight/constants.js.map +1 -0
  14. package/lib/commonjs/media-flight/expo-video-module.js +148 -0
  15. package/lib/commonjs/media-flight/expo-video-module.js.map +1 -0
  16. package/lib/commonjs/media-flight/index.js +102 -0
  17. package/lib/commonjs/media-flight/index.js.map +1 -0
  18. package/lib/commonjs/media-flight/store.js +457 -0
  19. package/lib/commonjs/media-flight/store.js.map +1 -0
  20. package/lib/commonjs/media-flight/types.js +6 -0
  21. package/lib/commonjs/media-flight/types.js.map +1 -0
  22. package/lib/commonjs/media-flight/use-media-flight.js +41 -0
  23. package/lib/commonjs/media-flight/use-media-flight.js.map +1 -0
  24. package/lib/commonjs/tab-bar/TabBarBase.js +1 -1
  25. package/lib/commonjs/{zoomable-image-gallery/ZoomableImageGallery.js → zoomable-media-gallery/ZoomableMediaGallery.js} +185 -131
  26. package/lib/commonjs/zoomable-media-gallery/ZoomableMediaGallery.js.map +1 -0
  27. package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.js.map +1 -1
  28. package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/index.js +5 -5
  29. package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/index.js.map +1 -1
  30. package/lib/commonjs/zoomable-media-gallery/types.js +6 -0
  31. package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/types.js.map +1 -1
  32. package/lib/module/image-aspect-ratio-cache/aspect-ratio-cache.js +1 -1
  33. package/lib/module/index.js +2 -1
  34. package/lib/module/index.js.map +1 -1
  35. package/lib/module/index.web.js +2 -1
  36. package/lib/module/index.web.js.map +1 -1
  37. package/lib/module/media-flight/MediaFlightLayer.js +146 -0
  38. package/lib/module/media-flight/MediaFlightLayer.js.map +1 -0
  39. package/lib/module/media-flight/MediaSurface.js +180 -0
  40. package/lib/module/media-flight/MediaSurface.js.map +1 -0
  41. package/lib/module/media-flight/constants.js +35 -0
  42. package/lib/module/media-flight/constants.js.map +1 -0
  43. package/lib/module/media-flight/expo-video-module.js +142 -0
  44. package/lib/module/media-flight/expo-video-module.js.map +1 -0
  45. package/lib/module/media-flight/index.js +9 -0
  46. package/lib/module/media-flight/index.js.map +1 -0
  47. package/lib/module/media-flight/store.js +439 -0
  48. package/lib/module/media-flight/store.js.map +1 -0
  49. package/lib/module/media-flight/types.js +4 -0
  50. package/lib/module/media-flight/types.js.map +1 -0
  51. package/lib/module/media-flight/use-media-flight.js +36 -0
  52. package/lib/module/media-flight/use-media-flight.js.map +1 -0
  53. package/lib/module/tab-bar/TabBarBase.js +1 -1
  54. package/lib/module/{zoomable-image-gallery/ZoomableImageGallery.js → zoomable-media-gallery/ZoomableMediaGallery.js} +184 -130
  55. package/lib/module/zoomable-media-gallery/ZoomableMediaGallery.js.map +1 -0
  56. package/lib/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.js.map +1 -1
  57. package/lib/module/zoomable-media-gallery/index.js +5 -0
  58. package/lib/module/zoomable-media-gallery/index.js.map +1 -0
  59. package/lib/module/zoomable-media-gallery/types.js +4 -0
  60. package/lib/module/{zoomable-image-gallery → zoomable-media-gallery}/types.js.map +1 -1
  61. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  63. package/lib/typescript/commonjs/media-flight/MediaFlightLayer.d.ts +11 -0
  64. package/lib/typescript/commonjs/media-flight/MediaFlightLayer.d.ts.map +1 -0
  65. package/lib/typescript/commonjs/media-flight/MediaSurface.d.ts +106 -0
  66. package/lib/typescript/commonjs/media-flight/MediaSurface.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/media-flight/constants.d.ts +18 -0
  68. package/lib/typescript/commonjs/media-flight/constants.d.ts.map +1 -0
  69. package/lib/typescript/commonjs/media-flight/expo-video-module.d.ts +103 -0
  70. package/lib/typescript/commonjs/media-flight/expo-video-module.d.ts.map +1 -0
  71. package/lib/typescript/commonjs/media-flight/index.d.ts +9 -0
  72. package/lib/typescript/commonjs/media-flight/index.d.ts.map +1 -0
  73. package/lib/typescript/commonjs/media-flight/store.d.ts +132 -0
  74. package/lib/typescript/commonjs/media-flight/store.d.ts.map +1 -0
  75. package/lib/typescript/commonjs/media-flight/types.d.ts +195 -0
  76. package/lib/typescript/commonjs/media-flight/types.d.ts.map +1 -0
  77. package/lib/typescript/commonjs/media-flight/use-media-flight.d.ts +4 -0
  78. package/lib/typescript/commonjs/media-flight/use-media-flight.d.ts.map +1 -0
  79. package/lib/typescript/commonjs/zoomable-media-gallery/ZoomableMediaGallery.d.ts +5 -0
  80. package/lib/typescript/commonjs/zoomable-media-gallery/ZoomableMediaGallery.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/zoomable-media-gallery/index.d.ts +4 -0
  83. package/lib/typescript/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/index.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/zoomable-media-gallery/types.d.ts +90 -0
  85. package/lib/typescript/commonjs/zoomable-media-gallery/types.d.ts.map +1 -0
  86. package/lib/typescript/module/index.d.ts.map +1 -1
  87. package/lib/typescript/module/index.web.d.ts.map +1 -1
  88. package/lib/typescript/module/media-flight/MediaFlightLayer.d.ts +11 -0
  89. package/lib/typescript/module/media-flight/MediaFlightLayer.d.ts.map +1 -0
  90. package/lib/typescript/module/media-flight/MediaSurface.d.ts +106 -0
  91. package/lib/typescript/module/media-flight/MediaSurface.d.ts.map +1 -0
  92. package/lib/typescript/module/media-flight/constants.d.ts +18 -0
  93. package/lib/typescript/module/media-flight/constants.d.ts.map +1 -0
  94. package/lib/typescript/module/media-flight/expo-video-module.d.ts +103 -0
  95. package/lib/typescript/module/media-flight/expo-video-module.d.ts.map +1 -0
  96. package/lib/typescript/module/media-flight/index.d.ts +9 -0
  97. package/lib/typescript/module/media-flight/index.d.ts.map +1 -0
  98. package/lib/typescript/module/media-flight/store.d.ts +132 -0
  99. package/lib/typescript/module/media-flight/store.d.ts.map +1 -0
  100. package/lib/typescript/module/media-flight/types.d.ts +195 -0
  101. package/lib/typescript/module/media-flight/types.d.ts.map +1 -0
  102. package/lib/typescript/module/media-flight/use-media-flight.d.ts +4 -0
  103. package/lib/typescript/module/media-flight/use-media-flight.d.ts.map +1 -0
  104. package/lib/typescript/module/zoomable-media-gallery/ZoomableMediaGallery.d.ts +5 -0
  105. package/lib/typescript/module/zoomable-media-gallery/ZoomableMediaGallery.d.ts.map +1 -0
  106. package/lib/typescript/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts.map +1 -1
  107. package/lib/typescript/module/zoomable-media-gallery/index.d.ts +4 -0
  108. package/lib/typescript/module/{zoomable-image-gallery → zoomable-media-gallery}/index.d.ts.map +1 -1
  109. package/lib/typescript/module/zoomable-media-gallery/types.d.ts +90 -0
  110. package/lib/typescript/module/zoomable-media-gallery/types.d.ts.map +1 -0
  111. package/package.json +42 -8
  112. package/src/image-aspect-ratio-cache/aspect-ratio-cache.ts +1 -1
  113. package/src/index.ts +2 -1
  114. package/src/index.web.ts +2 -1
  115. package/src/media-flight/MediaFlightLayer.tsx +144 -0
  116. package/src/media-flight/MediaSurface.tsx +233 -0
  117. package/src/media-flight/constants.ts +32 -0
  118. package/src/media-flight/expo-video-module.ts +181 -0
  119. package/src/media-flight/index.ts +33 -0
  120. package/src/media-flight/store.ts +497 -0
  121. package/src/media-flight/types.ts +210 -0
  122. package/src/media-flight/use-media-flight.ts +42 -0
  123. package/src/tab-bar/TabBarBase.tsx +1 -1
  124. package/src/{zoomable-image-gallery/ZoomableImageGallery.tsx → zoomable-media-gallery/ZoomableMediaGallery.tsx} +192 -117
  125. package/src/zoomable-media-gallery/index.ts +11 -0
  126. package/src/zoomable-media-gallery/types.ts +100 -0
  127. package/docs/zoomable-image-gallery.mdx +0 -66
  128. package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +0 -1
  129. package/lib/commonjs/zoomable-image-gallery/types.js +0 -2
  130. package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +0 -1
  131. package/lib/module/zoomable-image-gallery/index.js +0 -5
  132. package/lib/module/zoomable-image-gallery/index.js.map +0 -1
  133. package/lib/module/zoomable-image-gallery/types.js +0 -2
  134. package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts +0 -5
  135. package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +0 -1
  136. package/lib/typescript/commonjs/zoomable-image-gallery/index.d.ts +0 -4
  137. package/lib/typescript/commonjs/zoomable-image-gallery/types.d.ts +0 -56
  138. package/lib/typescript/commonjs/zoomable-image-gallery/types.d.ts.map +0 -1
  139. package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts +0 -5
  140. package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +0 -1
  141. package/lib/typescript/module/zoomable-image-gallery/index.d.ts +0 -4
  142. package/lib/typescript/module/zoomable-image-gallery/types.d.ts +0 -56
  143. package/lib/typescript/module/zoomable-image-gallery/types.d.ts.map +0 -1
  144. package/src/zoomable-image-gallery/index.ts +0 -9
  145. package/src/zoomable-image-gallery/types.ts +0 -59
  146. /package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.js +0 -0
  147. /package/lib/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.js +0 -0
  148. /package/lib/typescript/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts +0 -0
  149. /package/lib/typescript/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts +0 -0
  150. /package/src/{zoomable-image-gallery → zoomable-media-gallery}/constants.ts +0 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The optional-peer boundary for `expo-video`.
3
+ *
4
+ * Bloom ships no native code and never will, so `expo-video` cannot be a hard
5
+ * dependency: an app that renders no video must not be made to install a native
6
+ * module. It is therefore an OPTIONAL peer, and a static `import` contradicts
7
+ * that — Metro resolves every static import in the eager graph, so an omitted
8
+ * "optional" peer does not degrade, it aborts the whole bundle with `Unable to
9
+ * resolve module expo-video`, naming a package the app never mentions.
10
+ *
11
+ * The load has to be dynamic in the ONE shape Metro understands: a `require()`
12
+ * of a STRING LITERAL as a DIRECT STATEMENT of a `try` block, which Metro
13
+ * collects as an optional dependency (resolving the real module when installed,
14
+ * writing `null` into the dependency map when it is not, so the failure lands in
15
+ * the `catch` below at evaluation time instead of at build time). One `if` of
16
+ * nesting loses that — `isOptionalDependency` returns at the FIRST BlockStatement
17
+ * it meets — hence the `typeof require` guard sits OUTSIDE the try.
18
+ *
19
+ * `require` may also not exist at all (this file backs the `import` condition
20
+ * too, i.e. an ESM build under Node), hence that guard.
21
+ *
22
+ * A missing video module is invisible — a poster where a video should be looks
23
+ * exactly like a video that has not started — so {@link warnExpoVideoUnavailable}
24
+ * names the package once, in dev.
25
+ *
26
+ * @see connection-status/netinfo.ts — the same boundary for netinfo.
27
+ * @see hooks/haptics-module.ts — the same boundary for expo-haptics.
28
+ */
29
+ import type { ComponentType } from 'react';
30
+ import type { StyleProp, ViewStyle } from 'react-native';
31
+
32
+ /**
33
+ * Declared locally rather than taken from the ambient `NodeRequire`, which
34
+ * returns `any`: `unknown` forces the module handle to be narrowed below instead
35
+ * of leaking an untyped value into the caller.
36
+ */
37
+ declare const require: (moduleName: string) => unknown;
38
+
39
+ /**
40
+ * The slice of `expo-video`'s `VideoPlayer` Bloom depends on.
41
+ *
42
+ * Hand-written rather than `typeof import('expo-video')` so no expo-video type
43
+ * reaches Bloom's emitted declarations — a consumer that skips the optional peer
44
+ * must not inherit a TS7016 from Bloom's own `.d.ts`. The two are kept in step by
45
+ * the assignability check in `__tests__/MediaFlight.test.tsx`, which is excluded
46
+ * from the build and so may name the real package.
47
+ *
48
+ * Bloom never CALLS any of this. **The consumer creates the player, owns it and
49
+ * destroys it**; Bloom only hands the object back to a `VideoView`, which is the
50
+ * whole mechanism that lets a video move between surfaces without restarting
51
+ * (expo-video's own docs: "when the same player is playing in multiple video
52
+ * views"). The three members below exist so the type is not `object` — a
53
+ * structural minimum the real `VideoPlayer` satisfies, and that an arbitrary
54
+ * object does not.
55
+ *
56
+ * `play`/`pause` are declared in METHOD syntax deliberately: `strictFunctionTypes`
57
+ * exempts method parameters from contravariance, which keeps a real
58
+ * `VideoPlayer` assignable here across `@types/react` copies.
59
+ */
60
+ export interface VideoPlayerLike {
61
+ readonly playing: boolean;
62
+ play(): void;
63
+ pause(): void;
64
+ }
65
+
66
+ /** How a video is scaled inside its box. Mirrors expo-video's `VideoContentFit`. */
67
+ export type VideoSurfaceContentFit = 'contain' | 'cover' | 'fill';
68
+
69
+ /**
70
+ * Android's rendering surface for a video view.
71
+ *
72
+ * `textureView` is what Bloom asks for everywhere, because a `SurfaceView` is
73
+ * composited by the system OUTSIDE the app's view hierarchy: it ignores the
74
+ * parent's clip, corner radius and transform, so a video flying between two
75
+ * rects would paint as an un-rounded rectangle at the wrong place. It also
76
+ * cannot be changed at runtime (expo-video: "This prop should not be changed at
77
+ * runtime"), which is why {@link MediaSurfaceProps.surfaceType} is captured on
78
+ * first render.
79
+ */
80
+ export type VideoSurfaceType = 'textureView' | 'surfaceView';
81
+
82
+ /** The slice of `VideoViewProps` Bloom passes. */
83
+ export interface VideoViewLikeProps {
84
+ player?: VideoPlayerLike | null;
85
+ contentFit?: VideoSurfaceContentFit;
86
+ surfaceType?: VideoSurfaceType;
87
+ nativeControls?: boolean;
88
+ style?: StyleProp<ViewStyle>;
89
+ pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only';
90
+ accessibilityLabel?: string;
91
+ /**
92
+ * Raised once the mounted player has rendered its first frame into this view.
93
+ * On web expo-video raises it from the element's `loadeddata` event; it is the
94
+ * only OBSERVABLE "this surface is live now" the package offers, and the media
95
+ * flight layer's hand-off depends on it rather than on a timer.
96
+ */
97
+ onFirstFrameRender?: () => void;
98
+ }
99
+
100
+ /** The slice of expo-video's module surface Bloom renders. */
101
+ export interface ExpoVideoLike {
102
+ VideoView: ComponentType<VideoViewLikeProps>;
103
+ }
104
+
105
+ /** `undefined` until the first load attempt, then the module or `null`. */
106
+ let videoModule: ExpoVideoLike | null | undefined;
107
+ /** Why the load failed, quoted verbatim in the dev warning. */
108
+ let unavailableReason = '';
109
+ let hasWarned = false;
110
+
111
+ /** The expo-video module, or `null` when the optional peer is not installed. */
112
+ export function loadExpoVideo(): ExpoVideoLike | null {
113
+ if (videoModule !== undefined) return videoModule;
114
+ videoModule = null;
115
+
116
+ // The `typeof require` guard sits OUTSIDE the try on purpose — see the header.
117
+ // Nested one `if`/`else` deeper, the require below is no longer a direct
118
+ // statement of the try block, Metro stops marking it optional, and an app
119
+ // without expo-video fails to BUILD instead of losing the video surface.
120
+ if (typeof require === 'undefined') {
121
+ unavailableReason = 'this bundle has no CommonJS `require`';
122
+ return videoModule;
123
+ }
124
+
125
+ try {
126
+ const loaded = require('expo-video') as
127
+ | (Partial<ExpoVideoLike> & { default?: Partial<ExpoVideoLike> })
128
+ | null
129
+ | undefined;
130
+ // `VideoView` is a class component upstream, so `typeof` is `'function'`
131
+ // either way; a memo/forwardRef build would be an object carrying
132
+ // `$$typeof`. Both are accepted, nothing else is.
133
+ const resolved = isComponentBearing(loaded) ? loaded : loaded?.default;
134
+
135
+ if (isComponentBearing(resolved)) {
136
+ videoModule = { VideoView: resolved.VideoView as ComponentType<VideoViewLikeProps> };
137
+ } else {
138
+ unavailableReason = 'the module resolved without a `VideoView` export';
139
+ }
140
+ } catch (error) {
141
+ unavailableReason = error instanceof Error ? error.message : String(error);
142
+ }
143
+
144
+ return videoModule;
145
+ }
146
+
147
+ function isComponentBearing(
148
+ candidate: (Partial<ExpoVideoLike> & { default?: unknown }) | null | undefined,
149
+ ): candidate is Partial<ExpoVideoLike> & { VideoView: ComponentType<VideoViewLikeProps> } {
150
+ const view = candidate?.VideoView;
151
+ if (typeof view === 'function') return true;
152
+ return typeof view === 'object' && view !== null && '$$typeof' in view;
153
+ }
154
+
155
+ /**
156
+ * One warning per module lifetime, and none in production — the same mechanism
157
+ * as `connection-status/netinfo.ts` and `hooks/haptics-module.ts`. Metro and
158
+ * Vite/Rolldown both fold the `NODE_ENV` check statically, so a production
159
+ * bundle keeps neither the branch nor the message.
160
+ */
161
+ export function warnExpoVideoUnavailable(): void {
162
+ if (process.env.NODE_ENV === 'production' || hasWarned) return;
163
+ hasWarned = true;
164
+ // Internal Bloom diagnostic: only the consumer's package.json can fix this,
165
+ // so it names the package, the install command and what is lost.
166
+ // eslint-disable-next-line no-console
167
+ console.warn(
168
+ '[Bloom] A video media surface fell back to its poster: the optional peer ' +
169
+ '`expo-video` could not be loaded, so no video is rendered and the shared ' +
170
+ 'media transition carries a still frame instead. Install it ' +
171
+ '(`npx expo install expo-video`) or pass image content only. ' +
172
+ `Reason: ${unavailableReason}`,
173
+ );
174
+ }
175
+
176
+ /** Test seam — drops the cached module handle so a suite can load it again. */
177
+ export function resetExpoVideoModule(): void {
178
+ videoModule = undefined;
179
+ unavailableReason = '';
180
+ hasWarned = false;
181
+ }
@@ -0,0 +1,33 @@
1
+ export { MediaFlightLayer } from './MediaFlightLayer';
2
+ export { MediaSurface, MediaPoster, EmptyMediaSurface } from './MediaSurface';
3
+ export { useMediaFlight } from './use-media-flight';
4
+ export {
5
+ DEFAULT_FLIGHT_CORNER_RADIUS,
6
+ flightProgress,
7
+ handOffFlight,
8
+ hasFlight,
9
+ releaseFlight,
10
+ resetMediaFlight,
11
+ } from './store';
12
+ export { SURFACE_MOUNT_TIMEOUT_MS } from './constants';
13
+ export {
14
+ loadExpoVideo,
15
+ warnExpoVideoUnavailable,
16
+ resetExpoVideoModule,
17
+ type ExpoVideoLike,
18
+ type VideoPlayerLike,
19
+ type VideoSurfaceContentFit,
20
+ type VideoSurfaceType,
21
+ type VideoViewLikeProps,
22
+ } from './expo-video-module';
23
+ export type {
24
+ MediaFlight,
25
+ MediaFlightAnchorNode,
26
+ MediaFlightController,
27
+ MediaFlightOptions,
28
+ MediaSurfaceContent,
29
+ MediaSurfaceImage,
30
+ MediaSurfaceVideo,
31
+ MeasuredRect,
32
+ } from './types';
33
+ export type { MediaSurfaceProps } from './MediaSurface';