diff --git a/client/src/components/Dragger.js b/client/src/components/Dragger.js index 9901506..84d4144 100644 --- a/client/src/components/Dragger.js +++ b/client/src/components/Dragger.js @@ -4,20 +4,20 @@ import { Button, Input, message, Modal, Space, Upload } from 'antd' import { InboxOutlined } from '@ant-design/icons' import { AppContext } from '../contexts/GlobalContext' import { DEFAULT_IMAGE } from '../utils/utils' -import UTIF from 'utif'; +import UTIF from 'utif' const path = require('path') export function Dragger () { const context = useContext(AppContext) - const getBase64 = (file) => - new Promise((resolve, reject) => { - const reader = new FileReader() - reader.readAsDataURL(file) - reader.onload = () => resolve(reader.result) - reader.onerror = (error) => reject(error) - }) + // const getBase64 = (file) => + // new Promise((resolve, reject) => { + // const reader = new FileReader() + // reader.readAsDataURL(file) + // reader.onload = () => resolve(reader.result) + // reader.onerror = (error) => reject(error) + // }) const onChange = (info) => { const { status } = info.file @@ -118,69 +118,68 @@ export function Dragger () { const handleCancel = () => setPreviewOpen(false) // Function to generate preview for TIFF files const generateTiffPreview = (file, callback) => { - const reader = new FileReader(); - reader.onload = function(event) { + const reader = new FileReader() + reader.onload = function (event) { try { - const buffer = new Uint8Array(event.target.result); - console.log('Buffer length: ', buffer.length); //Log buffer length in bytes - - const tiffPages = UTIF.decode(buffer); - - //Check if tiffPages array is not empty - if (tiffPages.length === 0) throw new Error('No TIFF pages found'); - - const firstPage = tiffPages[0]; - console.log('First page before decoding:', firstPage); // Log first page object before decoding + const buffer = new Uint8Array(event.target.result) + console.log('Buffer length: ', buffer.length) // Log buffer length in bytes + + const tiffPages = UTIF.decode(buffer) + + // Check if tiffPages array is not empty + if (tiffPages.length === 0) throw new Error('No TIFF pages found') + + const firstPage = tiffPages[0] + console.log('First page before decoding:', firstPage) // Log first page object before decodin // Ensure the firstPage has necessary tags before decoding - if (!firstPage.t256 || !firstPage.t257) throw new Error('First page is missing essential tags (width and height)'); - - UTIF.decodeImage(buffer, firstPage); // firstPage before and after decoding, the result is same. - console.log('TIFF first page after decoding: ', firstPage); //Log the first page object + if (!firstPage.t256 || !firstPage.t257) throw new Error('First page is missing essential tags (width and height)') + + UTIF.decodeImage(buffer, firstPage) // firstPage before and after decoding, the result is same. + console.log('TIFF first page after decoding: ', firstPage) // Log the first page object // Extract width and height from the TIFF tags - const width = firstPage.t256 ? firstPage.t256[0] : 0; - const height = firstPage.t257 ? firstPage.t257[0] : 0; - + const width = firstPage.t256 ? firstPage.t256[0] : 0 + const height = firstPage.t257 ? firstPage.t257[0] : 0 + // Check if width and height are valid if (width > 0 && height > 0) { - const rgba = UTIF.toRGBA8(firstPage); // Uint8Array with RGBA pixels + const rgba = UTIF.toRGBA8(firstPage) // Uint8Array with RGBA pixels // Create a canvas to draw the TIFF image - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - canvas.width = width; - canvas.height = height; - const imageData = ctx.createImageData(width, height); + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + canvas.width = width + canvas.height = height + const imageData = ctx.createImageData(width, height) - imageData.data.set(rgba); - ctx.putImageData(imageData, 0, 0); + imageData.data.set(rgba) + ctx.putImageData(imageData, 0, 0) - const dataURL = canvas.toDataURL(); - console.log('Canvas data URL:', dataURL); - - callback(dataURL); + const dataURL = canvas.toDataURL() + console.log('Canvas data URL:', dataURL) + callback(dataURL) } else { - console.error('TIFF image has invalid dimensions:', { width, height }); - message.error('TIFF image has invalid dimensions.'); - setPreviewImage(DEFAULT_IMAGE); // Fallback to default image - } + console.error('TIFF image has invalid dimensions:', { width, height }) + message.error('TIFF image has invalid dimensions.') + setPreviewImage(DEFAULT_IMAGE) // Fallback to default image + } } catch (error) { - console.error('Failed to generate TIFF preview:', error); - message.error('Failed to generate TIFF preview.'); - setPreviewImage(DEFAULT_IMAGE); // Fallback to default image - } - }; - reader.readAsArrayBuffer(file); - }; + console.error('Failed to generate TIFF preview:', error) + message.error('Failed to generate TIFF preview.') + setPreviewImage(DEFAULT_IMAGE) // Fallback to default image + } + } + reader.readAsArrayBuffer(file) + } - //When click preview eye icon, implement handlePreview function + // When click preview eye icon, implement handlePreview function const handlePreview = async (file) => { - setFileUID(file.uid); - setPreviewOpen(true); - setPreviewImage(file.thumbUrl); - setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1)); + setFileUID(file.uid) + setPreviewOpen(true) + setPreviewImage(file.thumbUrl) + setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1)) if ( context.files.find(targetFile => targetFile.uid === file.uid) && context.files.find(targetFile => targetFile.uid === file.uid).folderPath) { @@ -201,24 +200,24 @@ export function Dragger () { // when click or drag file to this area to upload, below function will be deployed. const handleBeforeUpload = (file) => { // Create a URL for the thumbnail using object URL - if (file.type === "image/tiff" || file.type === "image/tif") { + if (file.type === 'image/tiff' || file.type === 'image/tif') { return new Promise((resolve) => { generateTiffPreview(file, (dataURL) => { - file.thumbUrl = dataURL; - console.log('file thumbUrl inside callback is', file.thumbUrl); + file.thumbUrl = dataURL + console.log('file thumbUrl inside callback is', file.thumbUrl) resolve(file) - }); - console.log('file thumbUrl is', file.thumbUrl); + }) + console.log('file thumbUrl is', file.thumbUrl) }) } else { - file.thumbUrl = URL.createObjectURL(file); + file.thumbUrl = URL.createObjectURL(file) } return true // Allow the upload } return ( <> - ) } - diff --git a/client/src/utils/api.js b/client/src/utils/api.js index 89bc2e7..63b3800 100644 --- a/client/src/utils/api.js +++ b/client/src/utils/api.js @@ -29,8 +29,8 @@ function handleError (error) { } export async function makeApiRequest (url, method, data = null) { try { - // const fullUrl = `${process.env.REACT_APP_API_PROTOCOL}://${process.env.REACT_APP_API_URL}/${url}`; - const res = await axios[method](url, data) + const fullUrl = `${process.env.REACT_APP_API_PROTOCOL}://${process.env.REACT_APP_API_URL}/${url}` + const res = await axios[method](fullUrl, data) return res.data } catch (error) { handleError(error) diff --git a/client/src/views/DataLoader.js b/client/src/views/DataLoader.js index 2ebc919..b75c89d 100644 --- a/client/src/views/DataLoader.js +++ b/client/src/views/DataLoader.js @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useState } from 'react' -import {Dragger} from '../components/Dragger' +import { Dragger } from '../components/Dragger' import { Button, Input, Select, Space, Typography } from 'antd' import { ArrowRightOutlined } from '@ant-design/icons' import { AppContext } from '../contexts/GlobalContext' diff --git a/client/src/views/Views.js b/client/src/views/Views.js index c164fef..37cf566 100644 --- a/client/src/views/Views.js +++ b/client/src/views/Views.js @@ -12,6 +12,7 @@ const { Content, Sider } = Layout function Views () { const [current, setCurrent] = useState('visualization') const [viewers, setViewers] = useState([]) + const [isLoading, setIsLoading] = useState(false) console.log(viewers) const onClick = (e) => { @@ -44,31 +45,38 @@ function Views () { currentLabel, scales ) => { + setIsLoading(true) try { + const viewerId = currentImage.uid + currentLabel.uid + JSON.stringify(scales) + let updatedViewers = viewers const exists = viewers.find( - (viewer) => viewer.key === currentImage.uid + currentLabel.uid + // (viewer) => viewer.key === currentImage.uid + currentLabel.uid + (viewer) => viewer.key === viewerId ) - console.log(exists, viewers) - if (!exists) { - const res = await getNeuroglancerViewer( - currentImage, - currentLabel, - scales - ) - const newUrl = res.replace(/\/\/[^:/]+/, '//localhost') - console.log('Viewer at ', newUrl) - - setViewers([ - ...viewers, - { - key: currentImage.uid + currentLabel.uid, - title: currentImage.name + ' & ' + currentLabel.name, - viewer: newUrl - } - ]) + // console.log(exists, viewers) + if (exists) { + updatedViewers = viewers.filter((viewer) => viewer.key !== viewerId) } + const res = await getNeuroglancerViewer( + currentImage, + currentLabel, + scales + ) + const newUrl = res.replace(/\/\/[^:/]+/, '//localhost') + console.log('Current Viewer at ', newUrl) + + setViewers([ + ...updatedViewers, + { + key: viewerId, + title: currentImage.name + ' & ' + currentLabel.name, + viewer: newUrl + } + ]) + setIsLoading(false) } catch (e) { console.log(e) + setIsLoading(false) } } @@ -79,30 +87,36 @@ function Views () { minWidth: '90vw' }} > - setCollapsed(value)} - theme='light' - collapsedWidth='0' - > - - - - - - {renderMenu()} - - + {isLoading ? ( +
Loading the viewer ...
+ ) : ( + + setCollapsed(value)} + theme='light' + collapsedWidth='0' + > + + + + + + {renderMenu()} + + + + ) } ) } diff --git a/client/src/views/Visualization.js b/client/src/views/Visualization.js index 7f183ba..5a9a022 100644 --- a/client/src/views/Visualization.js +++ b/client/src/views/Visualization.js @@ -4,7 +4,8 @@ import { ArrowRightOutlined, DownOutlined, EyeOutlined, - InboxOutlined + InboxOutlined, + ReloadOutlined } from '@ant-design/icons' function Visualization (props) { @@ -42,6 +43,20 @@ function Visualization (props) { setActiveKey(newActiveKey) } + const refreshViewer = (key) => { + // This function refreshes the viewer specified by the key + const updatedViewers = viewers.map((viewer) => { + if (viewer.key === key) { + // Refresh the viewer URL by adding a refresh request token to it + // The refresh request token is only for node.js to force refresh the element + // The appended token will be ignored when rendering + return {...viewer, viewer: viewer.viewer + '?refresh=' + new Date().getTime() } + } + return viewer + }) + setViewers(updatedViewers) + } + return (
{viewers.length > 0 @@ -54,7 +69,17 @@ function Visualization (props) { activeKey={activeKey} onChange={handleChange} items={viewers.map((viewer) => ({ - label: viewer.title, + label: ( + + {viewer.title} +