Skip to content

Commit a57ae0d

Browse files
authored
Merge pull request #64 from PytorchConnectomics/Fix_ESlint_warning
Allowing Visualization button to render more than one viewer
2 parents 4a1ced7 + 049b01c commit a57ae0d

File tree

5 files changed

+148
-111
lines changed

5 files changed

+148
-111
lines changed

client/src/components/Dragger.js

Lines changed: 61 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import { Button, Input, message, Modal, Space, Upload } from 'antd'
44
import { InboxOutlined } from '@ant-design/icons'
55
import { AppContext } from '../contexts/GlobalContext'
66
import { DEFAULT_IMAGE } from '../utils/utils'
7-
import UTIF from 'utif';
7+
import UTIF from 'utif'
88

99
const path = require('path')
1010

1111
export function Dragger () {
1212
const context = useContext(AppContext)
1313

14-
const getBase64 = (file) =>
15-
new Promise((resolve, reject) => {
16-
const reader = new FileReader()
17-
reader.readAsDataURL(file)
18-
reader.onload = () => resolve(reader.result)
19-
reader.onerror = (error) => reject(error)
20-
})
14+
// const getBase64 = (file) =>
15+
// new Promise((resolve, reject) => {
16+
// const reader = new FileReader()
17+
// reader.readAsDataURL(file)
18+
// reader.onload = () => resolve(reader.result)
19+
// reader.onerror = (error) => reject(error)
20+
// })
2121

2222
const onChange = (info) => {
2323
const { status } = info.file
@@ -118,69 +118,68 @@ export function Dragger () {
118118
const handleCancel = () => setPreviewOpen(false)
119119
// Function to generate preview for TIFF files
120120
const generateTiffPreview = (file, callback) => {
121-
const reader = new FileReader();
122-
reader.onload = function(event) {
121+
const reader = new FileReader()
122+
reader.onload = function (event) {
123123
try {
124-
const buffer = new Uint8Array(event.target.result);
125-
console.log('Buffer length: ', buffer.length); //Log buffer length in bytes
126-
127-
const tiffPages = UTIF.decode(buffer);
128-
129-
//Check if tiffPages array is not empty
130-
if (tiffPages.length === 0) throw new Error('No TIFF pages found');
131-
132-
const firstPage = tiffPages[0];
133-
console.log('First page before decoding:', firstPage); // Log first page object before decoding
124+
const buffer = new Uint8Array(event.target.result)
125+
console.log('Buffer length: ', buffer.length) // Log buffer length in bytes
126+
127+
const tiffPages = UTIF.decode(buffer)
128+
129+
// Check if tiffPages array is not empty
130+
if (tiffPages.length === 0) throw new Error('No TIFF pages found')
131+
132+
const firstPage = tiffPages[0]
133+
console.log('First page before decoding:', firstPage) // Log first page object before decodin
134134

135135
// Ensure the firstPage has necessary tags before decoding
136-
if (!firstPage.t256 || !firstPage.t257) throw new Error('First page is missing essential tags (width and height)');
137-
138-
UTIF.decodeImage(buffer, firstPage); // firstPage before and after decoding, the result is same.
139-
console.log('TIFF first page after decoding: ', firstPage); //Log the first page object
136+
if (!firstPage.t256 || !firstPage.t257) throw new Error('First page is missing essential tags (width and height)')
137+
138+
UTIF.decodeImage(buffer, firstPage) // firstPage before and after decoding, the result is same.
139+
console.log('TIFF first page after decoding: ', firstPage) // Log the first page object
140140

141141
// Extract width and height from the TIFF tags
142-
const width = firstPage.t256 ? firstPage.t256[0] : 0;
143-
const height = firstPage.t257 ? firstPage.t257[0] : 0;
144-
142+
const width = firstPage.t256 ? firstPage.t256[0] : 0
143+
const height = firstPage.t257 ? firstPage.t257[0] : 0
144+
145145
// Check if width and height are valid
146146
if (width > 0 && height > 0) {
147-
const rgba = UTIF.toRGBA8(firstPage); // Uint8Array with RGBA pixels
147+
const rgba = UTIF.toRGBA8(firstPage) // Uint8Array with RGBA pixels
148148

149149
// Create a canvas to draw the TIFF image
150-
const canvas = document.createElement('canvas');
151-
const ctx = canvas.getContext('2d');
152-
canvas.width = width;
153-
canvas.height = height;
154-
const imageData = ctx.createImageData(width, height);
150+
const canvas = document.createElement('canvas')
151+
const ctx = canvas.getContext('2d')
152+
canvas.width = width
153+
canvas.height = height
154+
const imageData = ctx.createImageData(width, height)
155155

156-
imageData.data.set(rgba);
157-
ctx.putImageData(imageData, 0, 0);
156+
imageData.data.set(rgba)
157+
ctx.putImageData(imageData, 0, 0)
158158

159-
const dataURL = canvas.toDataURL();
160-
console.log('Canvas data URL:', dataURL);
161-
162-
callback(dataURL);
159+
const dataURL = canvas.toDataURL()
160+
console.log('Canvas data URL:', dataURL)
163161

162+
callback(dataURL)
164163
} else {
165-
console.error('TIFF image has invalid dimensions:', { width, height });
166-
message.error('TIFF image has invalid dimensions.');
167-
setPreviewImage(DEFAULT_IMAGE); // Fallback to default image
168-
}
164+
console.error('TIFF image has invalid dimensions:', { width, height })
165+
message.error('TIFF image has invalid dimensions.')
166+
setPreviewImage(DEFAULT_IMAGE) // Fallback to default image
167+
}
169168
} catch (error) {
170-
console.error('Failed to generate TIFF preview:', error);
171-
message.error('Failed to generate TIFF preview.');
172-
setPreviewImage(DEFAULT_IMAGE); // Fallback to default image
173-
}
174-
};
175-
reader.readAsArrayBuffer(file);
176-
};
169+
console.error('Failed to generate TIFF preview:', error)
170+
message.error('Failed to generate TIFF preview.')
171+
setPreviewImage(DEFAULT_IMAGE) // Fallback to default image
172+
}
173+
}
174+
reader.readAsArrayBuffer(file)
175+
}
177176

