@retor/react-native 0.3.1 → 0.3.4
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/README.md +4 -2
- package/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +87 -65
- package/dist/index.mjs +89 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,14 +6,16 @@ Embed Retor 3D experiences in a React Native / Expo app — with composable bott
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Expo
|
|
9
|
-
npx expo install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg
|
|
9
|
+
npx expo install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg expo-blur
|
|
10
10
|
npm install @gorhom/bottom-sheet lucide-react-native @retor/react-native
|
|
11
11
|
|
|
12
12
|
# bare React Native
|
|
13
|
-
npm install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg @gorhom/bottom-sheet lucide-react-native @retor/react-native
|
|
13
|
+
npm install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg @gorhom/bottom-sheet lucide-react-native expo-blur @retor/react-native
|
|
14
14
|
cd ios && pod install
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
> `expo-blur` is optional — it's used for the default blurred sheet background. Skip it if you don't want blur and pass a custom `backgroundComponent` to any sheet.
|
|
18
|
+
|
|
17
19
|
You also need to wrap your app root in a `GestureHandlerRootView` (per `@gorhom/bottom-sheet` requirements):
|
|
18
20
|
|
|
19
21
|
```tsx
|
package/dist/index.d.mts
CHANGED
|
@@ -185,7 +185,7 @@ interface HudProps {
|
|
|
185
185
|
declare function Hud({ children }: HudProps): React.JSX.Element;
|
|
186
186
|
|
|
187
187
|
interface ProjectSheetProps {
|
|
188
|
-
/** Snap points. Defaults to ["35%", "
|
|
188
|
+
/** Snap points. Defaults to ["35%", "75%"]. */
|
|
189
189
|
snapPoints?: (string | number)[];
|
|
190
190
|
/** Override the default header (project name + description + arrow button). */
|
|
191
191
|
renderHeader?: (project: {
|
|
@@ -207,7 +207,7 @@ interface LinesCarouselProps {
|
|
|
207
207
|
declare function LinesCarousel({ children, gap, paddingHorizontal }: LinesCarouselProps): React.JSX.Element | null;
|
|
208
208
|
|
|
209
209
|
interface LineDetailSheetProps {
|
|
210
|
-
/** Snap points. Defaults to ["35%", "
|
|
210
|
+
/** Snap points. Defaults to ["35%", "75%"]. */
|
|
211
211
|
snapPoints?: (string | number)[];
|
|
212
212
|
/** Override the header. */
|
|
213
213
|
renderHeader?: (line: RetorLine) => React.ReactNode;
|
|
@@ -220,6 +220,12 @@ interface LineTagListProps {
|
|
|
220
220
|
/** Optional header rendered above the list (used internally for the default header). */
|
|
221
221
|
listHeader?: React.ReactNode;
|
|
222
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Renders the tags of the active line as a vertical list inside a
|
|
225
|
+
* `BottomSheetScrollView`. Tracks each item's Y position via `onLayout`
|
|
226
|
+
* and scrolls the closest tag to the top of the visible area as the
|
|
227
|
+
* camera scrolls.
|
|
228
|
+
*/
|
|
223
229
|
declare function LineTagList({ children, listHeader }: LineTagListProps): React.JSX.Element | null;
|
|
224
230
|
|
|
225
231
|
interface AddNoteSheetProps {
|
package/dist/index.d.ts
CHANGED
|
@@ -185,7 +185,7 @@ interface HudProps {
|
|
|
185
185
|
declare function Hud({ children }: HudProps): React.JSX.Element;
|
|
186
186
|
|
|
187
187
|
interface ProjectSheetProps {
|
|
188
|
-
/** Snap points. Defaults to ["35%", "
|
|
188
|
+
/** Snap points. Defaults to ["35%", "75%"]. */
|
|
189
189
|
snapPoints?: (string | number)[];
|
|
190
190
|
/** Override the default header (project name + description + arrow button). */
|
|
191
191
|
renderHeader?: (project: {
|
|
@@ -207,7 +207,7 @@ interface LinesCarouselProps {
|
|
|
207
207
|
declare function LinesCarousel({ children, gap, paddingHorizontal }: LinesCarouselProps): React.JSX.Element | null;
|
|
208
208
|
|
|
209
209
|
interface LineDetailSheetProps {
|
|
210
|
-
/** Snap points. Defaults to ["35%", "
|
|
210
|
+
/** Snap points. Defaults to ["35%", "75%"]. */
|
|
211
211
|
snapPoints?: (string | number)[];
|
|
212
212
|
/** Override the header. */
|
|
213
213
|
renderHeader?: (line: RetorLine) => React.ReactNode;
|
|
@@ -220,6 +220,12 @@ interface LineTagListProps {
|
|
|
220
220
|
/** Optional header rendered above the list (used internally for the default header). */
|
|
221
221
|
listHeader?: React.ReactNode;
|
|
222
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Renders the tags of the active line as a vertical list inside a
|
|
225
|
+
* `BottomSheetScrollView`. Tracks each item's Y position via `onLayout`
|
|
226
|
+
* and scrolls the closest tag to the top of the visible area as the
|
|
227
|
+
* camera scrolls.
|
|
228
|
+
*/
|
|
223
229
|
declare function LineTagList({ children, listHeader }: LineTagListProps): React.JSX.Element | null;
|
|
224
230
|
|
|
225
231
|
interface AddNoteSheetProps {
|
package/dist/index.js
CHANGED
|
@@ -217,14 +217,10 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
217
217
|
openLine: (lineId) => send("open-line", { lineId }),
|
|
218
218
|
exitLine: () => send("exit-line"),
|
|
219
219
|
scrollToTag: (tagId) => send("scroll-to-tag", { tagId }),
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
setAutoplay: (playing) => {
|
|
225
|
-
setIsPlaying(playing);
|
|
226
|
-
send("set-autoplay", { playing });
|
|
227
|
-
}
|
|
220
|
+
// Bridge owns autoplay state — it'll emit `autoplay-state` back, which
|
|
221
|
+
// we use to update isPlaying. No optimistic local update.
|
|
222
|
+
toggleAutoplay: () => send("toggle-autoplay"),
|
|
223
|
+
setAutoplay: (playing) => send("set-autoplay", { playing })
|
|
228
224
|
}),
|
|
229
225
|
[send]
|
|
230
226
|
);
|
|
@@ -236,30 +232,47 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
236
232
|
if (!notes || !readyRef.current) return;
|
|
237
233
|
send("set-notes", { notes });
|
|
238
234
|
}, [notes, send]);
|
|
235
|
+
const closestTagIdRef = (0, import_react2.useRef)(closestTagId);
|
|
236
|
+
const addNoteTagIdRef = (0, import_react2.useRef)(addNoteTagId);
|
|
237
|
+
const activeLineIdRef = (0, import_react2.useRef)(activeLineId);
|
|
238
|
+
const linesRef = (0, import_react2.useRef)(lines);
|
|
239
|
+
const onNoteSubmitRef = (0, import_react2.useRef)(onNoteSubmit);
|
|
240
|
+
(0, import_react2.useEffect)(() => {
|
|
241
|
+
closestTagIdRef.current = closestTagId;
|
|
242
|
+
}, [closestTagId]);
|
|
243
|
+
(0, import_react2.useEffect)(() => {
|
|
244
|
+
addNoteTagIdRef.current = addNoteTagId;
|
|
245
|
+
}, [addNoteTagId]);
|
|
246
|
+
(0, import_react2.useEffect)(() => {
|
|
247
|
+
activeLineIdRef.current = activeLineId;
|
|
248
|
+
}, [activeLineId]);
|
|
249
|
+
(0, import_react2.useEffect)(() => {
|
|
250
|
+
linesRef.current = lines;
|
|
251
|
+
}, [lines]);
|
|
252
|
+
(0, import_react2.useEffect)(() => {
|
|
253
|
+
onNoteSubmitRef.current = onNoteSubmit;
|
|
254
|
+
}, [onNoteSubmit]);
|
|
239
255
|
const openAddNote = (0, import_react2.useCallback)((tagId) => {
|
|
240
|
-
setAddNoteTagId(tagId ??
|
|
256
|
+
setAddNoteTagId(tagId ?? closestTagIdRef.current ?? null);
|
|
241
257
|
setIsAddNoteOpen(true);
|
|
242
|
-
}, [
|
|
258
|
+
}, []);
|
|
243
259
|
const closeAddNote = (0, import_react2.useCallback)(() => {
|
|
244
260
|
setIsAddNoteOpen(false);
|
|
245
261
|
}, []);
|
|
246
|
-
const submitNote = (0, import_react2.useCallback)(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
},
|
|
261
|
-
[addNoteTagId, activeLineId, lines, onNoteSubmit]
|
|
262
|
-
);
|
|
262
|
+
const submitNote = (0, import_react2.useCallback)((text, isPrivate = true) => {
|
|
263
|
+
const tagId = addNoteTagIdRef.current;
|
|
264
|
+
const lineId = activeLineIdRef.current;
|
|
265
|
+
const tag = linesRef.current.find((l) => l._id === lineId)?.tags.find((t) => t._id === tagId);
|
|
266
|
+
const payload = {
|
|
267
|
+
text,
|
|
268
|
+
isPrivate,
|
|
269
|
+
tagId,
|
|
270
|
+
lineId,
|
|
271
|
+
position: tag?.position ?? null
|
|
272
|
+
};
|
|
273
|
+
onNoteSubmitRef.current?.(payload);
|
|
274
|
+
setIsAddNoteOpen(false);
|
|
275
|
+
}, []);
|
|
263
276
|
const handleMessage = (0, import_react2.useCallback)(
|
|
264
277
|
(event) => {
|
|
265
278
|
let data = null;
|
|
@@ -299,6 +312,11 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
299
312
|
onLineProgress?.(payload);
|
|
300
313
|
break;
|
|
301
314
|
}
|
|
315
|
+
case "autoplay-state": {
|
|
316
|
+
const payload = data.payload;
|
|
317
|
+
setIsPlaying(!!payload.playing);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
302
320
|
default:
|
|
303
321
|
onMessage?.(data.type, data.payload);
|
|
304
322
|
}
|
|
@@ -371,7 +389,7 @@ function BlurBackground({ style }) {
|
|
|
371
389
|
}
|
|
372
390
|
|
|
373
391
|
// src/ProjectSheet.tsx
|
|
374
|
-
function ProjectSheet({ snapPoints = ["35%", "
|
|
392
|
+
function ProjectSheet({ snapPoints = ["35%", "75%"], renderHeader, children }) {
|
|
375
393
|
const { project, activeLineId, isAddNoteOpen } = useRetorBridge();
|
|
376
394
|
const sheetRef = (0, import_react5.useRef)(null);
|
|
377
395
|
const snapPointsArr = (0, import_react5.useMemo)(() => snapPoints, [snapPoints]);
|
|
@@ -379,11 +397,15 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
|
|
|
379
397
|
(0, import_react5.useEffect)(() => {
|
|
380
398
|
if (activeLineId || isAddNoteOpen) {
|
|
381
399
|
sheetRef.current?.dismiss();
|
|
382
|
-
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const t = setTimeout(() => {
|
|
383
403
|
sheetRef.current?.present();
|
|
404
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
384
405
|
setMinimized(false);
|
|
385
|
-
}
|
|
386
|
-
|
|
406
|
+
}, 80);
|
|
407
|
+
return () => clearTimeout(t);
|
|
408
|
+
}, [activeLineId, isAddNoteOpen, snapPointsArr.length]);
|
|
387
409
|
const handleSheetChange = (index) => {
|
|
388
410
|
setMinimized(index === 0);
|
|
389
411
|
};
|
|
@@ -401,6 +423,7 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
|
|
|
401
423
|
snapPoints: snapPointsArr,
|
|
402
424
|
enablePanDownToClose: false,
|
|
403
425
|
enableDismissOnClose: false,
|
|
426
|
+
enableOverDrag: false,
|
|
404
427
|
onChange: handleSheetChange,
|
|
405
428
|
backdropComponent: (props) => /* @__PURE__ */ import_react5.default.createElement(
|
|
406
429
|
import_bottom_sheet2.BottomSheetBackdrop,
|
|
@@ -492,19 +515,23 @@ var import_react_native5 = require("react-native");
|
|
|
492
515
|
var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
|
|
493
516
|
var import_react_native_svg = __toESM(require("react-native-svg"));
|
|
494
517
|
var import_lucide_react_native2 = require("lucide-react-native");
|
|
495
|
-
function LineDetailSheet({ snapPoints = ["35%", "
|
|
518
|
+
function LineDetailSheet({ snapPoints = ["35%", "75%"], renderHeader, children }) {
|
|
496
519
|
const { activeLine, isAddNoteOpen, controls } = useRetorBridge();
|
|
497
520
|
const sheetRef = (0, import_react6.useRef)(null);
|
|
498
521
|
const snapPointsArr = (0, import_react6.useMemo)(() => snapPoints, [snapPoints]);
|
|
499
522
|
const [minimized, setMinimized] = (0, import_react6.useState)(false);
|
|
500
523
|
(0, import_react6.useEffect)(() => {
|
|
501
|
-
if (activeLine
|
|
502
|
-
sheetRef.current?.present();
|
|
503
|
-
setMinimized(false);
|
|
504
|
-
} else {
|
|
524
|
+
if (!activeLine || isAddNoteOpen) {
|
|
505
525
|
sheetRef.current?.dismiss();
|
|
526
|
+
return;
|
|
506
527
|
}
|
|
507
|
-
|
|
528
|
+
const t = setTimeout(() => {
|
|
529
|
+
sheetRef.current?.present();
|
|
530
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
531
|
+
setMinimized(false);
|
|
532
|
+
}, 80);
|
|
533
|
+
return () => clearTimeout(t);
|
|
534
|
+
}, [activeLine, isAddNoteOpen, snapPointsArr.length]);
|
|
508
535
|
const handleSheetChange = (index) => {
|
|
509
536
|
setMinimized(index === 0);
|
|
510
537
|
};
|
|
@@ -527,6 +554,7 @@ function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }
|
|
|
527
554
|
snapPoints: snapPointsArr,
|
|
528
555
|
enablePanDownToClose: false,
|
|
529
556
|
enableDismissOnClose: false,
|
|
557
|
+
enableOverDrag: false,
|
|
530
558
|
onChange: handleSheetChange,
|
|
531
559
|
footerComponent: renderFooter,
|
|
532
560
|
backdropComponent: (props) => /* @__PURE__ */ import_react6.default.createElement(
|
|
@@ -574,45 +602,37 @@ function AutoplayButton() {
|
|
|
574
602
|
}
|
|
575
603
|
function LineTagList({ children, listHeader }) {
|
|
576
604
|
const { activeLine, closestTagId } = useRetorBridge();
|
|
577
|
-
const
|
|
605
|
+
const scrollRef = (0, import_react6.useRef)(null);
|
|
606
|
+
const offsetsRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
578
607
|
const tags = (0, import_react6.useMemo)(
|
|
579
608
|
() => (activeLine?.tags ?? []).filter((t) => t.name && t.name.trim().length > 0),
|
|
580
609
|
[activeLine]
|
|
581
610
|
);
|
|
611
|
+
(0, import_react6.useEffect)(() => {
|
|
612
|
+
offsetsRef.current = /* @__PURE__ */ new Map();
|
|
613
|
+
}, [activeLine?._id]);
|
|
582
614
|
(0, import_react6.useEffect)(() => {
|
|
583
615
|
if (!closestTagId) return;
|
|
584
|
-
const index = tags.findIndex((t2) => t2._id === closestTagId);
|
|
585
|
-
if (index < 0) return;
|
|
586
616
|
const t = setTimeout(() => {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
617
|
+
const y = offsetsRef.current.get(closestTagId);
|
|
618
|
+
if (y != null) {
|
|
619
|
+
scrollRef.current?.scrollTo({ y, animated: true });
|
|
590
620
|
}
|
|
591
|
-
},
|
|
621
|
+
}, 60);
|
|
592
622
|
return () => clearTimeout(t);
|
|
593
|
-
}, [closestTagId
|
|
623
|
+
}, [closestTagId]);
|
|
594
624
|
if (!activeLine) return null;
|
|
625
|
+
const handleItemLayout = (id, e) => {
|
|
626
|
+
offsetsRef.current.set(id, e.nativeEvent.layout.y);
|
|
627
|
+
};
|
|
595
628
|
return /* @__PURE__ */ import_react6.default.createElement(
|
|
596
|
-
import_bottom_sheet3.
|
|
629
|
+
import_bottom_sheet3.BottomSheetScrollView,
|
|
597
630
|
{
|
|
598
|
-
ref:
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
contentContainerStyle: styles3.list,
|
|
604
|
-
removeClippedSubviews: false,
|
|
605
|
-
onScrollToIndexFailed: (info) => {
|
|
606
|
-
const offset = info.averageItemLength * info.index;
|
|
607
|
-
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
608
|
-
setTimeout(() => {
|
|
609
|
-
try {
|
|
610
|
-
listRef.current?.scrollToIndex({ index: info.index, animated: true, viewPosition: 0 });
|
|
611
|
-
} catch {
|
|
612
|
-
}
|
|
613
|
-
}, 100);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
631
|
+
ref: scrollRef,
|
|
632
|
+
contentContainerStyle: styles3.list
|
|
633
|
+
},
|
|
634
|
+
listHeader,
|
|
635
|
+
tags.map((tag) => /* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { key: tag._id, onLayout: (e) => handleItemLayout(tag._id, e) }, children(tag, tag._id === closestTagId)))
|
|
616
636
|
);
|
|
617
637
|
}
|
|
618
638
|
function DefaultLineTagList({ listHeader }) {
|
|
@@ -664,10 +684,11 @@ var styles3 = import_react_native5.StyleSheet.create({
|
|
|
664
684
|
justifyContent: "center",
|
|
665
685
|
position: "relative"
|
|
666
686
|
},
|
|
667
|
-
list: {
|
|
687
|
+
list: { paddingBottom: 96, gap: 4 },
|
|
668
688
|
tagItem: {
|
|
669
689
|
flexDirection: "row",
|
|
670
690
|
alignItems: "center",
|
|
691
|
+
marginHorizontal: 16,
|
|
671
692
|
paddingHorizontal: 12,
|
|
672
693
|
paddingVertical: 12,
|
|
673
694
|
borderRadius: 12,
|
|
@@ -752,6 +773,7 @@ function AddNoteSheet({
|
|
|
752
773
|
ref: sheetRef,
|
|
753
774
|
snapPoints: snapPointsArr,
|
|
754
775
|
enablePanDownToClose: true,
|
|
776
|
+
enableOverDrag: false,
|
|
755
777
|
onDismiss: closeAddNote,
|
|
756
778
|
backdropComponent: (props) => /* @__PURE__ */ import_react7.default.createElement(
|
|
757
779
|
import_bottom_sheet4.BottomSheetBackdrop,
|
package/dist/index.mjs
CHANGED
|
@@ -173,14 +173,10 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
173
173
|
openLine: (lineId) => send("open-line", { lineId }),
|
|
174
174
|
exitLine: () => send("exit-line"),
|
|
175
175
|
scrollToTag: (tagId) => send("scroll-to-tag", { tagId }),
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
setAutoplay: (playing) => {
|
|
181
|
-
setIsPlaying(playing);
|
|
182
|
-
send("set-autoplay", { playing });
|
|
183
|
-
}
|
|
176
|
+
// Bridge owns autoplay state — it'll emit `autoplay-state` back, which
|
|
177
|
+
// we use to update isPlaying. No optimistic local update.
|
|
178
|
+
toggleAutoplay: () => send("toggle-autoplay"),
|
|
179
|
+
setAutoplay: (playing) => send("set-autoplay", { playing })
|
|
184
180
|
}),
|
|
185
181
|
[send]
|
|
186
182
|
);
|
|
@@ -192,30 +188,47 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
192
188
|
if (!notes || !readyRef.current) return;
|
|
193
189
|
send("set-notes", { notes });
|
|
194
190
|
}, [notes, send]);
|
|
191
|
+
const closestTagIdRef = useRef(closestTagId);
|
|
192
|
+
const addNoteTagIdRef = useRef(addNoteTagId);
|
|
193
|
+
const activeLineIdRef = useRef(activeLineId);
|
|
194
|
+
const linesRef = useRef(lines);
|
|
195
|
+
const onNoteSubmitRef = useRef(onNoteSubmit);
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
closestTagIdRef.current = closestTagId;
|
|
198
|
+
}, [closestTagId]);
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
addNoteTagIdRef.current = addNoteTagId;
|
|
201
|
+
}, [addNoteTagId]);
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
activeLineIdRef.current = activeLineId;
|
|
204
|
+
}, [activeLineId]);
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
linesRef.current = lines;
|
|
207
|
+
}, [lines]);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
onNoteSubmitRef.current = onNoteSubmit;
|
|
210
|
+
}, [onNoteSubmit]);
|
|
195
211
|
const openAddNote = useCallback((tagId) => {
|
|
196
|
-
setAddNoteTagId(tagId ??
|
|
212
|
+
setAddNoteTagId(tagId ?? closestTagIdRef.current ?? null);
|
|
197
213
|
setIsAddNoteOpen(true);
|
|
198
|
-
}, [
|
|
214
|
+
}, []);
|
|
199
215
|
const closeAddNote = useCallback(() => {
|
|
200
216
|
setIsAddNoteOpen(false);
|
|
201
217
|
}, []);
|
|
202
|
-
const submitNote = useCallback(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
},
|
|
217
|
-
[addNoteTagId, activeLineId, lines, onNoteSubmit]
|
|
218
|
-
);
|
|
218
|
+
const submitNote = useCallback((text, isPrivate = true) => {
|
|
219
|
+
const tagId = addNoteTagIdRef.current;
|
|
220
|
+
const lineId = activeLineIdRef.current;
|
|
221
|
+
const tag = linesRef.current.find((l) => l._id === lineId)?.tags.find((t) => t._id === tagId);
|
|
222
|
+
const payload = {
|
|
223
|
+
text,
|
|
224
|
+
isPrivate,
|
|
225
|
+
tagId,
|
|
226
|
+
lineId,
|
|
227
|
+
position: tag?.position ?? null
|
|
228
|
+
};
|
|
229
|
+
onNoteSubmitRef.current?.(payload);
|
|
230
|
+
setIsAddNoteOpen(false);
|
|
231
|
+
}, []);
|
|
219
232
|
const handleMessage = useCallback(
|
|
220
233
|
(event) => {
|
|
221
234
|
let data = null;
|
|
@@ -255,6 +268,11 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
255
268
|
onLineProgress?.(payload);
|
|
256
269
|
break;
|
|
257
270
|
}
|
|
271
|
+
case "autoplay-state": {
|
|
272
|
+
const payload = data.payload;
|
|
273
|
+
setIsPlaying(!!payload.playing);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
258
276
|
default:
|
|
259
277
|
onMessage?.(data.type, data.payload);
|
|
260
278
|
}
|
|
@@ -327,7 +345,7 @@ function BlurBackground({ style }) {
|
|
|
327
345
|
}
|
|
328
346
|
|
|
329
347
|
// src/ProjectSheet.tsx
|
|
330
|
-
function ProjectSheet({ snapPoints = ["35%", "
|
|
348
|
+
function ProjectSheet({ snapPoints = ["35%", "75%"], renderHeader, children }) {
|
|
331
349
|
const { project, activeLineId, isAddNoteOpen } = useRetorBridge();
|
|
332
350
|
const sheetRef = useRef2(null);
|
|
333
351
|
const snapPointsArr = useMemo3(() => snapPoints, [snapPoints]);
|
|
@@ -335,11 +353,15 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
|
|
|
335
353
|
useEffect2(() => {
|
|
336
354
|
if (activeLineId || isAddNoteOpen) {
|
|
337
355
|
sheetRef.current?.dismiss();
|
|
338
|
-
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const t = setTimeout(() => {
|
|
339
359
|
sheetRef.current?.present();
|
|
360
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
340
361
|
setMinimized(false);
|
|
341
|
-
}
|
|
342
|
-
|
|
362
|
+
}, 80);
|
|
363
|
+
return () => clearTimeout(t);
|
|
364
|
+
}, [activeLineId, isAddNoteOpen, snapPointsArr.length]);
|
|
343
365
|
const handleSheetChange = (index) => {
|
|
344
366
|
setMinimized(index === 0);
|
|
345
367
|
};
|
|
@@ -357,6 +379,7 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
|
|
|
357
379
|
snapPoints: snapPointsArr,
|
|
358
380
|
enablePanDownToClose: false,
|
|
359
381
|
enableDismissOnClose: false,
|
|
382
|
+
enableOverDrag: false,
|
|
360
383
|
onChange: handleSheetChange,
|
|
361
384
|
backdropComponent: (props) => /* @__PURE__ */ React5.createElement(
|
|
362
385
|
BottomSheetBackdrop,
|
|
@@ -447,25 +470,29 @@ import React6, { useCallback as useCallback2, useEffect as useEffect3, useMemo a
|
|
|
447
470
|
import { Pressable as Pressable2, StyleSheet as StyleSheet5, Text as Text2, View as View5 } from "react-native";
|
|
448
471
|
import {
|
|
449
472
|
BottomSheetBackdrop as BottomSheetBackdrop2,
|
|
450
|
-
BottomSheetFlatList,
|
|
451
473
|
BottomSheetFooter,
|
|
452
|
-
BottomSheetModal as BottomSheetModal2
|
|
474
|
+
BottomSheetModal as BottomSheetModal2,
|
|
475
|
+
BottomSheetScrollView
|
|
453
476
|
} from "@gorhom/bottom-sheet";
|
|
454
477
|
import Svg, { Circle } from "react-native-svg";
|
|
455
478
|
import { ArrowDown as ArrowDown2, ArrowUp as ArrowUp2, Pause, Play, Plus } from "lucide-react-native";
|
|
456
|
-
function LineDetailSheet({ snapPoints = ["35%", "
|
|
479
|
+
function LineDetailSheet({ snapPoints = ["35%", "75%"], renderHeader, children }) {
|
|
457
480
|
const { activeLine, isAddNoteOpen, controls } = useRetorBridge();
|
|
458
481
|
const sheetRef = useRef3(null);
|
|
459
482
|
const snapPointsArr = useMemo4(() => snapPoints, [snapPoints]);
|
|
460
483
|
const [minimized, setMinimized] = useState3(false);
|
|
461
484
|
useEffect3(() => {
|
|
462
|
-
if (activeLine
|
|
463
|
-
sheetRef.current?.present();
|
|
464
|
-
setMinimized(false);
|
|
465
|
-
} else {
|
|
485
|
+
if (!activeLine || isAddNoteOpen) {
|
|
466
486
|
sheetRef.current?.dismiss();
|
|
487
|
+
return;
|
|
467
488
|
}
|
|
468
|
-
|
|
489
|
+
const t = setTimeout(() => {
|
|
490
|
+
sheetRef.current?.present();
|
|
491
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
492
|
+
setMinimized(false);
|
|
493
|
+
}, 80);
|
|
494
|
+
return () => clearTimeout(t);
|
|
495
|
+
}, [activeLine, isAddNoteOpen, snapPointsArr.length]);
|
|
469
496
|
const handleSheetChange = (index) => {
|
|
470
497
|
setMinimized(index === 0);
|
|
471
498
|
};
|
|
@@ -488,6 +515,7 @@ function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }
|
|
|
488
515
|
snapPoints: snapPointsArr,
|
|
489
516
|
enablePanDownToClose: false,
|
|
490
517
|
enableDismissOnClose: false,
|
|
518
|
+
enableOverDrag: false,
|
|
491
519
|
onChange: handleSheetChange,
|
|
492
520
|
footerComponent: renderFooter,
|
|
493
521
|
backdropComponent: (props) => /* @__PURE__ */ React6.createElement(
|
|
@@ -535,45 +563,37 @@ function AutoplayButton() {
|
|
|
535
563
|
}
|
|
536
564
|
function LineTagList({ children, listHeader }) {
|
|
537
565
|
const { activeLine, closestTagId } = useRetorBridge();
|
|
538
|
-
const
|
|
566
|
+
const scrollRef = useRef3(null);
|
|
567
|
+
const offsetsRef = useRef3(/* @__PURE__ */ new Map());
|
|
539
568
|
const tags = useMemo4(
|
|
540
569
|
() => (activeLine?.tags ?? []).filter((t) => t.name && t.name.trim().length > 0),
|
|
541
570
|
[activeLine]
|
|
542
571
|
);
|
|
572
|
+
useEffect3(() => {
|
|
573
|
+
offsetsRef.current = /* @__PURE__ */ new Map();
|
|
574
|
+
}, [activeLine?._id]);
|
|
543
575
|
useEffect3(() => {
|
|
544
576
|
if (!closestTagId) return;
|
|
545
|
-
const index = tags.findIndex((t2) => t2._id === closestTagId);
|
|
546
|
-
if (index < 0) return;
|
|
547
577
|
const t = setTimeout(() => {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
578
|
+
const y = offsetsRef.current.get(closestTagId);
|
|
579
|
+
if (y != null) {
|
|
580
|
+
scrollRef.current?.scrollTo({ y, animated: true });
|
|
551
581
|
}
|
|
552
|
-
},
|
|
582
|
+
}, 60);
|
|
553
583
|
return () => clearTimeout(t);
|
|
554
|
-
}, [closestTagId
|
|
584
|
+
}, [closestTagId]);
|
|
555
585
|
if (!activeLine) return null;
|
|
586
|
+
const handleItemLayout = (id, e) => {
|
|
587
|
+
offsetsRef.current.set(id, e.nativeEvent.layout.y);
|
|
588
|
+
};
|
|
556
589
|
return /* @__PURE__ */ React6.createElement(
|
|
557
|
-
|
|
590
|
+
BottomSheetScrollView,
|
|
558
591
|
{
|
|
559
|
-
ref:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
contentContainerStyle: styles3.list,
|
|
565
|
-
removeClippedSubviews: false,
|
|
566
|
-
onScrollToIndexFailed: (info) => {
|
|
567
|
-
const offset = info.averageItemLength * info.index;
|
|
568
|
-
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
569
|
-
setTimeout(() => {
|
|
570
|
-
try {
|
|
571
|
-
listRef.current?.scrollToIndex({ index: info.index, animated: true, viewPosition: 0 });
|
|
572
|
-
} catch {
|
|
573
|
-
}
|
|
574
|
-
}, 100);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
592
|
+
ref: scrollRef,
|
|
593
|
+
contentContainerStyle: styles3.list
|
|
594
|
+
},
|
|
595
|
+
listHeader,
|
|
596
|
+
tags.map((tag) => /* @__PURE__ */ React6.createElement(View5, { key: tag._id, onLayout: (e) => handleItemLayout(tag._id, e) }, children(tag, tag._id === closestTagId)))
|
|
577
597
|
);
|
|
578
598
|
}
|
|
579
599
|
function DefaultLineTagList({ listHeader }) {
|
|
@@ -625,10 +645,11 @@ var styles3 = StyleSheet5.create({
|
|
|
625
645
|
justifyContent: "center",
|
|
626
646
|
position: "relative"
|
|
627
647
|
},
|
|
628
|
-
list: {
|
|
648
|
+
list: { paddingBottom: 96, gap: 4 },
|
|
629
649
|
tagItem: {
|
|
630
650
|
flexDirection: "row",
|
|
631
651
|
alignItems: "center",
|
|
652
|
+
marginHorizontal: 16,
|
|
632
653
|
paddingHorizontal: 12,
|
|
633
654
|
paddingVertical: 12,
|
|
634
655
|
borderRadius: 12,
|
|
@@ -718,6 +739,7 @@ function AddNoteSheet({
|
|
|
718
739
|
ref: sheetRef,
|
|
719
740
|
snapPoints: snapPointsArr,
|
|
720
741
|
enablePanDownToClose: true,
|
|
742
|
+
enableOverDrag: false,
|
|
721
743
|
onDismiss: closeAddNote,
|
|
722
744
|
backdropComponent: (props) => /* @__PURE__ */ React7.createElement(
|
|
723
745
|
BottomSheetBackdrop3,
|