@retor/react-native 0.4.0 → 0.4.2

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/dist/index.d.mts CHANGED
@@ -17,6 +17,10 @@ interface RetorTag {
17
17
  tagType?: string;
18
18
  iconName?: string;
19
19
  objectId?: string;
20
+ /** 0..1 position along the parent line. Set on notes; computed from index for control tags. */
21
+ progress?: number;
22
+ /** Profile image URL. For notes rendered with `icon` tag type, replaces the icon with a circular avatar. */
23
+ avatarUrl?: string;
20
24
  }
21
25
  interface RetorLine {
22
26
  _id: string;
@@ -91,6 +95,10 @@ interface RetorBridgeContextValue {
91
95
  lines: RetorLine[];
92
96
  activeLineId: string | null;
93
97
  activeLine: RetorLine | null;
98
+ /** Notes for the active line, pushed by the bridge from Convex. */
99
+ lineNotes: RetorTag[];
100
+ /** Notes injected by the consumer via the <Notes> sentinel. */
101
+ externalNotes: RetorTag[];
94
102
  isAddNoteOpen: boolean;
95
103
  addNoteTagId: string | null;
96
104
  controls: ViewerHandle;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,10 @@ interface RetorTag {
17
17
  tagType?: string;
18
18
  iconName?: string;
19
19
  objectId?: string;
20
+ /** 0..1 position along the parent line. Set on notes; computed from index for control tags. */
21
+ progress?: number;
22
+ /** Profile image URL. For notes rendered with `icon` tag type, replaces the icon with a circular avatar. */
23
+ avatarUrl?: string;
20
24
  }
21
25
  interface RetorLine {
22
26
  _id: string;
@@ -91,6 +95,10 @@ interface RetorBridgeContextValue {
91
95
  lines: RetorLine[];
92
96
  activeLineId: string | null;
93
97
  activeLine: RetorLine | null;
98
+ /** Notes for the active line, pushed by the bridge from Convex. */
99
+ lineNotes: RetorTag[];
100
+ /** Notes injected by the consumer via the <Notes> sentinel. */
101
+ externalNotes: RetorTag[];
94
102
  isAddNoteOpen: boolean;
95
103
  addNoteTagId: string | null;
96
104
  controls: ViewerHandle;
package/dist/index.js CHANGED
@@ -75,6 +75,8 @@ var fallback = {
75
75
  lines: [],
76
76
  activeLineId: null,
77
77
  activeLine: null,
78
+ lineNotes: [],
79
+ externalNotes: [],
78
80
  isAddNoteOpen: false,
79
81
  addNoteTagId: null,
80
82
  controls: noopHandle,
@@ -213,6 +215,7 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
213
215
  const [isPlaying, setIsPlaying] = (0, import_react2.useState)(false);
214
216
  const [isAddNoteOpen, setIsAddNoteOpen] = (0, import_react2.useState)(false);
215
217
  const [addNoteTagId, setAddNoteTagId] = (0, import_react2.useState)(null);
218
+ const [lineNotes, setLineNotes] = (0, import_react2.useState)([]);
216
219
  const uri = (0, import_react2.useMemo)(() => `${baseUrl}/p/${projectId}?vanilla=true`, [baseUrl, projectId]);
217
220
  const send = (0, import_react2.useCallback)((type, payload) => {
218
221
  const message = JSON.stringify({ source: "retor-host", type, payload });
@@ -302,9 +305,13 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
302
305
  progress: snap.progress,
303
306
  distanceFromStart: snap.distanceFromStart
304
307
  };
305
- onNoteSubmitRef.current?.(payload);
308
+ if (onNoteSubmitRef.current) {
309
+ onNoteSubmitRef.current(payload);
310
+ } else {
311
+ send("note-submit", payload);
312
+ }
306
313
  setIsAddNoteOpen(false);
307
- }, []);
314
+ }, [send]);
308
315
  const handleMessage = (0, import_react2.useCallback)(
309
316
  (event) => {
310
317
  let data = null;
@@ -335,6 +342,7 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
335
342
  case "line-close":
336
343
  setActiveLineId(null);
337
344
  setIsPlaying(false);
345
+ setLineNotes([]);
338
346
  onLineClose?.();
339
347
  break;
340
348
  case "line-progress": {
@@ -346,6 +354,14 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
346
354
  onLineProgress?.(payload);
347
355
  break;
348
356
  }
357
+ case "line-notes": {
358
+ const payload = data.payload;
359
+ setLineNotes(payload.notes ?? []);
360
+ break;
361
+ }
362
+ case "request-add-note":
363
+ openAddNote();
364
+ break;
349
365
  case "autoplay-state": {
350
366
  const payload = data.payload;
351
367
  setIsPlaying(!!payload.playing);
@@ -355,18 +371,21 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
355
371
  onMessage?.(data.type, data.payload);
356
372
  }
357
373
  },
358
- [notes, send, onInit, onLineOpen, onLineClose, onLineProgress, onMessage]
374
+ [notes, send, onInit, onLineOpen, onLineClose, onLineProgress, onMessage, openAddNote]
359
375
  );
360
376
  const activeLine = (0, import_react2.useMemo)(
361
377
  () => lines.find((l) => l._id === activeLineId) ?? null,
362
378
  [lines, activeLineId]
363
379
  );
380
+ const externalNotes = (0, import_react2.useMemo)(() => notes ?? [], [notes]);
364
381
  const ctxValue = (0, import_react2.useMemo)(
365
382
  () => ({
366
383
  project,
367
384
  lines,
368
385
  activeLineId,
369
386
  activeLine,
387
+ lineNotes,
388
+ externalNotes,
370
389
  isAddNoteOpen,
371
390
  addNoteTagId,
372
391
  controls,
@@ -374,7 +393,7 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
374
393
  closeAddNote,
375
394
  submitNote
376
395
  }),
377
- [project, lines, activeLineId, activeLine, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote]
396
+ [project, lines, activeLineId, activeLine, lineNotes, externalNotes, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote]
378
397
  );
379
398
  const progressCtx = (0, import_react2.useMemo)(
380
399
  () => ({ progress, closestTagId, targetPosition, distanceFromStart, isPlaying }),
@@ -557,6 +576,24 @@ var import_react_native5 = require("react-native");
557
576
  var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
558
577
  var import_react_native_svg = __toESM(require("react-native-svg"));
559
578
  var import_lucide_react_native2 = require("lucide-react-native");
579
+
580
+ // src/lineProgress.ts
581
+ function mergeLineTagsByProgress(controls, notes) {
582
+ const sortedControls = [...controls].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
583
+ const lastIdx = Math.max(1, sortedControls.length - 1);
584
+ const merged = [];
585
+ for (const c of sortedControls) {
586
+ const idx = c.index ?? 0;
587
+ merged.push({ ...c, _isNote: false, progress: idx / lastIdx });
588
+ }
589
+ for (const n of notes) {
590
+ merged.push({ ...n, _isNote: true, progress: n.progress ?? 0 });
591
+ }
592
+ merged.sort((a, b) => a.progress - b.progress);
593
+ return merged;
594
+ }
595
+
596
+ // src/LineDetailSheet.tsx
560
597
  var renderBackdrop2 = (props) => /* @__PURE__ */ import_react6.default.createElement(
561
598
  import_bottom_sheet3.BottomSheetBackdrop,
562
599
  {
@@ -648,14 +685,20 @@ function AutoplayButton() {
648
685
  )), isPlaying ? /* @__PURE__ */ import_react6.default.createElement(import_lucide_react_native2.Pause, { size: 11, color: "white", fill: "white" }) : /* @__PURE__ */ import_react6.default.createElement(import_lucide_react_native2.Play, { size: 11, color: "white", fill: "white", style: { marginLeft: 1 } }));
649
686
  }
650
687
  function LineTagList({ children, listHeader }) {
651
- const { activeLine } = useRetorBridge();
688
+ const { activeLine, lineNotes, externalNotes } = useRetorBridge();
652
689
  const { closestTagId } = useRetorProgress();
653
690
  const scrollRef = (0, import_react6.useRef)(null);
654
691
  const offsetsRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
655
- const tags = (0, import_react6.useMemo)(
656
- () => (activeLine?.tags ?? []).filter((t) => t.name && t.name.trim().length > 0),
657
- [activeLine]
658
- );
692
+ const tags = (0, import_react6.useMemo)(() => {
693
+ if (!activeLine) return [];
694
+ const controls = (activeLine.tags ?? []).filter(
695
+ (t) => t.name && t.name.trim().length > 0
696
+ );
697
+ const allNotes = [...lineNotes, ...externalNotes].filter(
698
+ (n) => n.name && n.name.trim().length > 0
699
+ );
700
+ return mergeLineTagsByProgress(controls, allNotes);
701
+ }, [activeLine, lineNotes, externalNotes]);
659
702
  (0, import_react6.useEffect)(() => {
660
703
  offsetsRef.current = /* @__PURE__ */ new Map();
661
704
  }, [activeLine?._id]);
package/dist/index.mjs CHANGED
@@ -22,6 +22,8 @@ var fallback = {
22
22
  lines: [],
23
23
  activeLineId: null,
24
24
  activeLine: null,
25
+ lineNotes: [],
26
+ externalNotes: [],
25
27
  isAddNoteOpen: false,
26
28
  addNoteTagId: null,
27
29
  controls: noopHandle,
@@ -169,6 +171,7 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
169
171
  const [isPlaying, setIsPlaying] = useState(false);
170
172
  const [isAddNoteOpen, setIsAddNoteOpen] = useState(false);
171
173
  const [addNoteTagId, setAddNoteTagId] = useState(null);
174
+ const [lineNotes, setLineNotes] = useState([]);
172
175
  const uri = useMemo2(() => `${baseUrl}/p/${projectId}?vanilla=true`, [baseUrl, projectId]);
173
176
  const send = useCallback((type, payload) => {
174
177
  const message = JSON.stringify({ source: "retor-host", type, payload });
@@ -258,9 +261,13 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
258
261
  progress: snap.progress,
259
262
  distanceFromStart: snap.distanceFromStart
260
263
  };
261
- onNoteSubmitRef.current?.(payload);
264
+ if (onNoteSubmitRef.current) {
265
+ onNoteSubmitRef.current(payload);
266
+ } else {
267
+ send("note-submit", payload);
268
+ }
262
269
  setIsAddNoteOpen(false);
263
- }, []);
270
+ }, [send]);
264
271
  const handleMessage = useCallback(
265
272
  (event) => {
266
273
  let data = null;
@@ -291,6 +298,7 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
291
298
  case "line-close":
292
299
  setActiveLineId(null);
293
300
  setIsPlaying(false);
301
+ setLineNotes([]);
294
302
  onLineClose?.();
295
303
  break;
296
304
  case "line-progress": {
@@ -302,6 +310,14 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
302
310
  onLineProgress?.(payload);
303
311
  break;
304
312
  }
313
+ case "line-notes": {
314
+ const payload = data.payload;
315
+ setLineNotes(payload.notes ?? []);
316
+ break;
317
+ }
318
+ case "request-add-note":
319
+ openAddNote();
320
+ break;
305
321
  case "autoplay-state": {
306
322
  const payload = data.payload;
307
323
  setIsPlaying(!!payload.playing);
@@ -311,18 +327,21 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
311
327
  onMessage?.(data.type, data.payload);
312
328
  }
313
329
  },
314
- [notes, send, onInit, onLineOpen, onLineClose, onLineProgress, onMessage]
330
+ [notes, send, onInit, onLineOpen, onLineClose, onLineProgress, onMessage, openAddNote]
315
331
  );
316
332
  const activeLine = useMemo2(
317
333
  () => lines.find((l) => l._id === activeLineId) ?? null,
318
334
  [lines, activeLineId]
319
335
  );
336
+ const externalNotes = useMemo2(() => notes ?? [], [notes]);
320
337
  const ctxValue = useMemo2(
321
338
  () => ({
322
339
  project,
323
340
  lines,
324
341
  activeLineId,
325
342
  activeLine,
343
+ lineNotes,
344
+ externalNotes,
326
345
  isAddNoteOpen,
327
346
  addNoteTagId,
328
347
  controls,
@@ -330,7 +349,7 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
330
349
  closeAddNote,
331
350
  submitNote
332
351
  }),
333
- [project, lines, activeLineId, activeLine, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote]
352
+ [project, lines, activeLineId, activeLine, lineNotes, externalNotes, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote]
334
353
  );
335
354
  const progressCtx = useMemo2(
336
355
  () => ({ progress, closestTagId, targetPosition, distanceFromStart, isPlaying }),
@@ -518,6 +537,24 @@ import {
518
537
  } from "@gorhom/bottom-sheet";
519
538
  import Svg, { Circle } from "react-native-svg";
520
539
  import { ArrowDown as ArrowDown2, ArrowUp as ArrowUp2, Pause, Play, Plus } from "lucide-react-native";
540
+
541
+ // src/lineProgress.ts
542
+ function mergeLineTagsByProgress(controls, notes) {
543
+ const sortedControls = [...controls].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
544
+ const lastIdx = Math.max(1, sortedControls.length - 1);
545
+ const merged = [];
546
+ for (const c of sortedControls) {
547
+ const idx = c.index ?? 0;
548
+ merged.push({ ...c, _isNote: false, progress: idx / lastIdx });
549
+ }
550
+ for (const n of notes) {
551
+ merged.push({ ...n, _isNote: true, progress: n.progress ?? 0 });
552
+ }
553
+ merged.sort((a, b) => a.progress - b.progress);
554
+ return merged;
555
+ }
556
+
557
+ // src/LineDetailSheet.tsx
521
558
  var renderBackdrop2 = (props) => /* @__PURE__ */ React6.createElement(
522
559
  BottomSheetBackdrop2,
523
560
  {
@@ -609,14 +646,20 @@ function AutoplayButton() {
609
646
  )), isPlaying ? /* @__PURE__ */ React6.createElement(Pause, { size: 11, color: "white", fill: "white" }) : /* @__PURE__ */ React6.createElement(Play, { size: 11, color: "white", fill: "white", style: { marginLeft: 1 } }));
610
647
  }
611
648
  function LineTagList({ children, listHeader }) {
612
- const { activeLine } = useRetorBridge();
649
+ const { activeLine, lineNotes, externalNotes } = useRetorBridge();
613
650
  const { closestTagId } = useRetorProgress();
614
651
  const scrollRef = useRef3(null);
615
652
  const offsetsRef = useRef3(/* @__PURE__ */ new Map());
616
- const tags = useMemo4(
617
- () => (activeLine?.tags ?? []).filter((t) => t.name && t.name.trim().length > 0),
618
- [activeLine]
619
- );
653
+ const tags = useMemo4(() => {
654
+ if (!activeLine) return [];
655
+ const controls = (activeLine.tags ?? []).filter(
656
+ (t) => t.name && t.name.trim().length > 0
657
+ );
658
+ const allNotes = [...lineNotes, ...externalNotes].filter(
659
+ (n) => n.name && n.name.trim().length > 0
660
+ );
661
+ return mergeLineTagsByProgress(controls, allNotes);
662
+ }, [activeLine, lineNotes, externalNotes]);
620
663
  useEffect3(() => {
621
664
  offsetsRef.current = /* @__PURE__ */ new Map();
622
665
  }, [activeLine?._id]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retor/react-native",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "React Native SDK for embedding Retor 3D experiences",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",