Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit ec6c703

Browse files
fix: DIA-743: [FE] Blank space is displayed instead of the records on scroll up/down
1 parent 93aaec6 commit ec6c703

File tree

4 files changed

+148
-50
lines changed

4 files changed

+148
-50
lines changed

Diff for: src/components/CellViews/ImageCell.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { getRoot } from "mobx-state-tree";
22
import { FF_LSDV_4711, isFF } from "../../utils/feature-flags";
33
import { AnnotationPreview } from "../Common/AnnotationPreview/AnnotationPreview";
4+
import { useRef } from "react";
5+
import { useImageProvider } from "../../providers/ImageProvider";
46

57
const imgDefaultProps = {};
68

@@ -13,23 +15,32 @@ export const ImageCell = (column) => {
1315
column: { alias },
1416
} = column;
1517
const root = getRoot(original);
18+
const imgRef = useRef();
19+
const { getImage } = useImageProvider();
1620

1721
const renderImagePreview = original.total_annotations === 0 || !root.showPreviews;
1822
const imgSrc = Array.isArray(value) ? value[0] : value;
1923

2024
if (!imgSrc) return null;
25+
getImage(imgSrc).then((loadedImage) => {
26+
if (imgRef.current && loadedImage.loaded && loadedImage.url) {
27+
imgRef.current.setAttribute("src", loadedImage.url);
28+
imgRef.current.style.display = "";
29+
}
30+
});
2131

