@retor/react-native 0.4.3 → 0.4.5
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 +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +54 -9
- package/dist/index.mjs +56 -11
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -23,6 +23,8 @@ interface RetorTag {
|
|
|
23
23
|
avatarUrl?: string;
|
|
24
24
|
/** Display name of the note's author. Used for the initial-letter fallback when `avatarUrl` is absent. */
|
|
25
25
|
authorName?: string;
|
|
26
|
+
/** ID of the user who created this note. Compared against `Viewer.userId` to show the delete button. */
|
|
27
|
+
userId?: string;
|
|
26
28
|
}
|
|
27
29
|
interface RetorLine {
|
|
28
30
|
_id: string;
|
|
@@ -63,6 +65,8 @@ interface ViewerHandle {
|
|
|
63
65
|
openLine: (lineId: string) => void;
|
|
64
66
|
exitLine: () => void;
|
|
65
67
|
scrollToTag: (tagId: string) => void;
|
|
68
|
+
/** Scroll the active line's camera to a 0..1 position. Use for notes (which aren't indexed by tagId on the server). */
|
|
69
|
+
scrollToProgress: (t: number) => void;
|
|
66
70
|
toggleAutoplay: () => void;
|
|
67
71
|
setAutoplay: (playing: boolean) => void;
|
|
68
72
|
}
|
|
@@ -101,12 +105,16 @@ interface RetorBridgeContextValue {
|
|
|
101
105
|
lineNotes: RetorTag[];
|
|
102
106
|
/** Notes injected by the consumer via the <Notes> sentinel. */
|
|
103
107
|
externalNotes: RetorTag[];
|
|
108
|
+
/** The current user's ID — notes with a matching `userId` show a delete button. */
|
|
109
|
+
userId: string | null;
|
|
104
110
|
isAddNoteOpen: boolean;
|
|
105
111
|
addNoteTagId: string | null;
|
|
106
112
|
controls: ViewerHandle;
|
|
107
113
|
openAddNote: (tagId?: string) => void;
|
|
108
114
|
closeAddNote: () => void;
|
|
109
115
|
submitNote: (text: string, isPrivate?: boolean) => void;
|
|
116
|
+
/** Optimistically remove a note and fire the consumer's onNoteDelete callback. */
|
|
117
|
+
deleteNote: (noteId: string) => void;
|
|
110
118
|
}
|
|
111
119
|
declare function useRetorBridge(): RetorBridgeContextValue;
|
|
112
120
|
/** Returns the array of all lines in the current project. */
|
|
@@ -185,8 +193,12 @@ interface ViewerProps {
|
|
|
185
193
|
id?: string;
|
|
186
194
|
/** Base URL where Retor is hosted. Defaults to https://retor.app */
|
|
187
195
|
baseUrl?: string;
|
|
196
|
+
/** The current user's ID. When set, notes with a matching `userId` field show a delete button. */
|
|
197
|
+
userId?: string;
|
|
188
198
|
/** Called when a note is submitted via `<AddNoteSheet>`. Receives the text + position. */
|
|
189
199
|
onNoteSubmit?: (note: NoteSubmitPayload) => void;
|
|
200
|
+
/** Called when the user deletes a note. The note is optimistically removed from the list. */
|
|
201
|
+
onNoteDelete?: (noteId: string) => void;
|
|
190
202
|
onInit?: (data: InitPayload) => void;
|
|
191
203
|
onLineOpen?: (data: {
|
|
192
204
|
lineId: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ interface RetorTag {
|
|
|
23
23
|
avatarUrl?: string;
|
|
24
24
|
/** Display name of the note's author. Used for the initial-letter fallback when `avatarUrl` is absent. */
|
|
25
25
|
authorName?: string;
|
|
26
|
+
/** ID of the user who created this note. Compared against `Viewer.userId` to show the delete button. */
|
|
27
|
+
userId?: string;
|
|
26
28
|
}
|
|
27
29
|
interface RetorLine {
|
|
28
30
|
_id: string;
|
|
@@ -63,6 +65,8 @@ interface ViewerHandle {
|
|
|
63
65
|
openLine: (lineId: string) => void;
|
|
64
66
|
exitLine: () => void;
|
|
65
67
|
scrollToTag: (tagId: string) => void;
|
|
68
|
+
/** Scroll the active line's camera to a 0..1 position. Use for notes (which aren't indexed by tagId on the server). */
|
|
69
|
+
scrollToProgress: (t: number) => void;
|
|
66
70
|
toggleAutoplay: () => void;
|
|
67
71
|
setAutoplay: (playing: boolean) => void;
|
|
68
72
|
}
|
|
@@ -101,12 +105,16 @@ interface RetorBridgeContextValue {
|
|
|
101
105
|
lineNotes: RetorTag[];
|
|
102
106
|
/** Notes injected by the consumer via the <Notes> sentinel. */
|
|
103
107
|
externalNotes: RetorTag[];
|
|
108
|
+
/** The current user's ID — notes with a matching `userId` show a delete button. */
|
|
109
|
+
userId: string | null;
|
|
104
110
|
isAddNoteOpen: boolean;
|
|
105
111
|
addNoteTagId: string | null;
|
|
106
112
|
controls: ViewerHandle;
|
|
107
113
|
openAddNote: (tagId?: string) => void;
|
|
108
114
|
closeAddNote: () => void;
|
|
109
115
|
submitNote: (text: string, isPrivate?: boolean) => void;
|
|
116
|
+
/** Optimistically remove a note and fire the consumer's onNoteDelete callback. */
|
|
117
|
+
deleteNote: (noteId: string) => void;
|
|
110
118
|
}
|
|
111
119
|
declare function useRetorBridge(): RetorBridgeContextValue;
|
|
112
120
|
/** Returns the array of all lines in the current project. */
|
|
@@ -185,8 +193,12 @@ interface ViewerProps {
|
|
|
185
193
|
id?: string;
|
|
186
194
|
/** Base URL where Retor is hosted. Defaults to https://retor.app */
|
|
187
195
|
baseUrl?: string;
|
|
196
|
+
/** The current user's ID. When set, notes with a matching `userId` field show a delete button. */
|
|
197
|
+
userId?: string;
|
|
188
198
|
/** Called when a note is submitted via `<AddNoteSheet>`. Receives the text + position. */
|
|
189
199
|
onNoteSubmit?: (note: NoteSubmitPayload) => void;
|
|
200
|
+
/** Called when the user deletes a note. The note is optimistically removed from the list. */
|
|
201
|
+
onNoteDelete?: (noteId: string) => void;
|
|
190
202
|
onInit?: (data: InitPayload) => void;
|
|
191
203
|
onLineOpen?: (data: {
|
|
192
204
|
lineId: string;
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,7 @@ var noopHandle = {
|
|
|
67
67
|
openLine: noop,
|
|
68
68
|
exitLine: noop,
|
|
69
69
|
scrollToTag: noop,
|
|
70
|
+
scrollToProgress: noop,
|
|
70
71
|
toggleAutoplay: noop,
|
|
71
72
|
setAutoplay: noop
|
|
72
73
|
};
|
|
@@ -77,12 +78,14 @@ var fallback = {
|
|
|
77
78
|
activeLine: null,
|
|
78
79
|
lineNotes: [],
|
|
79
80
|
externalNotes: [],
|
|
81
|
+
userId: null,
|
|
80
82
|
isAddNoteOpen: false,
|
|
81
83
|
addNoteTagId: null,
|
|
82
84
|
controls: noopHandle,
|
|
83
85
|
openAddNote: noop,
|
|
84
86
|
closeAddNote: noop,
|
|
85
|
-
submitNote: noop
|
|
87
|
+
submitNote: noop,
|
|
88
|
+
deleteNote: noop
|
|
86
89
|
};
|
|
87
90
|
function useRetorBridge() {
|
|
88
91
|
return (0, import_react.useContext)(RetorBridgeContext) ?? fallback;
|
|
@@ -176,6 +179,9 @@ function useViewer(target = "default") {
|
|
|
176
179
|
scrollToTag: (tagId) => {
|
|
177
180
|
resolve()?.scrollToTag(tagId);
|
|
178
181
|
},
|
|
182
|
+
scrollToProgress: (t) => {
|
|
183
|
+
resolve()?.scrollToProgress(t);
|
|
184
|
+
},
|
|
179
185
|
toggleAutoplay: () => {
|
|
180
186
|
resolve()?.toggleAutoplay();
|
|
181
187
|
},
|
|
@@ -201,7 +207,7 @@ function extractNotes(children) {
|
|
|
201
207
|
});
|
|
202
208
|
return notes;
|
|
203
209
|
}
|
|
204
|
-
var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "default", baseUrl = "https://retor.app", onNoteSubmit, onInit, onLineOpen, onLineClose, onLineProgress, onMessage, style, children }, ref) {
|
|
210
|
+
var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "default", baseUrl = "https://retor.app", userId = null, onNoteSubmit, onNoteDelete, onInit, onLineOpen, onLineClose, onLineProgress, onMessage, style, children }, ref) {
|
|
205
211
|
const webviewRef = (0, import_react2.useRef)(null);
|
|
206
212
|
const notes = extractNotes(children);
|
|
207
213
|
const readyRef = (0, import_react2.useRef)(false);
|
|
@@ -236,6 +242,7 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
236
242
|
openLine: (lineId) => send("open-line", { lineId }),
|
|
237
243
|
exitLine: () => send("exit-line"),
|
|
238
244
|
scrollToTag: (tagId) => send("scroll-to-tag", { tagId }),
|
|
245
|
+
scrollToProgress: (t) => send("scroll-to-progress", { t }),
|
|
239
246
|
// Bridge owns autoplay state — it'll emit `autoplay-state` back, which
|
|
240
247
|
// we use to update isPlaying. No optimistic local update.
|
|
241
248
|
toggleAutoplay: () => send("toggle-autoplay"),
|
|
@@ -255,6 +262,7 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
255
262
|
const addNoteTagIdRef = (0, import_react2.useRef)(addNoteTagId);
|
|
256
263
|
const activeLineIdRef = (0, import_react2.useRef)(activeLineId);
|
|
257
264
|
const onNoteSubmitRef = (0, import_react2.useRef)(onNoteSubmit);
|
|
265
|
+
const onNoteDeleteRef = (0, import_react2.useRef)(onNoteDelete);
|
|
258
266
|
const noteSnapshotRef = (0, import_react2.useRef)({ progress: 0, targetPosition: null, distanceFromStart: null });
|
|
259
267
|
const progressRef = (0, import_react2.useRef)(progress);
|
|
260
268
|
const targetPositionRef = (0, import_react2.useRef)(targetPosition);
|
|
@@ -271,6 +279,9 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
271
279
|
(0, import_react2.useEffect)(() => {
|
|
272
280
|
onNoteSubmitRef.current = onNoteSubmit;
|
|
273
281
|
}, [onNoteSubmit]);
|
|
282
|
+
(0, import_react2.useEffect)(() => {
|
|
283
|
+
onNoteDeleteRef.current = onNoteDelete;
|
|
284
|
+
}, [onNoteDelete]);
|
|
274
285
|
(0, import_react2.useEffect)(() => {
|
|
275
286
|
progressRef.current = progress;
|
|
276
287
|
}, [progress]);
|
|
@@ -312,6 +323,10 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
312
323
|
}
|
|
313
324
|
setIsAddNoteOpen(false);
|
|
314
325
|
}, [send]);
|
|
326
|
+
const deleteNote = (0, import_react2.useCallback)((noteId) => {
|
|
327
|
+
setLineNotes((prev) => prev.filter((n) => n._id !== noteId));
|
|
328
|
+
onNoteDeleteRef.current?.(noteId);
|
|
329
|
+
}, []);
|
|
315
330
|
const handleMessage = (0, import_react2.useCallback)(
|
|
316
331
|
(event) => {
|
|
317
332
|
let data = null;
|
|
@@ -386,14 +401,16 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
386
401
|
activeLine,
|
|
387
402
|
lineNotes,
|
|
388
403
|
externalNotes,
|
|
404
|
+
userId,
|
|
389
405
|
isAddNoteOpen,
|
|
390
406
|
addNoteTagId,
|
|
391
407
|
controls,
|
|
392
408
|
openAddNote,
|
|
393
409
|
closeAddNote,
|
|
394
|
-
submitNote
|
|
410
|
+
submitNote,
|
|
411
|
+
deleteNote
|
|
395
412
|
}),
|
|
396
|
-
[project, lines, activeLineId, activeLine, lineNotes, externalNotes, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote]
|
|
413
|
+
[project, lines, activeLineId, activeLine, lineNotes, externalNotes, userId, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote, deleteNote]
|
|
397
414
|
);
|
|
398
415
|
const progressCtx = (0, import_react2.useMemo)(
|
|
399
416
|
() => ({ progress, closestTagId, targetPosition, distanceFromStart, isPlaying }),
|
|
@@ -722,25 +739,45 @@ function LineTagList({ children, listHeader }) {
|
|
|
722
739
|
contentContainerStyle: styles3.list
|
|
723
740
|
},
|
|
724
741
|
listHeader,
|
|
725
|
-
tags.map((tag
|
|
742
|
+
tags.map((tag, i) => {
|
|
743
|
+
const isNote = !!tag._isNote;
|
|
744
|
+
const prevIsNote = i > 0 && !!tags[i - 1]._isNote;
|
|
745
|
+
const showSep = isNote && !prevIsNote && i > 0 || !isNote && i > 0 && prevIsNote;
|
|
746
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_react6.Fragment, { key: tag._id }, showSep && /* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { style: styles3.separator }), /* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { onLayout: (e) => handleItemLayout(tag._id, e) }, children(tag, tag._id === closestTagId)));
|
|
747
|
+
})
|
|
726
748
|
);
|
|
727
749
|
}
|
|
728
750
|
function DefaultLineTagList({ listHeader }) {
|
|
729
751
|
return /* @__PURE__ */ import_react6.default.createElement(LineTagList, { listHeader }, (tag, isActive) => /* @__PURE__ */ import_react6.default.createElement(DefaultTagItem, { tag, isActive }));
|
|
730
752
|
}
|
|
731
753
|
function DefaultTagItem({ tag, isActive }) {
|
|
732
|
-
const { controls, openAddNote } = useRetorBridge();
|
|
754
|
+
const { controls, openAddNote, userId, deleteNote } = useRetorBridge();
|
|
755
|
+
const isNote = !!tag._isNote;
|
|
756
|
+
const tagUserId = tag.userId;
|
|
757
|
+
const isOwn = !!userId && !!tagUserId && tagUserId === userId;
|
|
733
758
|
const initial = tag.authorName?.trim().charAt(0).toUpperCase();
|
|
759
|
+
const handlePress = () => {
|
|
760
|
+
if (typeof tag.progress === "number") controls.scrollToProgress(tag.progress);
|
|
761
|
+
else controls.scrollToTag(tag._id);
|
|
762
|
+
};
|
|
734
763
|
return /* @__PURE__ */ import_react6.default.createElement(
|
|
735
764
|
import_react_native5.Pressable,
|
|
736
765
|
{
|
|
737
|
-
onPress:
|
|
766
|
+
onPress: handlePress,
|
|
738
767
|
style: [styles3.tagItem, isActive && styles3.tagItemActive]
|
|
739
768
|
},
|
|
740
769
|
tag.avatarUrl ? /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Image, { source: { uri: tag.avatarUrl }, style: styles3.tagAvatar }) : initial ? /* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { style: styles3.tagInitial }, /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Text, { style: styles3.tagInitialText }, initial)) : null,
|
|
741
|
-
/* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Text, { style: [styles3.tagText, isActive && styles3.tagTextActive], numberOfLines: 1 }, tag.name), isActive && tag.description && /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Text, { style: styles3.tagDescription, numberOfLines: 2 }, tag.description)),
|
|
770
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Text, { style: [styles3.tagText, isActive && styles3.tagTextActive], numberOfLines: isNote ? 3 : 1 }, tag.name), isActive && tag.description && /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Text, { style: styles3.tagDescription, numberOfLines: 2 }, tag.description)),
|
|
742
771
|
tag.subtitle && /* @__PURE__ */ import_react6.default.createElement(import_react_native5.Text, { style: styles3.tagSubtitle, numberOfLines: 1 }, tag.subtitle),
|
|
743
|
-
|
|
772
|
+
isNote && isOwn && /* @__PURE__ */ import_react6.default.createElement(
|
|
773
|
+
import_react_native5.Pressable,
|
|
774
|
+
{
|
|
775
|
+
onPress: () => deleteNote(tag._id),
|
|
776
|
+
style: styles3.deleteBtn
|
|
777
|
+
},
|
|
778
|
+
/* @__PURE__ */ import_react6.default.createElement(import_lucide_react_native2.Trash2, { size: 12, color: "rgba(255,255,255,0.4)" })
|
|
779
|
+
),
|
|
780
|
+
isActive && !isNote && /* @__PURE__ */ import_react6.default.createElement(
|
|
744
781
|
import_react_native5.Pressable,
|
|
745
782
|
{
|
|
746
783
|
onPress: (e) => {
|
|
@@ -797,10 +834,18 @@ var styles3 = import_react_native5.StyleSheet.create({
|
|
|
797
834
|
justifyContent: "center"
|
|
798
835
|
},
|
|
799
836
|
tagInitialText: { color: "white", fontSize: 11, fontWeight: "700" },
|
|
837
|
+
separator: { height: 1, backgroundColor: "rgba(255,255,255,0.1)", marginHorizontal: 20, marginVertical: 4 },
|
|
800
838
|
tagText: { color: "rgba(255,255,255,0.6)", fontSize: 13 },
|
|
801
839
|
tagTextActive: { color: "white", fontWeight: "600" },
|
|
802
840
|
tagDescription: { color: "rgba(255,255,255,0.4)", fontSize: 11, marginTop: 2, lineHeight: 14 },
|
|
803
841
|
tagSubtitle: { color: "rgba(255,255,255,0.4)", fontSize: 10 },
|
|
842
|
+
deleteBtn: {
|
|
843
|
+
width: 24,
|
|
844
|
+
height: 24,
|
|
845
|
+
borderRadius: 12,
|
|
846
|
+
alignItems: "center",
|
|
847
|
+
justifyContent: "center"
|
|
848
|
+
},
|
|
804
849
|
plusBtn: {
|
|
805
850
|
width: 24,
|
|
806
851
|
height: 24,
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ var noopHandle = {
|
|
|
14
14
|
openLine: noop,
|
|
15
15
|
exitLine: noop,
|
|
16
16
|
scrollToTag: noop,
|
|
17
|
+
scrollToProgress: noop,
|
|
17
18
|
toggleAutoplay: noop,
|
|
18
19
|
setAutoplay: noop
|
|
19
20
|
};
|
|
@@ -24,12 +25,14 @@ var fallback = {
|
|
|
24
25
|
activeLine: null,
|
|
25
26
|
lineNotes: [],
|
|
26
27
|
externalNotes: [],
|
|
28
|
+
userId: null,
|
|
27
29
|
isAddNoteOpen: false,
|
|
28
30
|
addNoteTagId: null,
|
|
29
31
|
controls: noopHandle,
|
|
30
32
|
openAddNote: noop,
|
|
31
33
|
closeAddNote: noop,
|
|
32
|
-
submitNote: noop
|
|
34
|
+
submitNote: noop,
|
|
35
|
+
deleteNote: noop
|
|
33
36
|
};
|
|
34
37
|
function useRetorBridge() {
|
|
35
38
|
return useContext(RetorBridgeContext) ?? fallback;
|
|
@@ -132,6 +135,9 @@ function useViewer(target = "default") {
|
|
|
132
135
|
scrollToTag: (tagId) => {
|
|
133
136
|
resolve()?.scrollToTag(tagId);
|
|
134
137
|
},
|
|
138
|
+
scrollToProgress: (t) => {
|
|
139
|
+
resolve()?.scrollToProgress(t);
|
|
140
|
+
},
|
|
135
141
|
toggleAutoplay: () => {
|
|
136
142
|
resolve()?.toggleAutoplay();
|
|
137
143
|
},
|
|
@@ -157,7 +163,7 @@ function extractNotes(children) {
|
|
|
157
163
|
});
|
|
158
164
|
return notes;
|
|
159
165
|
}
|
|
160
|
-
var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl = "https://retor.app", onNoteSubmit, onInit, onLineOpen, onLineClose, onLineProgress, onMessage, style, children }, ref) {
|
|
166
|
+
var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl = "https://retor.app", userId = null, onNoteSubmit, onNoteDelete, onInit, onLineOpen, onLineClose, onLineProgress, onMessage, style, children }, ref) {
|
|
161
167
|
const webviewRef = useRef(null);
|
|
162
168
|
const notes = extractNotes(children);
|
|
163
169
|
const readyRef = useRef(false);
|
|
@@ -192,6 +198,7 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
192
198
|
openLine: (lineId) => send("open-line", { lineId }),
|
|
193
199
|
exitLine: () => send("exit-line"),
|
|
194
200
|
scrollToTag: (tagId) => send("scroll-to-tag", { tagId }),
|
|
201
|
+
scrollToProgress: (t) => send("scroll-to-progress", { t }),
|
|
195
202
|
// Bridge owns autoplay state — it'll emit `autoplay-state` back, which
|
|
196
203
|
// we use to update isPlaying. No optimistic local update.
|
|
197
204
|
toggleAutoplay: () => send("toggle-autoplay"),
|
|
@@ -211,6 +218,7 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
211
218
|
const addNoteTagIdRef = useRef(addNoteTagId);
|
|
212
219
|
const activeLineIdRef = useRef(activeLineId);
|
|
213
220
|
const onNoteSubmitRef = useRef(onNoteSubmit);
|
|
221
|
+
const onNoteDeleteRef = useRef(onNoteDelete);
|
|
214
222
|
const noteSnapshotRef = useRef({ progress: 0, targetPosition: null, distanceFromStart: null });
|
|
215
223
|
const progressRef = useRef(progress);
|
|
216
224
|
const targetPositionRef = useRef(targetPosition);
|
|
@@ -227,6 +235,9 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
227
235
|
useEffect(() => {
|
|
228
236
|
onNoteSubmitRef.current = onNoteSubmit;
|
|
229
237
|
}, [onNoteSubmit]);
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
onNoteDeleteRef.current = onNoteDelete;
|
|
240
|
+
}, [onNoteDelete]);
|
|
230
241
|
useEffect(() => {
|
|
231
242
|
progressRef.current = progress;
|
|
232
243
|
}, [progress]);
|
|
@@ -268,6 +279,10 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
268
279
|
}
|
|
269
280
|
setIsAddNoteOpen(false);
|
|
270
281
|
}, [send]);
|
|
282
|
+
const deleteNote = useCallback((noteId) => {
|
|
283
|
+
setLineNotes((prev) => prev.filter((n) => n._id !== noteId));
|
|
284
|
+
onNoteDeleteRef.current?.(noteId);
|
|
285
|
+
}, []);
|
|
271
286
|
const handleMessage = useCallback(
|
|
272
287
|
(event) => {
|
|
273
288
|
let data = null;
|
|
@@ -342,14 +357,16 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
342
357
|
activeLine,
|
|
343
358
|
lineNotes,
|
|
344
359
|
externalNotes,
|
|
360
|
+
userId,
|
|
345
361
|
isAddNoteOpen,
|
|
346
362
|
addNoteTagId,
|
|
347
363
|
controls,
|
|
348
364
|
openAddNote,
|
|
349
365
|
closeAddNote,
|
|
350
|
-
submitNote
|
|
366
|
+
submitNote,
|
|
367
|
+
deleteNote
|
|
351
368
|
}),
|
|
352
|
-
[project, lines, activeLineId, activeLine, lineNotes, externalNotes, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote]
|
|
369
|
+
[project, lines, activeLineId, activeLine, lineNotes, externalNotes, userId, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote, deleteNote]
|
|
353
370
|
);
|
|
354
371
|
const progressCtx = useMemo2(
|
|
355
372
|
() => ({ progress, closestTagId, targetPosition, distanceFromStart, isPlaying }),
|
|
@@ -527,7 +544,7 @@ var styles2 = StyleSheet4.create({
|
|
|
527
544
|
});
|
|
528
545
|
|
|
529
546
|
// src/LineDetailSheet.tsx
|
|
530
|
-
import React6, { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
|
|
547
|
+
import React6, { Fragment, useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
|
|
531
548
|
import { Image, Pressable as Pressable2, StyleSheet as StyleSheet5, Text as Text2, View as View5 } from "react-native";
|
|
532
549
|
import {
|
|
533
550
|
BottomSheetBackdrop as BottomSheetBackdrop2,
|
|
@@ -536,7 +553,7 @@ import {
|
|
|
536
553
|
BottomSheetScrollView
|
|
537
554
|
} from "@gorhom/bottom-sheet";
|
|
538
555
|
import Svg, { Circle } from "react-native-svg";
|
|
539
|
-
import { ArrowDown as ArrowDown2, ArrowUp as ArrowUp2, Pause, Play, Plus } from "lucide-react-native";
|
|
556
|
+
import { ArrowDown as ArrowDown2, ArrowUp as ArrowUp2, Pause, Play, Plus, Trash2 } from "lucide-react-native";
|
|
540
557
|
|
|
541
558
|
// src/lineProgress.ts
|
|
542
559
|
function mergeLineTagsByProgress(controls, notes) {
|
|
@@ -683,25 +700,45 @@ function LineTagList({ children, listHeader }) {
|
|
|
683
700
|
contentContainerStyle: styles3.list
|
|
684
701
|
},
|
|
685
702
|
listHeader,
|
|
686
|
-
tags.map((tag
|
|
703
|
+
tags.map((tag, i) => {
|
|
704
|
+
const isNote = !!tag._isNote;
|
|
705
|
+
const prevIsNote = i > 0 && !!tags[i - 1]._isNote;
|
|
706
|
+
const showSep = isNote && !prevIsNote && i > 0 || !isNote && i > 0 && prevIsNote;
|
|
707
|
+
return /* @__PURE__ */ React6.createElement(Fragment, { key: tag._id }, showSep && /* @__PURE__ */ React6.createElement(View5, { style: styles3.separator }), /* @__PURE__ */ React6.createElement(View5, { onLayout: (e) => handleItemLayout(tag._id, e) }, children(tag, tag._id === closestTagId)));
|
|
708
|
+
})
|
|
687
709
|
);
|
|
688
710
|
}
|
|
689
711
|
function DefaultLineTagList({ listHeader }) {
|
|
690
712
|
return /* @__PURE__ */ React6.createElement(LineTagList, { listHeader }, (tag, isActive) => /* @__PURE__ */ React6.createElement(DefaultTagItem, { tag, isActive }));
|
|
691
713
|
}
|
|
692
714
|
function DefaultTagItem({ tag, isActive }) {
|
|
693
|
-
const { controls, openAddNote } = useRetorBridge();
|
|
715
|
+
const { controls, openAddNote, userId, deleteNote } = useRetorBridge();
|
|
716
|
+
const isNote = !!tag._isNote;
|
|
717
|
+
const tagUserId = tag.userId;
|
|
718
|
+
const isOwn = !!userId && !!tagUserId && tagUserId === userId;
|
|
694
719
|
const initial = tag.authorName?.trim().charAt(0).toUpperCase();
|
|
720
|
+
const handlePress = () => {
|
|
721
|
+
if (typeof tag.progress === "number") controls.scrollToProgress(tag.progress);
|
|
722
|
+
else controls.scrollToTag(tag._id);
|
|
723
|
+
};
|
|
695
724
|
return /* @__PURE__ */ React6.createElement(
|
|
696
725
|
Pressable2,
|
|
697
726
|
{
|
|
698
|
-
onPress:
|
|
727
|
+
onPress: handlePress,
|
|
699
728
|
style: [styles3.tagItem, isActive && styles3.tagItemActive]
|
|
700
729
|
},
|
|
701
730
|
tag.avatarUrl ? /* @__PURE__ */ React6.createElement(Image, { source: { uri: tag.avatarUrl }, style: styles3.tagAvatar }) : initial ? /* @__PURE__ */ React6.createElement(View5, { style: styles3.tagInitial }, /* @__PURE__ */ React6.createElement(Text2, { style: styles3.tagInitialText }, initial)) : null,
|
|
702
|
-
/* @__PURE__ */ React6.createElement(View5, { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React6.createElement(Text2, { style: [styles3.tagText, isActive && styles3.tagTextActive], numberOfLines: 1 }, tag.name), isActive && tag.description && /* @__PURE__ */ React6.createElement(Text2, { style: styles3.tagDescription, numberOfLines: 2 }, tag.description)),
|
|
731
|
+
/* @__PURE__ */ React6.createElement(View5, { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React6.createElement(Text2, { style: [styles3.tagText, isActive && styles3.tagTextActive], numberOfLines: isNote ? 3 : 1 }, tag.name), isActive && tag.description && /* @__PURE__ */ React6.createElement(Text2, { style: styles3.tagDescription, numberOfLines: 2 }, tag.description)),
|
|
703
732
|
tag.subtitle && /* @__PURE__ */ React6.createElement(Text2, { style: styles3.tagSubtitle, numberOfLines: 1 }, tag.subtitle),
|
|
704
|
-
|
|
733
|
+
isNote && isOwn && /* @__PURE__ */ React6.createElement(
|
|
734
|
+
Pressable2,
|
|
735
|
+
{
|
|
736
|
+
onPress: () => deleteNote(tag._id),
|
|
737
|
+
style: styles3.deleteBtn
|
|
738
|
+
},
|
|
739
|
+
/* @__PURE__ */ React6.createElement(Trash2, { size: 12, color: "rgba(255,255,255,0.4)" })
|
|
740
|
+
),
|
|
741
|
+
isActive && !isNote && /* @__PURE__ */ React6.createElement(
|
|
705
742
|
Pressable2,
|
|
706
743
|
{
|
|
707
744
|
onPress: (e) => {
|
|
@@ -758,10 +795,18 @@ var styles3 = StyleSheet5.create({
|
|
|
758
795
|
justifyContent: "center"
|
|
759
796
|
},
|
|
760
797
|
tagInitialText: { color: "white", fontSize: 11, fontWeight: "700" },
|
|
798
|
+
separator: { height: 1, backgroundColor: "rgba(255,255,255,0.1)", marginHorizontal: 20, marginVertical: 4 },
|
|
761
799
|
tagText: { color: "rgba(255,255,255,0.6)", fontSize: 13 },
|
|
762
800
|
tagTextActive: { color: "white", fontWeight: "600" },
|
|
763
801
|
tagDescription: { color: "rgba(255,255,255,0.4)", fontSize: 11, marginTop: 2, lineHeight: 14 },
|
|
764
802
|
tagSubtitle: { color: "rgba(255,255,255,0.4)", fontSize: 10 },
|
|
803
|
+
deleteBtn: {
|
|
804
|
+
width: 24,
|
|
805
|
+
height: 24,
|
|
806
|
+
borderRadius: 12,
|
|
807
|
+
alignItems: "center",
|
|
808
|
+
justifyContent: "center"
|
|
809
|
+
},
|
|
765
810
|
plusBtn: {
|
|
766
811
|
width: 24,
|
|
767
812
|
height: 24,
|