Skip to content

Commit 08b2ea4

Browse files
fix: OPTIC-1125: Label config preview always showing a step behind when making updates (#6417)
Co-authored-by: yyassi-heartex <[email protected]> Co-authored-by: Yousif Yassi <[email protected]>
1 parent 9ebc915 commit 08b2ea4

File tree

3 files changed

+40
-45
lines changed

3 files changed

+40
-45
lines changed

web/apps/labelstudio/src/pages/CreateProject/Config/Preview.jsx

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,25 @@ import { useAPI } from "../../../providers/ApiProvider";
99

1010
const configClass = cn("configure");
1111

12-
const loadDependencies = async () => import("@humansignal/editor");
12+
// Lazy load Label Studio with a single promise to avoid multiple loads
13+
// and enable as early as possible to load the dependencies once this component is mounted for the first time
14+
let dependencies;
15+
const loadDependencies = async () => {
16+
if (!dependencies) {
17+
dependencies = import("@humansignal/editor");
18+
}
19+
return dependencies;
20+
};
1321

1422
export const Preview = ({ config, data, error, loading, project }) => {
23+
// @see comment about dependencies above
24+
loadDependencies();
25+
26+
const [storeReady, setStoreReady] = useState(false);
1527
const lsf = useRef(null);
16-
const resolvingEditor = useMemo(loadDependencies);
1728
const rootRef = useRef();
18-
const projectRef = useRef(project);
1929
const api = useAPI();
20-
30+
const projectRef = useRef(project);
2131
projectRef.current = project;
2232

2333
const currentTask = useMemo(() => {
@@ -53,12 +63,14 @@ export const Preview = ({ config, data, error, loading, project }) => {
5363
}, [config]);
5464

5565
const initLabelStudio = useCallback(async (config, task) => {
56-
if (!task.data) return;
66+
// wait for dependencies to load, the promise is resolved only once
67+
// and is started when the component is mounted for the first time
68+
await loadDependencies();
5769

58-
await resolvingEditor;
70+
if (lsf.current || !task.data) return;
5971

6072
try {
61-
const lsf = new window.LabelStudio(rootRef.current, {
73+
lsf.current = new window.LabelStudio(rootRef.current, {
6274
config,
6375
task,
6476
interfaces: ["side-column"],
@@ -71,6 +83,7 @@ export const Preview = ({ config, data, error, loading, project }) => {
7183
const c = as.createAnnotation();
7284

7385
as.selectAnnotation(c.id);
86+
setStoreReady(true);
7487
};
7588

7689
if (isFF(FF_DEV_3617)) {
@@ -82,12 +95,9 @@ export const Preview = ({ config, data, error, loading, project }) => {
8295
},
8396
});
8497

85-
lsf.on("presignUrlForProject", onPresignUrlForProject);
86-
87-
return lsf;
98+
lsf.current.on("presignUrlForProject", onPresignUrlForProject);
8899
} catch (err) {
89100
console.error(err);
90-
return null;
91101
}
92102
}, []);
93103

@@ -99,45 +109,30 @@ export const Preview = ({ config, data, error, loading, project }) => {
99109
}, [loading, error]);
100110

101111
useEffect(() => {
102-
if (!lsf.current) {
103-
initLabelStudio(currentConfig, currentTask).then((ls) => {
104-
lsf.current = ls;
105-
});
106-
}
107-
}, [currentConfig, currentTask]);
108-
109-
useEffect(() => {
110-
if (lsf.current?.store) {
111-
lsf.current.store.assignConfig(currentConfig);
112-
console.log("LSF config updated");
113-
}
114-
}, [currentConfig]);
115-
116-
useEffect(() => {
117-
if (lsf.current?.store) {
118-
const store = lsf.current.store;
112+
initLabelStudio(currentConfig, currentTask).then(() => {
113+
if (storeReady && lsf.current?.store) {
114+
const store = lsf.current.store;
119115

120-
store.resetState();
121-
store.assignTask(currentTask);
122-
store.initializeStore(currentTask);
116+
store.resetState();
117+
store.assignTask(currentTask);
118+
store.assignConfig(currentConfig);
119+
store.initializeStore(currentTask);
123120

124-
const c = store.annotationStore.addAnnotation({
125-
userGenerate: true,
126-
});
121+
const c = store.annotationStore.addAnnotation({
122+
userGenerate: true,
123+
});
127124

128-
store.annotationStore.selectAnnotation(c.id);
129-
console.log("LSF task updated");
130-
}
131-
}, [currentTask]);
125+
store.annotationStore.selectAnnotation(c.id);
126+
console.log("LSF updated");
127+
}
128+
});
129+
}, [currentConfig, currentTask, storeReady]);
132130

133131
useEffect(() => {
134132
return () => {
135133
if (lsf.current) {
136134
console.info("Destroying LSF");
137-
// there can be weird error from LSF, but we can just skip it for now
138-
try {
139-
lsf.current.destroy();
140-
} catch (e) {}
135+
lsf.current.destroy();
141136
lsf.current = null;
142137
}
143138
};

web/libs/editor/src/tags/object/Image/ImageEntity.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const ImageEntity = types
6767
}))
6868
.actions((self) => ({
6969
preload() {
70-
if (self.ensurePreloaded()) return;
70+
if (self.ensurePreloaded() || !self.src) return;
7171

7272
self.setDownloading(true);
7373

web/libs/editor/tests/integration/e2e/image_segmentation/tools/rect3point.cy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ describe("Rect3Point tool", () => {
132132
ImageView.clickAtRelative(0.5, 0.5);
133133

134134
LabelStudio.serialize().then(([result]) => {
135-
expect(result.value.x).to.be.closeTo(50, 0.1);
136-
expect(result.value.y).to.be.closeTo(50, 0.1);
135+
expect(result.value.x).to.be.closeTo(50, 0.2);
136+
expect(result.value.y).to.be.closeTo(50, 0.2);
137137
expect(result.value.rotation).to.be.eq(0);
138138
});
139139
});

0 commit comments

Comments
 (0)