2232
return renderImagePreview ? (
2333
<img
2434
{...imgDefaultProps}
35+
ref={imgRef}
2536
key={imgSrc}
26-
src={imgSrc}
2737
alt="Data"
2838
style={{
2939
maxHeight: "100%",
3040
maxWidth: "100px",
3141
objectFit: "contain",
3242
borderRadius: 3,
43+
display: "none",
3344
}}
3445
/>
3546
) : (

Diff for: src/components/DataManager/DataManager.js

+15-12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { FiltersSidebar } from "../Filters/FiltersSidebar/FilterSidebar";
1111
import { DataView } from "../MainView";
1212
import "./DataManager.styl";
1313
import { Toolbar } from "./Toolbar/Toolbar";
14+
import { ImageProvider } from "../../providers/ImageProvider";
1415

1516
const injector = inject(({ store }) => {
1617
const { sidebarEnabled, sidebarVisible } = store.viewsStore ?? {};
@@ -127,19 +128,21 @@ const TabsSwitch = switchInjector(observer(({ sdk, views, tabs, selectedKey }) =
127128

128129
export const DataManager = injector(({ shrinkWidth }) => {
129130
return (
130-
<Block name="tabs-content">
131-
<Elem name="tab" mod={{ shrink: shrinkWidth }}>
132-
<Interface name="tabs">
133-
<TabsSwitch />
134-
</Interface>
131+
<ImageProvider key="app-image-provider">
132+
<Block name="tabs-content">
133+
<Elem name="tab" mod={{ shrink: shrinkWidth }}>
134+
<Interface name="tabs">
135+
<TabsSwitch />
136+
</Interface>
135137

136-
<Interface name="toolbar">
137-
<Toolbar />
138-
</Interface>
138+
<Interface name="toolbar">
139+
<Toolbar />
140+
</Interface>
139141

140-
<DataView />
141-
</Elem>
142-
<FiltersSidebar />
143-
</Block>
142+
<DataView />
143+
</Elem>
144+
<FiltersSidebar />
145+
</Block>
146+
</ImageProvider>
144147
);
145148
});

Diff for: src/components/Label/Label.js

+40-37
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Resizer } from "../Common/Resizer/Resizer";
1111
import { Space } from "../Common/Space/Space";
1212
import { DataView } from "../MainView";
1313
import "./Label.styl";
14+
import { ImageProvider } from "../../providers/ImageProvider";
1415

1516
const LabelingHeader = ({ SDK, onClick, isExplorerMode }) => {
1617
return (
@@ -99,45 +100,47 @@ export const Labeling = injector(observer(({
99100
const outlinerEnabled = isFF(FF_DEV_1170);
100101

101102
return (
102-
<Block name="label-view" mod={{ loading }}>
103-
{SDK.interfaceEnabled("labelingHeader") && (
104-
<LabelingHeader
105-
SDK={SDK}
106-
onClick={closeLabeling}
107-
isExplorerMode={isExplorerMode}
108-
/>
109-
)}
110-
111-
<Elem name="content">
112-
{isExplorerMode && (
113-
<Elem name="table">
114-
<Elem
115-
tag={Resizer}
116-
name="dataview"
117-
minWidth={200}
118-
showResizerLine={false}
119-
type={'quickview'}
120-
maxWidth={window.innerWidth * 0.35}
121-
initialWidth={view.labelingTableWidth} // hardcoded as in main-menu-trigger
122-
onResizeFinished={onResize}
123-
style={{ display: "flex", flex: 1, width: '100%' }}
124-
>
125-
<DataView />
126-
</Elem>
127-
</Elem>
103+
<ImageProvider key="app-image-provider">
104+
<Block name="label-view" mod={{ loading }}>
105+
{SDK.interfaceEnabled("labelingHeader") && (
106+
<LabelingHeader
107+
SDK={SDK}
108+
onClick={closeLabeling}
109+
isExplorerMode={isExplorerMode}
110+
/>
128111
)}
129112

130-
<Elem name="lsf-wrapper" mod={{ mode: isExplorerMode ? "explorer" : "labeling" }}>
131-
{loading && <Elem name="waiting" mod={{ animated: true }}/>}
132-
<Elem
133-
ref={lsfRef}
134-
id="label-studio-dm"
135-
name="lsf-container"
136-
key="label-studio"
137-
mod={{ outliner: outlinerEnabled }}
138-
/>
113+
<Elem name="content">
114+
{isExplorerMode && (
115+
<Elem name="table">
116+
<Elem
117+
tag={Resizer}
118+
name="dataview"
119+
minWidth={200}
120+
showResizerLine={false}
121+
type={'quickview'}
122+
maxWidth={window.innerWidth * 0.35}
123+
initialWidth={view.labelingTableWidth} // hardcoded as in main-menu-trigger
124+
onResizeFinished={onResize}
125+
style={{ display: "flex", flex: 1, width: '100%' }}
126+
>
127+
<DataView />
128+
</Elem>
129+
</Elem>
130+
)}
131+
132+
<Elem name="lsf-wrapper" mod={{ mode: isExplorerMode ? "explorer" : "labeling" }}>
133+
{loading && <Elem name="waiting" mod={{ animated: true }}/>}
134+
<Elem
135+
ref={lsfRef}
136+
id="label-studio-dm"
137+
name="lsf-container"
138+
key="label-studio"
139+
mod={{ outliner: outlinerEnabled }}
140+
/>
141+
</Elem>
139142
</Elem>
140-
</Elem>
141-
</Block>
143+
</Block>
144+
</ImageProvider>
142145
);
143146
}));

Diff for: src/providers/ImageProvider.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React, { useCallback, useContext, useMemo, useRef } from "react";
2+
3+
export const ImageContext = React.createContext({});
4+
ImageContext.displayName = "ImageContext";
5+
6+
export const ImageProvider = ({ children }) => {
7+
const loadedImagesRef = useRef(new Map());
8+
const getImage = useCallback((imgSrc) => {
9+
const loadedImages = loadedImagesRef.current;
10+
11+
if (loadedImages.has(imgSrc)) {
12+
const loadedImage = loadedImages.get(imgSrc);
13+
14+
return loadedImage.promise;
15+
}
16+
const imageFetchPromise = fetch(imgSrc)
17+
.then((response) => {
18+
const reader = response.body.getReader();
19+
20+
return new ReadableStream({
21+
start(controller) {
22+
return pump();
23+
function pump() {
24+
return reader.read().then(({ done, value }) => {
25+
// When no more data needs to be consumed, close the stream
26+
if (done) {
27+
controller.close();
28+
return;
29+
}
30+
// Enqueue the next data chunk into our target stream
31+
controller.enqueue(value);
32+
return pump();
33+
});
34+
}
35+
},
36+
});
37+
})
38+
// Create a new response out of the stream
39+
.then((stream) => new Response(stream))
40+
// Create an object URL for the response
41+
.then((response) => response.blob())
42+
.then((blob) => URL.createObjectURL(blob))
43+
// Update image
44+
.then((url) => {
45+
loadedImages.set(imgSrc, {
46+
url,
47+
promise: imageFetchPromise,
48+
loaded: true,
49+
});
50+
return loadedImages.get(imgSrc);
51+
})
52+
.catch((err) => {
53+
console.error(err);
54+
return loadedImages.get(imgSrc);
55+
});
56+
57+
loadedImages.set(imgSrc, {
58+
url: imgSrc,
59+
promise: imageFetchPromise,
60+
loaded: false,
61+
});
62+
63+
return imageFetchPromise;
64+
}, []);
65+
const contextValue = useMemo(() => {
66+
return {
67+
loadedImages: loadedImagesRef.current,
68+
getImage,
69+
};
70+
}, [getImage]);
71+
72+
return (
73+
<ImageContext.Provider value={contextValue}>
74+
{children}
75+
</ImageContext.Provider>
76+
);
77+
};
78+
79+
export const useImageProvider = () => {
80+
return useContext(ImageContext) ?? {};
81+
};

0 commit comments

Comments
 (0)