178-
//When click preview eye icon, implement handlePreview function
177+
// When click preview eye icon, implement handlePreview function
179178
const handlePreview = async (file) => {
180-
setFileUID(file.uid);
181-
setPreviewOpen(true);
182-
setPreviewImage(file.thumbUrl);
183-
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
179+
setFileUID(file.uid)
180+
setPreviewOpen(true)
181+
setPreviewImage(file.thumbUrl)
182+
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1))
184183
if (
185184
context.files.find(targetFile => targetFile.uid === file.uid) &&
186185
context.files.find(targetFile => targetFile.uid === file.uid).folderPath) {
@@ -201,24 +200,24 @@ export function Dragger () {
201200
// when click or drag file to this area to upload, below function will be deployed.
202201
const handleBeforeUpload = (file) => {
203202
// Create a URL for the thumbnail using object URL
204-
if (file.type === "image/tiff" || file.type === "image/tif") {
203+
if (file.type === 'image/tiff' || file.type === 'image/tif') {
205204
return new Promise((resolve) => {
206205
generateTiffPreview(file, (dataURL) => {
207-
file.thumbUrl = dataURL;
208-
console.log('file thumbUrl inside callback is', file.thumbUrl);
206+
file.thumbUrl = dataURL
207+
console.log('file thumbUrl inside callback is', file.thumbUrl)
209208
resolve(file)
210-
});
211-
console.log('file thumbUrl is', file.thumbUrl);
209+
})
210+
console.log('file thumbUrl is', file.thumbUrl)
212211
})
213212
} else {
214-
file.thumbUrl = URL.createObjectURL(file);
213+
file.thumbUrl = URL.createObjectURL(file)
215214
}
216215
return true // Allow the upload
217216
}
218217

219218
return (
220219
<>
221-
<Upload.Dragger
220+
<Upload.Dragger
222221
multiple
223222
onChange={onChange}
224223
customRequest={uploadImage}
@@ -276,4 +275,3 @@ export function Dragger () {
276275
</>
277276
)
278277
}
279-

client/src/utils/api.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ function handleError (error) {
2929
}
3030
export async function makeApiRequest (url, method, data = null) {
3131
try {
32-
// const fullUrl = `${process.env.REACT_APP_API_PROTOCOL}://${process.env.REACT_APP_API_URL}/${url}`;
33-
const res = await axios[method](url, data)
32+
const fullUrl = `${process.env.REACT_APP_API_PROTOCOL}://${process.env.REACT_APP_API_URL}/${url}`
33+
const res = await axios[method](fullUrl, data)
3434
return res.data
3535
} catch (error) {
3636
handleError(error)

client/src/views/DataLoader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useContext, useEffect, useState } from 'react'
2-
import {Dragger} from '../components/Dragger'
2+
import { Dragger } from '../components/Dragger'
33
import { Button, Input, Select, Space, Typography } from 'antd'
44
import { ArrowRightOutlined } from '@ant-design/icons'
55
import { AppContext } from '../contexts/GlobalContext'

client/src/views/Views.js

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const { Content, Sider } = Layout
1212
function Views () {
1313
const [current, setCurrent] = useState('visualization')
1414
const [viewers, setViewers] = useState([])
15+
const [isLoading, setIsLoading] = useState(false)
1516
console.log(viewers)
1617

1718
const onClick = (e) => {
@@ -44,31 +45,38 @@ function Views () {
4445
currentLabel,
4546
scales
4647
) => {
48+
setIsLoading(true)
4749
try {
50+
const viewerId = currentImage.uid + currentLabel.uid + JSON.stringify(scales)
51+
let updatedViewers = viewers
4852
const exists = viewers.find(
49-
(viewer) => viewer.key === currentImage.uid + currentLabel.uid
53+
// (viewer) => viewer.key === currentImage.uid + currentLabel.uid
54+
(viewer) => viewer.key === viewerId
5055
)
51-
console.log(exists, viewers)
52-
if (!exists) {
53-
const res = await getNeuroglancerViewer(
54-
currentImage,
55-
currentLabel,
56-
scales
57-
)
58-
const newUrl = res.replace(/\/\/[^:/]+/, '//localhost')
59-
console.log('Viewer at ', newUrl)
60-
61-
setViewers([
62-
...viewers,
63-
{
64-
key: currentImage.uid + currentLabel.uid,
65-
title: currentImage.name + ' & ' + currentLabel.name,
66-
viewer: newUrl
67-
}
68-
])
56+
// console.log(exists, viewers)
57+
if (exists) {
58+
updatedViewers = viewers.filter((viewer) => viewer.key !== viewerId)
6959
}
60+
const res = await getNeuroglancerViewer(
61+
currentImage,
62+
currentLabel,
63+
scales
64+
)
65+
const newUrl = res.replace(/\/\/[^:/]+/, '//localhost')
66+
console.log('Current Viewer at ', newUrl)
67+
68+
setViewers([
69+
...updatedViewers,
70+
{
71+
key: viewerId,
72+
title: currentImage.name + ' & ' + currentLabel.name,
73+
viewer: newUrl
74+
}
75+
])
76+
setIsLoading(false)
7077
} catch (e) {
7178
console.log(e)
79+
setIsLoading(false)
7280
}
7381
}
7482

@@ -79,30 +87,36 @@ function Views () {
7987
minWidth: '90vw'
8088
}}
8189
>
82-
<Sider
83-
// collapsible
84-
collapsed={collapsed}
85-
onCollapse={(value) => setCollapsed(value)}
86-
theme='light'
87-
collapsedWidth='0'
88-
>
89-
<DataLoader fetchNeuroglancerViewer={fetchNeuroglancerViewer} />
90-
</Sider>
91-
<Layout className='site-layout'>
92-
<Content
93-
style={{
94-
margin: '0 16px'
95-
}}
96-
>
97-
<Menu
98-
onClick={onClick}
99-
selectedKeys={[current]}
100-
mode='horizontal'
101-
items={items}
102-
/>
103-
{renderMenu()}
104-
</Content>
105-
</Layout>
90+
{isLoading ? (
91+
<div>Loading the viewer ...</div>
92+
) : (
93+
<React.Fragment>
94+
<Sider
95+
// collapsible
96+
collapsed={collapsed}
97+
onCollapse={(value) => setCollapsed(value)}
98+
theme='light'
99+
collapsedWidth='0'
100+
>
101+
<DataLoader fetchNeuroglancerViewer={fetchNeuroglancerViewer} />
102+
</Sider>
103+
<Layout className='site-layout'>
104+
<Content
105+
style={{
106+
margin: '0 16px'
107+
}}
108+
>
109+
<Menu
110+
onClick={onClick}
111+
selectedKeys={[current]}
112+
mode='horizontal'
113+
items={items}
114+
/>
115+
{renderMenu()}
116+
</Content>
117+
</Layout>
118+
</React.Fragment>
119+
) }
106120
</Layout>
107121
)
108122
}

client/src/views/Visualization.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
ArrowRightOutlined,
55
DownOutlined,
66
EyeOutlined,
7-
InboxOutlined
7+
InboxOutlined,
8+
ReloadOutlined
89
} from '@ant-design/icons'
910

1011
function Visualization (props) {
@@ -42,6 +43,20 @@ function Visualization (props) {
4243
setActiveKey(newActiveKey)
4344
}
4445

46+
const refreshViewer = (key) => {
47+
// This function refreshes the viewer specified by the key
48+
const updatedViewers = viewers.map((viewer) => {
49+
if (viewer.key === key) {
50+
// Refresh the viewer URL by adding a refresh request token to it
51+
// The refresh request token is only for node.js to force refresh the element
52+
// The appended token will be ignored when rendering
53+
return {...viewer, viewer: viewer.viewer + '?refresh=' + new Date().getTime() }
54+
}
55+
return viewer
56+
})
57+
setViewers(updatedViewers)
58+
}
59+
4560
return (
4661
<div style={{ marginTop: '20px' }}>
4762
{viewers.length > 0
@@ -54,7 +69,17 @@ function Visualization (props) {
5469
activeKey={activeKey}
5570
onChange={handleChange}
5671
items={viewers.map((viewer) => ({
57-
label: viewer.title,
72+
label: (
73+
<span>
74+
{viewer.title}
75+
<Button
76+
type='link'
77+
icon={<ReloadOutlined />}
78+
onClick={() => refreshViewer(viewer.key)}
79+
/>
80+
81+
</span>
82+
),
5883
key: viewer.key,
5984
children: (
6085
<iframe

0 commit comments

Comments
 (0)