@movementinfra/expo-twostep-video 0.1.9 → 0.1.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"VideoScrubber.d.ts","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAsB,MAAM,uBAAuB,CAAC;AAYjG;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,aAAa,6FA0bjB,CAAC;AAuGH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"VideoScrubber.d.ts","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAsB,MAAM,uBAAuB,CAAC;AAYjG;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,aAAa,6FAkdjB,CAAC;AAuGH,eAAe,aAAa,CAAC"}
@@ -40,6 +40,7 @@ const VideoScrubber = forwardRef((props, ref) => {
40
40
  const currentTimeRef = useRef(currentTime);
41
41
  const durationRef = useRef(duration);
42
42
  const containerWidthRef = useRef(containerWidth);
43
+ const handleWidthRef = useRef(handleWidth);
43
44
  // Sync refs with props when they change from outside (e.g., new video loaded)
44
45
  useEffect(() => {
45
46
  if (!dragging) {
@@ -57,6 +58,9 @@ const VideoScrubber = forwardRef((props, ref) => {
57
58
  useEffect(() => {
58
59
  containerWidthRef.current = containerWidth;
59
60
  }, [containerWidth]);
61
+ useEffect(() => {
62
+ handleWidthRef.current = handleWidth;
63
+ }, [handleWidth]);
60
64
  // Calculate thumbnail times
61
65
  const thumbnailTimes = useMemo(() => {
62
66
  if (duration <= 0)
@@ -91,12 +95,14 @@ const VideoScrubber = forwardRef((props, ref) => {
91
95
  cancelled = true;
92
96
  };
93
97
  }, [assetId, thumbnailTimes, thumbnailHeight, duration]);
94
- // Convert time to position and vice versa
98
+ // The content area is between the handles
99
+ const contentWidth = containerWidth > 0 ? containerWidth - handleWidth * 2 : 0;
100
+ // Convert time to position within the content area (between handles)
95
101
  const timeToPosition = useCallback((time) => {
96
- if (duration <= 0 || containerWidth <= 0)
97
- return 0;
98
- return (time / duration) * containerWidth;
99
- }, [duration, containerWidth]);
102
+ if (duration <= 0 || contentWidth <= 0)
103
+ return handleWidth;
104
+ return handleWidth + (time / duration) * contentWidth;
105
+ }, [duration, contentWidth, handleWidth]);
100
106
  // Calculate positions
101
107
  const startPosition = timeToPosition(startTime);
102
108
  const endPosition = timeToPosition(endTime);
@@ -149,12 +155,15 @@ const VideoScrubber = forwardRef((props, ref) => {
149
155
  setDragging(type);
150
156
  },
151
157
  onPanResponderMove: (_e, gestureState) => {
152
- const width = containerWidthRef.current;
158
+ const containerW = containerWidthRef.current;
159
+ const hw = handleWidthRef.current;
153
160
  const dur = durationRef.current;
154
- if (width <= 0 || dur <= 0)
161
+ // Content width is where time is mapped (between handles)
162
+ const cw = containerW - hw * 2;
163
+ if (cw <= 0 || dur <= 0)
155
164
  return;
156
165
  const deltaX = gestureState.dx;
157
- const deltaTime = (deltaX / width) * dur;
166
+ const deltaTime = (deltaX / cw) * dur;
158
167
  let newTime = initialTime + deltaTime;
159
168
  if (type === 'start') {
160
169
  // Constrain start handle
@@ -186,10 +195,12 @@ const VideoScrubber = forwardRef((props, ref) => {
186
195
  onScrubEndRef.current?.(dragEndTimeRef.current);
187
196
  }
188
197
  else {
189
- const width = containerWidthRef.current;
198
+ const containerW = containerWidthRef.current;
199
+ const hw = handleWidthRef.current;
190
200
  const dur = durationRef.current;
191
- if (width > 0 && dur > 0) {
192
- const deltaTime = (gestureState.dx / width) * dur;
201
+ const cw = containerW - hw * 2;
202
+ if (cw > 0 && dur > 0) {
203
+ const deltaTime = (gestureState.dx / cw) * dur;
193
204
  const newTime = Math.max(dragStartTimeRef.current, Math.min(initialTime + deltaTime, dragEndTimeRef.current));
194
205
  onScrubEndRef.current?.(newTime);
195
206
  }
@@ -204,20 +215,31 @@ const VideoScrubber = forwardRef((props, ref) => {
204
215
  const startPanResponder = useMemo(() => createPanResponder('start'), [createPanResponder]);
205
216
  const endPanResponder = useMemo(() => createPanResponder('end'), [createPanResponder]);
206
217
  const playheadPanResponder = useMemo(() => createPanResponder('playhead'), [createPanResponder]);
207
- // Tap to seek
218
+ // Tap to seek - locationX is relative to the tap area which spans from startPosition to endPosition
208
219
  const handleTap = useCallback((e) => {
209
220
  if (disabled)
210
221
  return;
211
222
  const { locationX } = e.nativeEvent;
223
+ const startT = dragStartTimeRef.current;
224
+ const endT = dragEndTimeRef.current;
225
+ const selectionDuration = endT - startT;
226
+ if (selectionDuration <= 0)
227
+ return;
228
+ // The tap area spans the selection, so locationX=0 is startTime and locationX=width is endTime
229
+ // Calculate time proportionally within the selection
212
230
  const width = containerWidthRef.current;
213
231
  const dur = durationRef.current;
214
232
  if (width <= 0 || dur <= 0)
215
233
  return;
216
- const time = Math.max(0, Math.min(dur, (locationX / width) * dur));
217
- // Only seek within selection
218
- const clampedTime = Math.max(dragStartTimeRef.current, Math.min(time, dragEndTimeRef.current));
219
- onScrubRef.current?.(clampedTime);
220
- }, [disabled]);
234
+ // Calculate the width of the tap area (endPosition - startPosition in content coordinates)
235
+ const cw = width - handleWidth * 2; // contentWidth
236
+ const tapAreaWidth = ((endT - startT) / dur) * cw;
237
+ if (tapAreaWidth <= 0)
238
+ return;
239
+ const proportion = Math.max(0, Math.min(1, locationX / tapAreaWidth));
240
+ const time = startT + proportion * selectionDuration;
241
+ onScrubRef.current?.(time);
242
+ }, [disabled, handleWidth]);
221
243
  // Expose ref methods
222
244
  useImperativeHandle(ref, () => ({
223
245
  regenerateThumbnails: async () => {
@@ -240,16 +262,16 @@ const VideoScrubber = forwardRef((props, ref) => {
240
262
  },
241
263
  getSelection: () => ({ startTime, endTime }),
242
264
  }));
243
- const thumbnailWidth = containerWidth > 0 && thumbnails.length > 0
244
- ? containerWidth / thumbnails.length
265
+ const thumbnailWidth = contentWidth > 0 && thumbnails.length > 0
266
+ ? contentWidth / thumbnails.length
245
267
  : 0;
246
268
  return (<View style={[
247
269
  styles.container,
248
270
  { backgroundColor: theme.backgroundColor },
249
271
  style,
250
272
  ]} onLayout={handleLayout}>
251
- {/* Thumbnail Strip */}
252
- <View style={[styles.thumbnailStrip, { height: thumbnailHeight }]}>
273
+ {/* Thumbnail Strip - positioned between handles */}
274
+ <View style={[styles.thumbnailStrip, { height: thumbnailHeight, marginLeft: handleWidth, width: contentWidth }]}>
253
275
  {thumbnails.map((thumb, index) => (<Image key={index} source={{ uri: `data:image/png;base64,${thumb}` }} style={[
254
276
  styles.thumbnail,
255
277
  {
@@ -260,35 +282,35 @@ const VideoScrubber = forwardRef((props, ref) => {
260
282
  {isLoading && thumbnails.length === 0 && (<View style={[styles.loadingPlaceholder, { height: thumbnailHeight }]}/>)}
261
283
  </View>
262
284
 
263
- {/* Dimmed region - left */}
285
+ {/* Dimmed region - left (covers trimmed content from left handle to start handle position) */}
264
286
  <View style={[
265
287
  styles.dimmedRegion,
266
288
  {
267
- left: 0,
268
- width: startPosition,
289
+ left: handleWidth,
290
+ width: Math.max(0, startPosition - handleWidth),
269
291
  height: thumbnailHeight,
270
292
  backgroundColor: theme.dimmedColor,
271
293
  },
272
294
  ]} pointerEvents="none"/>
273
295
 
274
- {/* Dimmed region - right */}
296
+ {/* Dimmed region - right (covers trimmed content from end handle position to right handle) */}
275
297
  <View style={[
276
298
  styles.dimmedRegion,
277
299
  {
278
300
  left: endPosition,
279
- right: 0,
301
+ width: Math.max(0, containerWidth - handleWidth - endPosition),
280
302
  height: thumbnailHeight,
281
303
  backgroundColor: theme.dimmedColor,
282
304
  },
283
305
  ]} pointerEvents="none"/>
284
306
 
285
- {/* Selection frame - top and bottom borders */}
307
+ {/* Selection frame - top and bottom borders (span between handles) */}
286
308
  <View style={[
287
309
  styles.selectionBorder,
288
310
  styles.selectionBorderTop,
289
311
  {
290
- left: startPosition + handleWidth,
291
- width: Math.max(0, endPosition - startPosition - handleWidth * 2),
312
+ left: startPosition,
313
+ width: Math.max(0, endPosition - startPosition),
292
314
  backgroundColor: theme.borderColor,
293
315
  },
294
316
  ]} pointerEvents="none"/>
@@ -296,19 +318,19 @@ const VideoScrubber = forwardRef((props, ref) => {
296
318
  styles.selectionBorder,
297
319
  styles.selectionBorderBottom,
298
320
  {
299
- left: startPosition + handleWidth,
300
- width: Math.max(0, endPosition - startPosition - handleWidth * 2),
321
+ left: startPosition,
322
+ width: Math.max(0, endPosition - startPosition),
301
323
  top: thumbnailHeight - 2,
302
324
  backgroundColor: theme.borderColor,
303
325
  },
304
326
  ]} pointerEvents="none"/>
305
327
 
306
- {/* Start Handle */}
328
+ {/* Start Handle - positioned to the LEFT of the trim point (outside content when startTime=0) */}
307
329
  <Animated.View style={[
308
330
  styles.handle,
309
331
  styles.startHandle,
310
332
  {
311
- left: startPosition,
333
+ left: startPosition - handleWidth,
312
334
  width: handleWidth,
313
335
  height: thumbnailHeight,
314
336
  backgroundColor: theme.handleColor,
@@ -321,12 +343,12 @@ const VideoScrubber = forwardRef((props, ref) => {
321
343
  </View>
322
344
  </Animated.View>
323
345
 
324
- {/* End Handle */}
346
+ {/* End Handle - positioned to the RIGHT of the trim point (outside content when endTime=duration) */}
325
347
  <Animated.View style={[
326
348
  styles.handle,
327
349
  styles.endHandle,
328
350
  {
329
- left: endPosition - handleWidth,
351
+ left: endPosition,
330
352
  width: handleWidth,
331
353
  height: thumbnailHeight,
332
354
  backgroundColor: theme.handleColor,
@@ -339,12 +361,12 @@ const VideoScrubber = forwardRef((props, ref) => {
339
361
  </View>
340
362
  </Animated.View>
341
363
 
342
- {/* Tap area for seeking */}
364
+ {/* Tap area for seeking - covers the selection between start and end trim points */}
343
365
  <View style={[
344
366
  styles.tapArea,
345
367
  {
346
- left: startPosition + handleWidth,
347
- width: Math.max(0, endPosition - startPosition - handleWidth * 2),
368
+ left: startPosition,
369
+ width: Math.max(0, endPosition - startPosition),
348
370
  height: thumbnailHeight,
349
371
  },
350
372
  ]} onTouchEnd={handleTap}/>
@@ -1 +1 @@
1
- {"version":3,"file":"VideoScrubber.js","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n View,\n Image,\n StyleSheet,\n LayoutChangeEvent,\n PanResponder,\n PanResponderGestureState,\n GestureResponderEvent,\n Animated,\n} from 'react-native';\n\nimport { VideoScrubberProps, VideoScrubberRef, VideoScrubberTheme } from './VideoScrubber.types';\nimport ExpoTwoStepVideoModule from './ExpoTwoStepVideoModule';\n\nconst DEFAULT_THEME: Required<VideoScrubberTheme> = {\n handleColor: '#FFD700',\n selectedRegionColor: 'rgba(255,215,0,0.2)',\n playheadColor: '#FFFFFF',\n backgroundColor: '#1c1c1e',\n dimmedColor: 'rgba(0,0,0,0.6)',\n borderColor: '#FFD700',\n};\n\n/**\n * Video scrubber/trimmer component with thumbnail strip and draggable handles\n *\n * @example\n * ```tsx\n * <VideoScrubber\n * assetId={asset.id}\n * duration={asset.duration}\n * currentTime={currentTime}\n * startTime={trimStart}\n * endTime={trimEnd}\n * onStartTimeChange={setTrimStart}\n * onEndTimeChange={setTrimEnd}\n * onScrubbing={(time) => playerRef.current?.seek(time)}\n * />\n * ```\n */\nconst VideoScrubber = forwardRef<VideoScrubberRef, VideoScrubberProps>((props, ref) => {\n const {\n assetId,\n duration,\n currentTime,\n startTime,\n endTime,\n thumbnailCount = 10,\n thumbnailHeight = 50,\n onStartTimeChange,\n onEndTimeChange,\n onScrub,\n onScrubbing,\n onScrubEnd,\n minDuration = 0.5,\n style,\n disabled = false,\n theme: userTheme,\n showPlayhead = true,\n handleWidth = 20,\n } = props;\n\n const theme = useMemo(\n () => ({ ...DEFAULT_THEME, ...userTheme }),\n [userTheme]\n );\n\n const [thumbnails, setThumbnails] = useState<string[]>([]);\n const [containerWidth, setContainerWidth] = useState(0);\n const [isLoading, setIsLoading] = useState(true);\n const [dragging, setDragging] = useState<'start' | 'end' | 'playhead' | null>(null);\n\n // Track current values during drag - synced with props when not dragging\n const dragStartTimeRef = useRef(startTime);\n const dragEndTimeRef = useRef(endTime);\n const currentTimeRef = useRef(currentTime);\n const durationRef = useRef(duration);\n const containerWidthRef = useRef(containerWidth);\n\n // Sync refs with props when they change from outside (e.g., new video loaded)\n useEffect(() => {\n if (!dragging) {\n dragStartTimeRef.current = startTime;\n dragEndTimeRef.current = endTime;\n }\n }, [startTime, endTime, dragging]);\n\n // Keep other refs in sync\n useEffect(() => {\n currentTimeRef.current = currentTime;\n }, [currentTime]);\n\n useEffect(() => {\n durationRef.current = duration;\n }, [duration]);\n\n useEffect(() => {\n containerWidthRef.current = containerWidth;\n }, [containerWidth]);\n\n // Calculate thumbnail times\n const thumbnailTimes = useMemo(() => {\n if (duration <= 0) return [];\n const count = Math.min(thumbnailCount, Math.max(1, Math.ceil(duration)));\n const interval = duration / count;\n return Array.from({ length: count }, (_, i) => i * interval + interval / 2);\n }, [duration, thumbnailCount]);\n\n // Load thumbnails\n useEffect(() => {\n if (!assetId || thumbnailTimes.length === 0 || duration <= 0) {\n setThumbnails([]);\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n\n ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n )\n .then((thumbs: string[]) => {\n if (!cancelled) {\n setThumbnails(thumbs);\n setIsLoading(false);\n }\n })\n .catch((err: Error) => {\n if (!cancelled) {\n console.error('Failed to generate thumbnails:', err);\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [assetId, thumbnailTimes, thumbnailHeight, duration]);\n\n // Convert time to position and vice versa\n const timeToPosition = useCallback(\n (time: number) => {\n if (duration <= 0 || containerWidth <= 0) return 0;\n return (time / duration) * containerWidth;\n },\n [duration, containerWidth]\n );\n\n // Calculate positions\n const startPosition = timeToPosition(startTime);\n const endPosition = timeToPosition(endTime);\n const playheadPosition = timeToPosition(currentTime);\n\n // Handle layout change\n const handleLayout = useCallback((e: LayoutChangeEvent) => {\n setContainerWidth(e.nativeEvent.layout.width);\n }, []);\n\n // Store callbacks in refs to avoid recreating PanResponder on every render\n const onStartTimeChangeRef = useRef(onStartTimeChange);\n const onEndTimeChangeRef = useRef(onEndTimeChange);\n const onScrubRef = useRef(onScrub);\n const onScrubbingRef = useRef(onScrubbing);\n const onScrubEndRef = useRef(onScrubEnd);\n\n\n useEffect(() => {\n onStartTimeChangeRef.current = onStartTimeChange;\n onEndTimeChangeRef.current = onEndTimeChange;\n onScrubRef.current = onScrub;\n onScrubbingRef.current = onScrubbing;\n onScrubEndRef.current = onScrubEnd;\n }, [onStartTimeChange, onEndTimeChange, onScrub, onScrubbing, onScrubEnd]);\n\n // Create pan responders for handles and playhead\n // Using refs to avoid stale closures - PanResponder is only created once per type\n const createPanResponder = useCallback(\n (type: 'start' | 'end' | 'playhead') => {\n let initialTime = 0;\n\n return PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: (_, gestureState) => {\n // Claim the gesture if there's any horizontal movement\n // This prevents ScrollView from capturing horizontal drags\n return !disabled && Math.abs(gestureState.dx) > 2;\n },\n // Capture phase handlers - these fire before parent views\n onStartShouldSetPanResponderCapture: () => false, // Don't capture on start, let it bubble\n onMoveShouldSetPanResponderCapture: (_, gestureState) => {\n // Only capture if horizontal movement is dominant\n return !disabled && Math.abs(gestureState.dx) > 5 && Math.abs(gestureState.dx) > Math.abs(gestureState.dy);\n },\n onPanResponderGrant: () => {\n // Read current values from refs at the moment the gesture starts\n if (type === 'start') {\n initialTime = dragStartTimeRef.current;\n } else if (type === 'end') {\n initialTime = dragEndTimeRef.current;\n } else {\n initialTime = currentTimeRef.current;\n }\n setDragging(type);\n },\n onPanResponderMove: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n const width = containerWidthRef.current;\n const dur = durationRef.current;\n if (width <= 0 || dur <= 0) return;\n\n const deltaX = gestureState.dx;\n const deltaTime = (deltaX / width) * dur;\n let newTime = initialTime + deltaTime;\n\n if (type === 'start') {\n // Constrain start handle\n newTime = Math.max(0, Math.min(newTime, dragEndTimeRef.current - minDuration));\n dragStartTimeRef.current = newTime;\n onStartTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else if (type === 'end') {\n // Constrain end handle\n newTime = Math.max(dragStartTimeRef.current + minDuration, Math.min(newTime, dur));\n dragEndTimeRef.current = newTime;\n onEndTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else {\n // Playhead - constrain to selection\n newTime = Math.max(dragStartTimeRef.current, Math.min(newTime, dragEndTimeRef.current));\n onScrubRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n }\n },\n onPanResponderRelease: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n setDragging(null);\n if (type === 'start') {\n onScrubEndRef.current?.(dragStartTimeRef.current);\n } else if (type === 'end') {\n onScrubEndRef.current?.(dragEndTimeRef.current);\n } else {\n const width = containerWidthRef.current;\n const dur = durationRef.current;\n if (width > 0 && dur > 0) {\n const deltaTime = (gestureState.dx / width) * dur;\n const newTime = Math.max(\n dragStartTimeRef.current,\n Math.min(initialTime + deltaTime, dragEndTimeRef.current)\n );\n onScrubEndRef.current?.(newTime);\n }\n }\n },\n onPanResponderTerminate: () => {\n setDragging(null);\n },\n });\n },\n [disabled, minDuration] // Only recreate if disabled or minDuration changes\n );\n\n const startPanResponder = useMemo(\n () => createPanResponder('start'),\n [createPanResponder]\n );\n const endPanResponder = useMemo(\n () => createPanResponder('end'),\n [createPanResponder]\n );\n const playheadPanResponder = useMemo(\n () => createPanResponder('playhead'),\n [createPanResponder]\n );\n\n // Tap to seek\n const handleTap = useCallback(\n (e: GestureResponderEvent) => {\n if (disabled) return;\n const { locationX } = e.nativeEvent;\n const width = containerWidthRef.current;\n const dur = durationRef.current;\n if (width <= 0 || dur <= 0) return;\n\n const time = Math.max(0, Math.min(dur, (locationX / width) * dur));\n // Only seek within selection\n const clampedTime = Math.max(dragStartTimeRef.current, Math.min(time, dragEndTimeRef.current));\n onScrubRef.current?.(clampedTime);\n },\n [disabled]\n );\n\n // Expose ref methods\n useImperativeHandle(ref, () => ({\n regenerateThumbnails: async () => {\n if (!assetId || thumbnailTimes.length === 0) return;\n setIsLoading(true);\n try {\n const thumbs = await ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n );\n setThumbnails(thumbs);\n } catch (err) {\n console.error('Failed to regenerate thumbnails:', err);\n } finally {\n setIsLoading(false);\n }\n },\n setPlayheadPosition: (time: number) => {\n onScrub?.(Math.max(startTime, Math.min(time, endTime)));\n },\n getSelection: () => ({ startTime, endTime }),\n }));\n\n const thumbnailWidth = containerWidth > 0 && thumbnails.length > 0\n ? containerWidth / thumbnails.length\n : 0;\n\n return (\n <View\n style={[\n styles.container,\n { backgroundColor: theme.backgroundColor },\n style,\n ]}\n onLayout={handleLayout}\n >\n {/* Thumbnail Strip */}\n <View style={[styles.thumbnailStrip, { height: thumbnailHeight }]}>\n {thumbnails.map((thumb, index) => (\n <Image\n key={index}\n source={{ uri: `data:image/png;base64,${thumb}` }}\n style={[\n styles.thumbnail,\n {\n width: thumbnailWidth,\n height: thumbnailHeight,\n },\n ]}\n resizeMode=\"cover\"\n />\n ))}\n {isLoading && thumbnails.length === 0 && (\n <View style={[styles.loadingPlaceholder, { height: thumbnailHeight }]} />\n )}\n </View>\n\n {/* Dimmed region - left */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: 0,\n width: startPosition,\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Dimmed region - right */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: endPosition,\n right: 0,\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Selection frame - top and bottom borders */}\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderTop,\n {\n left: startPosition + handleWidth,\n width: Math.max(0, endPosition - startPosition - handleWidth * 2),\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderBottom,\n {\n left: startPosition + handleWidth,\n width: Math.max(0, endPosition - startPosition - handleWidth * 2),\n top: thumbnailHeight - 2,\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Start Handle */}\n <Animated.View\n style={[\n styles.handle,\n styles.startHandle,\n {\n left: startPosition,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'start' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 15, right: 10 }}\n {...startPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopLeft]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomLeft]} />\n </View>\n </Animated.View>\n\n {/* End Handle */}\n <Animated.View\n style={[\n styles.handle,\n styles.endHandle,\n {\n left: endPosition - handleWidth,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'end' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 10, right: 15 }}\n {...endPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopRight]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomRight]} />\n </View>\n </Animated.View>\n\n {/* Tap area for seeking */}\n <View\n style={[\n styles.tapArea,\n {\n left: startPosition + handleWidth,\n width: Math.max(0, endPosition - startPosition - handleWidth * 2),\n height: thumbnailHeight,\n },\n ]}\n onTouchEnd={handleTap}\n />\n\n {/* Playhead */}\n {showPlayhead && playheadPosition >= startPosition && playheadPosition <= endPosition && (\n <Animated.View\n style={[\n styles.playhead,\n {\n left: playheadPosition - 1,\n height: thumbnailHeight + 8,\n top: -4,\n backgroundColor: theme.playheadColor,\n opacity: dragging === 'playhead' ? 0.8 : 1,\n },\n ]}\n {...playheadPanResponder.panHandlers}\n >\n <View style={[styles.playheadKnob, { backgroundColor: theme.playheadColor }]} />\n </Animated.View>\n )}\n </View>\n );\n});\n\nVideoScrubber.displayName = 'VideoScrubber';\n\nconst styles = StyleSheet.create({\n container: {\n width: '100%',\n borderRadius: 8,\n overflow: 'hidden',\n position: 'relative',\n },\n thumbnailStrip: {\n flexDirection: 'row',\n width: '100%',\n },\n thumbnail: {\n backgroundColor: '#2c2c2e',\n },\n loadingPlaceholder: {\n flex: 1,\n backgroundColor: '#2c2c2e',\n },\n dimmedRegion: {\n position: 'absolute',\n top: 0,\n },\n selectionBorder: {\n position: 'absolute',\n height: 2,\n },\n selectionBorderTop: {\n top: 0,\n },\n selectionBorderBottom: {\n bottom: 0,\n },\n handle: {\n position: 'absolute',\n top: 0,\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: 4,\n },\n startHandle: {\n borderTopLeftRadius: 6,\n borderBottomLeftRadius: 6,\n borderTopRightRadius: 0,\n borderBottomRightRadius: 0,\n },\n endHandle: {\n borderTopLeftRadius: 0,\n borderBottomLeftRadius: 0,\n borderTopRightRadius: 6,\n borderBottomRightRadius: 6,\n },\n handleChevron: {\n width: 8,\n height: 16,\n justifyContent: 'center',\n alignItems: 'center',\n },\n chevronLine: {\n width: 2,\n height: 8,\n backgroundColor: '#000',\n borderRadius: 1,\n position: 'absolute',\n },\n chevronLineTopLeft: {\n transform: [{ rotate: '20deg' }],\n top: 0,\n },\n chevronLineBottomLeft: {\n transform: [{ rotate: '-20deg' }],\n bottom: 0,\n },\n chevronLineTopRight: {\n transform: [{ rotate: '-20deg' }],\n top: 0,\n },\n chevronLineBottomRight: {\n transform: [{ rotate: '20deg' }],\n bottom: 0,\n },\n tapArea: {\n position: 'absolute',\n top: 0,\n },\n playhead: {\n position: 'absolute',\n width: 2,\n borderRadius: 1,\n },\n playheadKnob: {\n position: 'absolute',\n top: -4,\n left: -4,\n width: 10,\n height: 10,\n borderRadius: 5,\n },\n});\n\nexport default VideoScrubber;\n"]}
1
+ {"version":3,"file":"VideoScrubber.js","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n View,\n Image,\n StyleSheet,\n LayoutChangeEvent,\n PanResponder,\n PanResponderGestureState,\n GestureResponderEvent,\n Animated,\n} from 'react-native';\n\nimport { VideoScrubberProps, VideoScrubberRef, VideoScrubberTheme } from './VideoScrubber.types';\nimport ExpoTwoStepVideoModule from './ExpoTwoStepVideoModule';\n\nconst DEFAULT_THEME: Required<VideoScrubberTheme> = {\n handleColor: '#FFD700',\n selectedRegionColor: 'rgba(255,215,0,0.2)',\n playheadColor: '#FFFFFF',\n backgroundColor: '#1c1c1e',\n dimmedColor: 'rgba(0,0,0,0.6)',\n borderColor: '#FFD700',\n};\n\n/**\n * Video scrubber/trimmer component with thumbnail strip and draggable handles\n *\n * @example\n * ```tsx\n * <VideoScrubber\n * assetId={asset.id}\n * duration={asset.duration}\n * currentTime={currentTime}\n * startTime={trimStart}\n * endTime={trimEnd}\n * onStartTimeChange={setTrimStart}\n * onEndTimeChange={setTrimEnd}\n * onScrubbing={(time) => playerRef.current?.seek(time)}\n * />\n * ```\n */\nconst VideoScrubber = forwardRef<VideoScrubberRef, VideoScrubberProps>((props, ref) => {\n const {\n assetId,\n duration,\n currentTime,\n startTime,\n endTime,\n thumbnailCount = 10,\n thumbnailHeight = 50,\n onStartTimeChange,\n onEndTimeChange,\n onScrub,\n onScrubbing,\n onScrubEnd,\n minDuration = 0.5,\n style,\n disabled = false,\n theme: userTheme,\n showPlayhead = true,\n handleWidth = 20,\n } = props;\n\n const theme = useMemo(\n () => ({ ...DEFAULT_THEME, ...userTheme }),\n [userTheme]\n );\n\n const [thumbnails, setThumbnails] = useState<string[]>([]);\n const [containerWidth, setContainerWidth] = useState(0);\n const [isLoading, setIsLoading] = useState(true);\n const [dragging, setDragging] = useState<'start' | 'end' | 'playhead' | null>(null);\n\n // Track current values during drag - synced with props when not dragging\n const dragStartTimeRef = useRef(startTime);\n const dragEndTimeRef = useRef(endTime);\n const currentTimeRef = useRef(currentTime);\n const durationRef = useRef(duration);\n const containerWidthRef = useRef(containerWidth);\n const handleWidthRef = useRef(handleWidth);\n\n // Sync refs with props when they change from outside (e.g., new video loaded)\n useEffect(() => {\n if (!dragging) {\n dragStartTimeRef.current = startTime;\n dragEndTimeRef.current = endTime;\n }\n }, [startTime, endTime, dragging]);\n\n // Keep other refs in sync\n useEffect(() => {\n currentTimeRef.current = currentTime;\n }, [currentTime]);\n\n useEffect(() => {\n durationRef.current = duration;\n }, [duration]);\n\n useEffect(() => {\n containerWidthRef.current = containerWidth;\n }, [containerWidth]);\n\n useEffect(() => {\n handleWidthRef.current = handleWidth;\n }, [handleWidth]);\n\n // Calculate thumbnail times\n const thumbnailTimes = useMemo(() => {\n if (duration <= 0) return [];\n const count = Math.min(thumbnailCount, Math.max(1, Math.ceil(duration)));\n const interval = duration / count;\n return Array.from({ length: count }, (_, i) => i * interval + interval / 2);\n }, [duration, thumbnailCount]);\n\n // Load thumbnails\n useEffect(() => {\n if (!assetId || thumbnailTimes.length === 0 || duration <= 0) {\n setThumbnails([]);\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n\n ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n )\n .then((thumbs: string[]) => {\n if (!cancelled) {\n setThumbnails(thumbs);\n setIsLoading(false);\n }\n })\n .catch((err: Error) => {\n if (!cancelled) {\n console.error('Failed to generate thumbnails:', err);\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [assetId, thumbnailTimes, thumbnailHeight, duration]);\n\n // The content area is between the handles\n const contentWidth = containerWidth > 0 ? containerWidth - handleWidth * 2 : 0;\n\n // Convert time to position within the content area (between handles)\n const timeToPosition = useCallback(\n (time: number) => {\n if (duration <= 0 || contentWidth <= 0) return handleWidth;\n return handleWidth + (time / duration) * contentWidth;\n },\n [duration, contentWidth, handleWidth]\n );\n\n // Calculate positions\n const startPosition = timeToPosition(startTime);\n const endPosition = timeToPosition(endTime);\n const playheadPosition = timeToPosition(currentTime);\n\n // Handle layout change\n const handleLayout = useCallback((e: LayoutChangeEvent) => {\n setContainerWidth(e.nativeEvent.layout.width);\n }, []);\n\n // Store callbacks in refs to avoid recreating PanResponder on every render\n const onStartTimeChangeRef = useRef(onStartTimeChange);\n const onEndTimeChangeRef = useRef(onEndTimeChange);\n const onScrubRef = useRef(onScrub);\n const onScrubbingRef = useRef(onScrubbing);\n const onScrubEndRef = useRef(onScrubEnd);\n\n\n useEffect(() => {\n onStartTimeChangeRef.current = onStartTimeChange;\n onEndTimeChangeRef.current = onEndTimeChange;\n onScrubRef.current = onScrub;\n onScrubbingRef.current = onScrubbing;\n onScrubEndRef.current = onScrubEnd;\n }, [onStartTimeChange, onEndTimeChange, onScrub, onScrubbing, onScrubEnd]);\n\n // Create pan responders for handles and playhead\n // Using refs to avoid stale closures - PanResponder is only created once per type\n const createPanResponder = useCallback(\n (type: 'start' | 'end' | 'playhead') => {\n let initialTime = 0;\n\n return PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: (_, gestureState) => {\n // Claim the gesture if there's any horizontal movement\n // This prevents ScrollView from capturing horizontal drags\n return !disabled && Math.abs(gestureState.dx) > 2;\n },\n // Capture phase handlers - these fire before parent views\n onStartShouldSetPanResponderCapture: () => false, // Don't capture on start, let it bubble\n onMoveShouldSetPanResponderCapture: (_, gestureState) => {\n // Only capture if horizontal movement is dominant\n return !disabled && Math.abs(gestureState.dx) > 5 && Math.abs(gestureState.dx) > Math.abs(gestureState.dy);\n },\n onPanResponderGrant: () => {\n // Read current values from refs at the moment the gesture starts\n if (type === 'start') {\n initialTime = dragStartTimeRef.current;\n } else if (type === 'end') {\n initialTime = dragEndTimeRef.current;\n } else {\n initialTime = currentTimeRef.current;\n }\n setDragging(type);\n },\n onPanResponderMove: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n const containerW = containerWidthRef.current;\n const hw = handleWidthRef.current;\n const dur = durationRef.current;\n // Content width is where time is mapped (between handles)\n const cw = containerW - hw * 2;\n if (cw <= 0 || dur <= 0) return;\n\n const deltaX = gestureState.dx;\n const deltaTime = (deltaX / cw) * dur;\n let newTime = initialTime + deltaTime;\n\n if (type === 'start') {\n // Constrain start handle\n newTime = Math.max(0, Math.min(newTime, dragEndTimeRef.current - minDuration));\n dragStartTimeRef.current = newTime;\n onStartTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else if (type === 'end') {\n // Constrain end handle\n newTime = Math.max(dragStartTimeRef.current + minDuration, Math.min(newTime, dur));\n dragEndTimeRef.current = newTime;\n onEndTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else {\n // Playhead - constrain to selection\n newTime = Math.max(dragStartTimeRef.current, Math.min(newTime, dragEndTimeRef.current));\n onScrubRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n }\n },\n onPanResponderRelease: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n setDragging(null);\n if (type === 'start') {\n onScrubEndRef.current?.(dragStartTimeRef.current);\n } else if (type === 'end') {\n onScrubEndRef.current?.(dragEndTimeRef.current);\n } else {\n const containerW = containerWidthRef.current;\n const hw = handleWidthRef.current;\n const dur = durationRef.current;\n const cw = containerW - hw * 2;\n if (cw > 0 && dur > 0) {\n const deltaTime = (gestureState.dx / cw) * dur;\n const newTime = Math.max(\n dragStartTimeRef.current,\n Math.min(initialTime + deltaTime, dragEndTimeRef.current)\n );\n onScrubEndRef.current?.(newTime);\n }\n }\n },\n onPanResponderTerminate: () => {\n setDragging(null);\n },\n });\n },\n [disabled, minDuration] // Only recreate if disabled or minDuration changes\n );\n\n const startPanResponder = useMemo(\n () => createPanResponder('start'),\n [createPanResponder]\n );\n const endPanResponder = useMemo(\n () => createPanResponder('end'),\n [createPanResponder]\n );\n const playheadPanResponder = useMemo(\n () => createPanResponder('playhead'),\n [createPanResponder]\n );\n\n // Tap to seek - locationX is relative to the tap area which spans from startPosition to endPosition\n const handleTap = useCallback(\n (e: GestureResponderEvent) => {\n if (disabled) return;\n const { locationX } = e.nativeEvent;\n const startT = dragStartTimeRef.current;\n const endT = dragEndTimeRef.current;\n const selectionDuration = endT - startT;\n if (selectionDuration <= 0) return;\n\n // The tap area spans the selection, so locationX=0 is startTime and locationX=width is endTime\n // Calculate time proportionally within the selection\n const width = containerWidthRef.current;\n const dur = durationRef.current;\n if (width <= 0 || dur <= 0) return;\n\n // Calculate the width of the tap area (endPosition - startPosition in content coordinates)\n const cw = width - handleWidth * 2; // contentWidth\n const tapAreaWidth = ((endT - startT) / dur) * cw;\n if (tapAreaWidth <= 0) return;\n\n const proportion = Math.max(0, Math.min(1, locationX / tapAreaWidth));\n const time = startT + proportion * selectionDuration;\n onScrubRef.current?.(time);\n },\n [disabled, handleWidth]\n );\n\n // Expose ref methods\n useImperativeHandle(ref, () => ({\n regenerateThumbnails: async () => {\n if (!assetId || thumbnailTimes.length === 0) return;\n setIsLoading(true);\n try {\n const thumbs = await ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n );\n setThumbnails(thumbs);\n } catch (err) {\n console.error('Failed to regenerate thumbnails:', err);\n } finally {\n setIsLoading(false);\n }\n },\n setPlayheadPosition: (time: number) => {\n onScrub?.(Math.max(startTime, Math.min(time, endTime)));\n },\n getSelection: () => ({ startTime, endTime }),\n }));\n\n const thumbnailWidth = contentWidth > 0 && thumbnails.length > 0\n ? contentWidth / thumbnails.length\n : 0;\n\n return (\n <View\n style={[\n styles.container,\n { backgroundColor: theme.backgroundColor },\n style,\n ]}\n onLayout={handleLayout}\n >\n {/* Thumbnail Strip - positioned between handles */}\n <View style={[styles.thumbnailStrip, { height: thumbnailHeight, marginLeft: handleWidth, width: contentWidth }]}>\n {thumbnails.map((thumb, index) => (\n <Image\n key={index}\n source={{ uri: `data:image/png;base64,${thumb}` }}\n style={[\n styles.thumbnail,\n {\n width: thumbnailWidth,\n height: thumbnailHeight,\n },\n ]}\n resizeMode=\"cover\"\n />\n ))}\n {isLoading && thumbnails.length === 0 && (\n <View style={[styles.loadingPlaceholder, { height: thumbnailHeight }]} />\n )}\n </View>\n\n {/* Dimmed region - left (covers trimmed content from left handle to start handle position) */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: handleWidth,\n width: Math.max(0, startPosition - handleWidth),\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Dimmed region - right (covers trimmed content from end handle position to right handle) */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: endPosition,\n width: Math.max(0, containerWidth - handleWidth - endPosition),\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Selection frame - top and bottom borders (span between handles) */}\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderTop,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderBottom,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n top: thumbnailHeight - 2,\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Start Handle - positioned to the LEFT of the trim point (outside content when startTime=0) */}\n <Animated.View\n style={[\n styles.handle,\n styles.startHandle,\n {\n left: startPosition - handleWidth,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'start' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 15, right: 10 }}\n {...startPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopLeft]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomLeft]} />\n </View>\n </Animated.View>\n\n {/* End Handle - positioned to the RIGHT of the trim point (outside content when endTime=duration) */}\n <Animated.View\n style={[\n styles.handle,\n styles.endHandle,\n {\n left: endPosition,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'end' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 10, right: 15 }}\n {...endPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopRight]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomRight]} />\n </View>\n </Animated.View>\n\n {/* Tap area for seeking - covers the selection between start and end trim points */}\n <View\n style={[\n styles.tapArea,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n height: thumbnailHeight,\n },\n ]}\n onTouchEnd={handleTap}\n />\n\n {/* Playhead */}\n {showPlayhead && playheadPosition >= startPosition && playheadPosition <= endPosition && (\n <Animated.View\n style={[\n styles.playhead,\n {\n left: playheadPosition - 1,\n height: thumbnailHeight + 8,\n top: -4,\n backgroundColor: theme.playheadColor,\n opacity: dragging === 'playhead' ? 0.8 : 1,\n },\n ]}\n {...playheadPanResponder.panHandlers}\n >\n <View style={[styles.playheadKnob, { backgroundColor: theme.playheadColor }]} />\n </Animated.View>\n )}\n </View>\n );\n});\n\nVideoScrubber.displayName = 'VideoScrubber';\n\nconst styles = StyleSheet.create({\n container: {\n width: '100%',\n borderRadius: 8,\n overflow: 'hidden',\n position: 'relative',\n },\n thumbnailStrip: {\n flexDirection: 'row',\n width: '100%',\n },\n thumbnail: {\n backgroundColor: '#2c2c2e',\n },\n loadingPlaceholder: {\n flex: 1,\n backgroundColor: '#2c2c2e',\n },\n dimmedRegion: {\n position: 'absolute',\n top: 0,\n },\n selectionBorder: {\n position: 'absolute',\n height: 2,\n },\n selectionBorderTop: {\n top: 0,\n },\n selectionBorderBottom: {\n bottom: 0,\n },\n handle: {\n position: 'absolute',\n top: 0,\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: 4,\n },\n startHandle: {\n borderTopLeftRadius: 6,\n borderBottomLeftRadius: 6,\n borderTopRightRadius: 0,\n borderBottomRightRadius: 0,\n },\n endHandle: {\n borderTopLeftRadius: 0,\n borderBottomLeftRadius: 0,\n borderTopRightRadius: 6,\n borderBottomRightRadius: 6,\n },\n handleChevron: {\n width: 8,\n height: 16,\n justifyContent: 'center',\n alignItems: 'center',\n },\n chevronLine: {\n width: 2,\n height: 8,\n backgroundColor: '#000',\n borderRadius: 1,\n position: 'absolute',\n },\n chevronLineTopLeft: {\n transform: [{ rotate: '20deg' }],\n top: 0,\n },\n chevronLineBottomLeft: {\n transform: [{ rotate: '-20deg' }],\n bottom: 0,\n },\n chevronLineTopRight: {\n transform: [{ rotate: '-20deg' }],\n top: 0,\n },\n chevronLineBottomRight: {\n transform: [{ rotate: '20deg' }],\n bottom: 0,\n },\n tapArea: {\n position: 'absolute',\n top: 0,\n },\n playhead: {\n position: 'absolute',\n width: 2,\n borderRadius: 1,\n },\n playheadKnob: {\n position: 'absolute',\n top: -4,\n left: -4,\n width: 10,\n height: 10,\n borderRadius: 5,\n },\n});\n\nexport default VideoScrubber;\n"]}
@@ -11,6 +11,7 @@ class ExpoTwoStepVideoView: ExpoView {
11
11
  private var playerLayer: AVPlayerLayer?
12
12
  private var timeObserver: Any?
13
13
  private var isObservingStatus: Bool = false
14
+ private var isAudioSessionConfigured: Bool = false
14
15
 
15
16
  /// Event dispatchers
16
17
  let onPlaybackStatusChange = EventDispatcher()
@@ -32,6 +33,8 @@ class ExpoTwoStepVideoView: ExpoView {
32
33
  clipsToBounds = true
33
34
  backgroundColor = .black
34
35
  setupPlayerLayer()
36
+ setupAudioSession()
37
+ setupAudioSessionObservers()
35
38
  }
36
39
 
37
40
  private func setupPlayerLayer() {
@@ -43,6 +46,87 @@ class ExpoTwoStepVideoView: ExpoView {
43
46
  }
44
47
  }
45
48
 
49
+ /// Configure audio session for video playback
50
+ /// Uses .playback category to ensure audio plays even when silent switch is on
51
+ private func setupAudioSession() {
52
+ do {
53
+ let audioSession = AVAudioSession.sharedInstance()
54
+ // .playback category: audio plays even with silent switch, stops other audio
55
+ // .defaultToSpeaker: routes audio to speaker by default (not earpiece)
56
+ try audioSession.setCategory(.playback, mode: .moviePlayback, options: [.defaultToSpeaker])
57
+ try audioSession.setActive(true, options: [.notifyOthersOnDeactivation])
58
+ isAudioSessionConfigured = true
59
+ } catch {
60
+ print("ExpoTwoStepVideoView: Failed to configure audio session: \(error)")
61
+ // Try a simpler configuration as fallback
62
+ do {
63
+ try AVAudioSession.sharedInstance().setCategory(.playback)
64
+ try AVAudioSession.sharedInstance().setActive(true)
65
+ isAudioSessionConfigured = true
66
+ } catch {
67
+ print("ExpoTwoStepVideoView: Fallback audio session also failed: \(error)")
68
+ }
69
+ }
70
+ }
71
+
72
+ /// Listen for audio session interruptions (phone calls, other apps)
73
+ private func setupAudioSessionObservers() {
74
+ NotificationCenter.default.addObserver(
75
+ self,
76
+ selector: #selector(handleAudioSessionInterruption),
77
+ name: AVAudioSession.interruptionNotification,
78
+ object: AVAudioSession.sharedInstance()
79
+ )
80
+
81
+ NotificationCenter.default.addObserver(
82
+ self,
83
+ selector: #selector(handleAudioSessionRouteChange),
84
+ name: AVAudioSession.routeChangeNotification,
85
+ object: AVAudioSession.sharedInstance()
86
+ )
87
+ }
88
+
89
+ @objc private func handleAudioSessionInterruption(notification: Notification) {
90
+ guard let userInfo = notification.userInfo,
91
+ let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
92
+ let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
93
+ return
94
+ }
95
+
96
+ switch type {
97
+ case .began:
98
+ // Interruption began - pause playback
99
+ player?.pause()
100
+ onPlaybackStatusChange(["status": "interrupted"])
101
+ case .ended:
102
+ // Interruption ended - check if we should resume
103
+ guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
104
+ let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
105
+ if options.contains(.shouldResume) {
106
+ // Re-activate audio session and resume
107
+ setupAudioSession()
108
+ player?.play()
109
+ onPlaybackStatusChange(["status": "playing"])
110
+ }
111
+ @unknown default:
112
+ break
113
+ }
114
+ }
115
+
116
+ @objc private func handleAudioSessionRouteChange(notification: Notification) {
117
+ guard let userInfo = notification.userInfo,
118
+ let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
119
+ let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
120
+ return
121
+ }
122
+
123
+ // Pause when headphones are unplugged
124
+ if reason == .oldDeviceUnavailable {
125
+ player?.pause()
126
+ onPlaybackStatusChange(["status": "paused"])
127
+ }
128
+ }
129
+
46
130
  override func layoutSubviews() {
47
131
  super.layoutSubviews()
48
132
  playerLayer?.frame = bounds
@@ -117,6 +201,21 @@ class ExpoTwoStepVideoView: ExpoView {
117
201
  }
118
202
 
119
203
  func play() {
204
+ // Ensure audio session is properly configured before playing
205
+ // This handles cases where another component may have changed the audio session
206
+ if !isAudioSessionConfigured {
207
+ setupAudioSession()
208
+ } else {
209
+ // Re-activate in case it was deactivated
210
+ do {
211
+ try AVAudioSession.sharedInstance().setActive(true, options: [.notifyOthersOnDeactivation])
212
+ } catch {
213
+ print("ExpoTwoStepVideoView: Failed to activate audio session: \(error)")
214
+ // Try full reconfiguration
215
+ setupAudioSession()
216
+ }
217
+ }
218
+
120
219
  player?.play()
121
220
  onPlaybackStatusChange(["status": "playing"])
122
221
  }
@@ -216,6 +315,9 @@ class ExpoTwoStepVideoView: ExpoView {
216
315
 
217
316
  deinit {
218
317
  cleanup()
318
+ // Remove audio session observers
319
+ NotificationCenter.default.removeObserver(self, name: AVAudioSession.interruptionNotification, object: nil)
320
+ NotificationCenter.default.removeObserver(self, name: AVAudioSession.routeChangeNotification, object: nil)
219
321
  // Also remove the layer to break any potential retain cycles
220
322
  playerLayer?.removeFromSuperlayer()
221
323
  playerLayer = nil
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@movementinfra/expo-twostep-video",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Minimal video editing for React Native using AVFoundation",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",