Skip to content

Commit a1f16d7

Browse files
authored
fix: BROS-136: Don't allow to create regions in View All (#7907)
1 parent 24c2e3b commit a1f16d7

File tree

10 files changed

+49
-58
lines changed

10 files changed

+49
-58
lines changed

web/libs/editor/src/components/TimeSeries/TimeSeriesVisualizer.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class TimeSeriesVisualizerD3 extends React.Component {
9797
} = this.props;
9898

9999
const activeStates = parent?.activeStates();
100-
const statesSelected = activeStates && activeStates.length;
100+
const statesSelected = activeStates?.length;
101101
const readonly = parent?.annotation?.isReadOnly();
102102

103103
// skip if event fired by .move() - prevent recursion and bugs
@@ -108,10 +108,13 @@ class TimeSeriesVisualizerD3 extends React.Component {
108108
const x = d3.mouse(d3.event.sourceEvent.target)[0];
109109
const newRegion = this.newRegion;
110110

111+
// double click handler to create instant region
111112
// when 2nd click happens during 300ms after 1st click and in the same place
112113
if (newRegion && Math.abs(newRegion.x - x) < 4) {
113114
clearTimeout(this.newRegionTimer);
114-
parent?.regionChanged(newRegion.range, ranges.length, newRegion.states);
115+
if (!readonly) {
116+
parent?.regionChanged(newRegion.range, ranges.length, newRegion.states);
117+
}
115118
this.newRegion = null;
116119
this.newRegionTimer = null;
117120
} else if (statesSelected) {

web/libs/editor/src/components/Timeline/Timeline.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const TimelineComponent: FC<TimelineProps> = ({
3232
speed,
3333
className,
3434
formatPosition,
35+
readonly = false,
3536
...props
3637
}) => {
3738
const View = Views[mode];
@@ -106,8 +107,9 @@ const TimelineComponent: FC<TimelineProps> = ({
106107
seekOffset,
107108
settings: View.settings,
108109
visibleWidth: seekVisibleWidth,
110+
readonly,
109111
}),
110-
[position, seekOffset, seekVisibleWidth, length, regions, step, playing, View.settings, data],
112+
[position, seekOffset, seekVisibleWidth, length, regions, step, playing, View.settings, data, readonly],
111113
);
112114

113115
useEffect(() => {

web/libs/editor/src/components/Timeline/Types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface TimelineProps<D extends ViewTypes = "frames"> {
3232
controlsOnTop?: boolean;
3333
controls?: TimelineControls;
3434
customControls?: TimelineCustomControls[];
35+
readonly?: boolean;
3536
onReady?: (data: Record<string, any>) => void;
3637
onPlay?: () => void;
3738
onPause?: () => void;
@@ -121,6 +122,7 @@ export interface TimelineContextValue {
121122
settings?: TimelineSettings;
122123
changeSetting?: (key: string, value: any) => void;
123124
data?: any;
125+
readonly?: boolean;
124126
}
125127

126128
export interface TimelineMinimapProps {

web/libs/editor/src/components/Timeline/Views/Frames/Controls.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type DataType = {
1010
};
1111

1212
export const Controls: FC<TimelineExtraControls<Actions, DataType>> = ({ onAction }) => {
13-
const { position, regions } = useContext(TimelineContext);
13+
const { position, regions, readonly } = useContext(TimelineContext);
1414
const hasSelectedRegion = regions.some(({ selected, timeline }) => selected && !timeline);
1515
const closestKeypoint = useMemo(() => {
1616
const region = regions.find((r) => r.selected && !r.timeline);
@@ -69,11 +69,11 @@ export const Controls: FC<TimelineExtraControls<Actions, DataType>> = ({ onActio
6969

7070
return (
7171
<>
72-
<ControlButton onClick={onKeypointToggle} disabled={!hasSelectedRegion} tooltip="Toggle Keypoint">
72+
<ControlButton onClick={onKeypointToggle} disabled={!hasSelectedRegion || readonly} tooltip="Toggle Keypoint">
7373
{keypointIcon}
7474
</ControlButton>
7575

76-
<ControlButton onClick={onLifespanToggle} disabled={!closestKeypoint} tooltip="Toggle Interpolation">
76+
<ControlButton onClick={onLifespanToggle} disabled={!closestKeypoint || readonly} tooltip="Toggle Interpolation">
7777
{interpolationIcon}
7878
</ControlButton>
7979
</>

web/libs/editor/src/tags/object/Audio/model.js

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ import { observe } from "mobx";
22
import { getEnv, getRoot, getType, types } from "mobx-state-tree";
33
import { createRef } from "react";
44
import { customTypes } from "../../../core/CustomTypes";
5-
import { guidGenerator } from "../../../core/Helpers.ts";
65
import { AnnotationMixin } from "../../../mixins/AnnotationMixin";
76
import IsReadyMixin from "../../../mixins/IsReadyMixin";
87
import ProcessAttrsMixin from "../../../mixins/ProcessAttrs";
98
import { SyncableMixin } from "../../../mixins/Syncable";
109
import { AudioRegionModel } from "../../../regions/AudioRegion";
11-
import Utils from "../../../utils";
1210
import { FF_LSDV_E_278, isFF } from "../../../utils/feature-flags";
1311
import { isDefined } from "../../../utils/utilities";
1412
import ObjectBase from "../Base";
@@ -156,13 +154,13 @@ export const AudioModel = types.compose(
156154
activeStates() {
157155
const states = self.states();
158156

159-
return states && states.filter((s) => getType(s).name === "LabelsModel" && s.isSelected);
157+
return states?.filter((s) => getType(s).name === "LabelsModel" && s.isSelected);
160158
},
161159

162160
get activeState() {
163161
const states = self.states();
164162

165-
return states && states.filter((s) => getType(s).name === "LabelsModel" && s.isSelected)[0];
163+
return states?.filter((s) => getType(s).name === "LabelsModel" && s.isSelected)[0];
166164
},
167165

168166
get activeLabel() {
@@ -176,6 +174,9 @@ export const AudioModel = types.compose(
176174
// use label to generate a unique key to ensure that adding/deleting can trigger changes
177175
return labels ? labels.join(",") : "";
178176
},
177+
get readonly() {
178+
return self.annotation.isReadOnly();
179+
},
179180
}))
180181
////// Sync actions
181182
.actions((self) => ({
@@ -219,9 +220,9 @@ export const AudioModel = types.compose(
219220
////// Incoming
220221

221222
registerSyncHandlers() {
222-
["play", "pause", "seek"].forEach((event) => {
223+
for (const event of ["play", "pause", "seek"]) {
223224
self.syncHandlers.set(event, self.handleSync);
224-
});
225+
}
225226
self.syncHandlers.set("speed", self.handleSyncSpeed);
226227
},
227228

@@ -288,13 +289,13 @@ export const AudioModel = types.compose(
288289
const selectedColor = activeState?.selectedColor;
289290
const labels = activeState?.selectedValues();
290291

291-
selectedRegions.forEach((r) => {
292+
for (const r of selectedRegions) {
292293
r.update({ color: selectedColor, labels: labels ?? [] });
293294

294295
const region = r.isRegion ? self.updateRegion(r) : self.addRegion(r);
295296

296297
self.annotation.selectArea(region);
297-
});
298+
}
298299

299300
if (selectedRegions.length) {
300301
self.requestWSUpdate();
@@ -341,7 +342,7 @@ export const AudioModel = types.compose(
341342
(target) => target.type === "paragraphs" && target.contextscroll,
342343
);
343344

344-
syncedParagraphs.forEach((paragraph) => {
345+
for (const paragraph of syncedParagraphs) {
345346
const segments = Object.values(paragraph.regionsStartEnd).map(({ start, end }) => ({
346347
start,
347348
end,
@@ -351,7 +352,7 @@ export const AudioModel = types.compose(
351352
}));
352353

353354
self._ws.addRegions(segments);
354-
});
355+
}
355356
},
356357

357358
handleNewRegions() {
@@ -381,7 +382,7 @@ export const AudioModel = types.compose(
381382
},
382383

383384
onHotKey(e) {
384-
e && e.preventDefault();
385+
e?.preventDefault();
385386
self._ws.togglePlay();
386387
return false;
387388
},
@@ -394,34 +395,6 @@ export const AudioModel = types.compose(
394395
self.playBackRate = val;
395396
},
396397

397-
createRegion(wsRegion, states) {
398-
let bgColor = self.selectedregionbg;
399-
const st = states.find((s) => s.type === "labels");
400-
401-
if (st) bgColor = Utils.Colors.convertToRGBA(st.getSelectedColor(), 0.3);
402-
403-
const r = AudioRegionModel.create({
404-
id: wsRegion.id ? wsRegion.id : guidGenerator(),
405-
pid: wsRegion.pid ? wsRegion.pid : guidGenerator(),
406-
parentID: wsRegion.parent_id === null ? "" : wsRegion.parent_id,
407-
start: wsRegion.start,
408-
end: wsRegion.end,
409-
score: wsRegion.score,
410-
readonly: wsRegion.readonly,
411-
regionbg: self.regionbg,
412-
selectedregionbg: bgColor,
413-
normalization: wsRegion.normalization,
414-
states,
415-
});
416-
417-
r.setWSRegion(wsRegion);
418-
419-
self.regions.push(r);
420-
self.annotation.addRegion(r);
421-
422-
return r;
423-
},
424-
425398
addRegion(wsRegion) {
426399
// area id is assigned to WS region during deserealization
427400
const find_r = self.annotation.areas.get(wsRegion.id);
@@ -485,9 +458,9 @@ export const AudioModel = types.compose(
485458
},
486459

487460
clearRegionMappings() {
488-
self.regs.forEach((r) => {
461+
for (const r of self.regs) {
489462
r.setWSRegion(null);
490-
});
463+
}
491464
},
492465

493466
onLoad(ws) {

web/libs/editor/src/tags/object/Audio/view.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ const AudioView: FC<AudioProps> = ({ item, children, settings = {}, changeSettin
7171
onError: item.onError,
7272
regions: {
7373
createable: !item.readonly,
74-
updateable: !item.readonly,
75-
deleteable: !item.readonly,
7674
},
7775
timeline: {
7876
backgroundColor: isDarkMode ? "rgb(38, 37, 34)" : "rgba(255,255,255,0.8)",

web/libs/editor/src/tags/object/Paragraphs/HtxParagraphs.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ class HtxParagraphsView extends Component {
303303
if (!states || states.length === 0 || ev.ctrlKey || ev.metaKey)
304304
return this._selectRegions(ev.ctrlKey || ev.metaKey);
305305

306+
if (item.annotation.isReadOnly()) {
307+
return;
308+
}
309+
306310
const selectedRanges = this.captureDocumentSelection();
307311

308312
if (selectedRanges.length === 0) {

web/libs/editor/src/tags/object/TimeSeries/Channel.jsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class ChannelD3 extends React.Component {
199199
} = this.props;
200200

201201
const activeStates = parent?.activeStates();
202-
const statesSelected = activeStates && activeStates.length;
202+
const statesSelected = activeStates?.length;
203203
const readonly = parent?.annotation?.isReadOnly();
204204

205205
// skip if event fired by .move() - prevent recursion and bugs
@@ -210,10 +210,13 @@ class ChannelD3 extends React.Component {
210210
const x = d3.mouse(d3.event.sourceEvent.target)[0];
211211
const newRegion = this.newRegion;
212212

213+
// double click handler to create instant region
213214
// when 2nd click happens during 300ms after 1st click and in the same place
214215
if (newRegion && Math.abs(newRegion.x - x) < 4) {
215216
clearTimeout(this.newRegionTimer);
216-
parent?.regionChanged(newRegion.range, ranges.length, newRegion.states);
217+
if (!readonly) {
218+
parent?.regionChanged(newRegion.range, ranges.length, newRegion.states);
219+
}
217220
this.newRegion = null;
218221
this.newRegionTimer = null;
219222
} else if (statesSelected) {
@@ -364,7 +367,7 @@ class ChannelD3 extends React.Component {
364367
const block = this.gCreator;
365368
const getRegion = this.getRegion;
366369
const x = this.x;
367-
const brush = (this.brushCreator = d3
370+
const brush = d3
368371
.brushX()
369372
.extent([
370373
[0, 0],
@@ -381,7 +384,9 @@ class ChannelD3 extends React.Component {
381384
// replacing default filter to allow ctrl-click action
382385
.filter(() => {
383386
return !d3.event.button;
384-
}));
387+
});
388+
389+
this.brushCreator = brush;
385390

386391
this.gCreator.call(this.brushCreator);
387392
}

web/libs/editor/src/tags/object/Video/HtxVideo.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ const HtxVideoView = ({ item, store }) => {
531531
disableView={!supportsTimelineRegions && !supportsRegions}
532532
framerate={item.framerate}
533533
controls={{ FramesControl: true }}
534+
readonly={item.annotation?.isReadOnly()}
534535
customControls={[
535536
{
536537
position: "left",

web/libs/editor/src/tags/object/Video/Video.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ const Model = types
140140
// normalize framerate — should be string with number of frames per second
141141
const framerate = Number(parseValue(self.framerate, self.store.task?.dataObj));
142142

143-
if (!framerate || isNaN(framerate)) self.framerate = "24";
143+
if (!framerate || Number.isNaN(framerate)) self.framerate = "24";
144144
else if (framerate < 1) self.framerate = String(1 / framerate);
145145
else self.framerate = String(framerate);
146146
},
@@ -178,9 +178,9 @@ const Model = types
178178
////// Incoming
179179

180180
registerSyncHandlers() {
181-
["play", "pause", "seek"].forEach((event) => {
181+
for (const event of ["play", "pause", "seek"]) {
182182
self.syncHandlers.set(event, self.handleSync);
183-
});
183+
}
184184
self.syncHandlers.set("speed", self.handleSyncSpeed);
185185
},
186186

@@ -260,9 +260,9 @@ const Model = types
260260
const area = self.annotation.createResult({ sequence }, {}, control, self);
261261

262262
// add labels
263-
self.activeStates().forEach((tag) => {
263+
for (const tag of self.activeStates()) {
264264
area.setValue(tag);
265-
});
265+
}
266266

267267
return area;
268268
},
@@ -304,6 +304,9 @@ const Model = types
304304
* @returns {Object} created region
305305
*/
306306
startDrawing({ frame, region: id }) {
307+
// don't create or edit regions in read-only mode
308+
if (self.annotation.isReadOnly()) return null;
309+
307310
if (id) {
308311
const region = self.annotation.regions.find((r) => r.cleanId === id);
309312
const range = region?.ranges?.[0];

0 commit comments

Comments
 (0)