Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78344c2c66 | ||
|
|
b451ce4407 | ||
|
|
21b586b08f | ||
|
|
3bbeb1c521 | ||
|
|
7668b69f20 | ||
|
|
3a12138134 | ||
|
|
97786b808c | ||
|
|
cd1baa1b36 | ||
|
|
3ae3bee452 | ||
|
|
fd017c4512 | ||
|
|
c680720ce5 | ||
|
|
dfc4e89a91 | ||
|
|
13d337aaab | ||
|
|
4f26940005 | ||
|
|
9d08e81aa5 |
No files matched your search
@@ -3,6 +3,39 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.52.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.52.16...@ohif/extension-vtk@0.52.17) (2019-10-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Fix crosshairs location when using MIP ([#1080](https://github.com/OHIF/Viewers/issues/1080)) ([b451ce4](https://github.com/OHIF/Viewers/commit/b451ce440796f13b0891739d2130e8ee71993d3d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.52.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.52.15...@ohif/extension-vtk@0.52.16) (2019-10-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Switch to orhtographic view for 2D MPR ([#1074](https://github.com/OHIF/Viewers/issues/1074)) ([13d337a](https://github.com/OHIF/Viewers/commit/13d337aaabb8dadf6366c6262c5e47e7781edd08))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.52.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.52.14...@ohif/extension-vtk@0.52.15) (2019-10-23)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* ⚡️ Throttle UI refresh of synced WL value in vtk port ([#1070](https://github.com/OHIF/Viewers/issues/1070)) ([9d08e81](https://github.com/OHIF/Viewers/commit/9d08e81aa5def63a5e34f464aae099f7c29f0ac5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.52.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.52.13...@ohif/extension-vtk@0.52.14) (2019-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "0.52.14",
|
||||
"version": "0.52.17",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -48,10 +48,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"react-vtkjs-viewport": "^0.3.1"
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react-vtkjs-viewport": "^0.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^1.4.0",
|
||||
"@ohif/core": "^1.5.0",
|
||||
"@ohif/ui": "^0.56.0",
|
||||
"cornerstone-tools": "^4.0.9",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
|
||||
@@ -213,6 +213,10 @@ class OHIFVTKViewport extends Component {
|
||||
|
||||
volumeMapper.setSampleDistance(sampleDistance);
|
||||
|
||||
// Be generous to surpress warnings, as the logging really hurts performance.
|
||||
// TODO: maybe we should auto adjust samples to 1000.
|
||||
volumeMapper.setMaximumSamplesPerRay(4000);
|
||||
|
||||
volumeCache[displaySetInstanceUid] = volumeActor;
|
||||
|
||||
return volumeActor;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import setMPRLayout from './utils/setMPRLayout.js';
|
||||
import setViewportToVTK from './utils/setViewportToVTK.js';
|
||||
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
const { BlendMode } = Constants;
|
||||
|
||||
@@ -102,15 +103,23 @@ const actions = {
|
||||
});
|
||||
},
|
||||
enableLevelTool: () => {
|
||||
function updateVOI(apis, windowWidth, windowCenter) {
|
||||
apis.forEach(api => {
|
||||
api.updateVOI(windowWidth, windowCenter);
|
||||
});
|
||||
}
|
||||
|
||||
const throttledUpdateVOIs = throttle(updateVOI, 16, { trailing: true }); // ~ 60 fps
|
||||
|
||||
const callbacks = {
|
||||
setOnLevelsChanged: ({ windowCenter, windowWidth }) => {
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
|
||||
renderWindow.render();
|
||||
|
||||
api.updateVOI(windowWidth, windowCenter);
|
||||
});
|
||||
|
||||
throttledUpdateVOIs(apis, windowWidth, windowCenter);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -61,11 +61,13 @@
|
||||
"css-loader": "^3.2.0",
|
||||
"dotenv": "^8.1.0",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-prettier": "^6.4.0",
|
||||
"eslint-config-react-app": "^4.0.1",
|
||||
"eslint-plugin-flowtype": "2.x",
|
||||
"eslint-plugin-import": "2.x",
|
||||
"eslint-plugin-jsx-a11y": "6.x",
|
||||
"eslint-plugin-node": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "7.x",
|
||||
"eslint-plugin-react-hooks": "1.5.0",
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.4.0...@ohif/core@1.5.0) (2019-10-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Allow routes to load Google Cloud DICOM Stores in the Study List ([#1069](https://github.com/OHIF/Viewers/issues/1069)) ([21b586b](https://github.com/OHIF/Viewers/commit/21b586b08f3dde6613859712a9e0577dece564db))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.4.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.3.2...@ohif/core@1.4.0) (2019-10-15)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -6,7 +6,7 @@ const PARAM_PATTERN_IDENTIFIER = ':';
|
||||
function toLowerCaseFirstLetter(word) {
|
||||
return word[0].toLowerCase() + word.slice(1);
|
||||
}
|
||||
const getFilters = (location = {}) => {
|
||||
const getQueryFilters = (location = {}) => {
|
||||
const { search } = location;
|
||||
|
||||
if (!search) {
|
||||
@@ -55,13 +55,19 @@ const replaceParam = (path = '', paramKey, paramValue) => {
|
||||
return path;
|
||||
};
|
||||
|
||||
const isValidPath = path => {
|
||||
const paramPatternPiece = `/${PARAM_PATTERN_IDENTIFIER}`;
|
||||
return path.indexOf(paramPatternPiece) < 0;
|
||||
};
|
||||
|
||||
const queryString = {
|
||||
getQueryFilters: getFilters,
|
||||
getQueryFilters,
|
||||
};
|
||||
|
||||
const paramString = {
|
||||
parseParam: parseParam,
|
||||
replaceParam: replaceParam,
|
||||
isValidPath,
|
||||
parseParam,
|
||||
replaceParam,
|
||||
};
|
||||
|
||||
export { parse, queryString, paramString };
|
||||
@@ -3,6 +3,47 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.12.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.5...@ohif/viewer@1.12.0) (2019-10-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Allow routes to load Google Cloud DICOM Stores in the Study List ([#1069](https://github.com/OHIF/Viewers/issues/1069)) ([21b586b](https://github.com/OHIF/Viewers/commit/21b586b08f3dde6613859712a9e0577dece564db))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.11.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.4...@ohif/viewer@1.11.5) (2019-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
## [1.11.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.3...@ohif/viewer@1.11.4) (2019-10-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Revert "Revert "fix: MPR initialization"" ([#1065](https://github.com/OHIF/Viewers/issues/1065)) ([c680720](https://github.com/OHIF/Viewers/commit/c680720ce5ead58fdb399e3a356edac18093f5c0)), closes [#1062](https://github.com/OHIF/Viewers/issues/1062) [#1064](https://github.com/OHIF/Viewers/issues/1064)
|
||||
|
||||
## [1.11.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.2...@ohif/viewer@1.11.3) (2019-10-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Switch to orhtographic view for 2D MPR ([#1074](https://github.com/OHIF/Viewers/issues/1074)) ([13d337a](https://github.com/OHIF/Viewers/commit/13d337aaabb8dadf6366c6262c5e47e7781edd08))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.11.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.1...@ohif/viewer@1.11.2) (2019-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.11.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.0...@ohif/viewer@1.11.1) (2019-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('OHIFStandaloneViewer', () => {
|
||||
|
||||
it('loads route with at least 2 rows', () => {
|
||||
cy.screenshot();
|
||||
cy.percySnapshot();
|
||||
cy.percyCanvasSnapshot('Study List');
|
||||
|
||||
cy.get('#studyListData tr')
|
||||
.its('length')
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('OHIF Study Viewer Page', () => {
|
||||
|
||||
it('checks if series thumbnails are being displayed', () => {
|
||||
cy.screenshot();
|
||||
cy.percySnapshot();
|
||||
cy.percyCanvasSnapshot('Series Thumbnails');
|
||||
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
.its('length')
|
||||
|
||||
@@ -22,6 +22,6 @@ describe('OHIF Microscopy Extension', () => {
|
||||
|
||||
cy.wait(3000); //Waiting for image to render before taking the snapshot
|
||||
cy.screenshot();
|
||||
cy.percySnapshot();
|
||||
cy.percyCanvasSnapshot('Microscopy Extension');
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,6 @@ describe('OHIF PDF Extension', () => {
|
||||
.should('be.eq', 1);
|
||||
|
||||
cy.screenshot();
|
||||
cy.percySnapshot();
|
||||
cy.percyCanvasSnapshot('PDF Extension');
|
||||
});
|
||||
});
|
||||
@@ -29,7 +29,7 @@ describe('OHIF VTK Extension', () => {
|
||||
|
||||
it('checks if VTK buttons are displayed on the toolbar', () => {
|
||||
cy.screenshot();
|
||||
cy.percySnapshot();
|
||||
cy.percyCanvasSnapshot('VTK Extension');
|
||||
|
||||
cy.get('@crosshairsBtn')
|
||||
.should('be.visible')
|
||||
|
||||
@@ -306,3 +306,34 @@ Cypress.Commands.add('isInViewport', element => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Percy.io Canvas screenshot workaround
|
||||
*
|
||||
*/
|
||||
Cypress.Commands.add('percyCanvasSnapshot', (name, options = {}) => {
|
||||
function convertCanvas(documentClone) {
|
||||
documentClone
|
||||
.querySelectorAll('canvas')
|
||||
.forEach(selector => canvasToImage(selector));
|
||||
|
||||
return documentClone;
|
||||
}
|
||||
|
||||
function canvasToImage(selectorOrEl) {
|
||||
let canvas =
|
||||
typeof selectorOrEl === 'object'
|
||||
? selectorOrEl
|
||||
: document.querySelector(selectorOrEl);
|
||||
let image = document.createElement('img');
|
||||
let canvasImageBase64 = canvas.toDataURL();
|
||||
|
||||
image.src = canvasImageBase64;
|
||||
image.style = 'max-width: 100%';
|
||||
canvas.setAttribute('data-percy-modified', true);
|
||||
canvas.parentElement.appendChild(image);
|
||||
canvas.style = 'display: none';
|
||||
}
|
||||
|
||||
cy.percySnapshot(name, { ...options, domTransformation: convertCanvas });
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "1.11.1",
|
||||
"version": "1.12.0",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -45,12 +45,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^1.4.0",
|
||||
"@ohif/core": "^1.5.0",
|
||||
"@ohif/extension-cornerstone": "^2.0.0",
|
||||
"@ohif/extension-dicom-html": "^1.0.0",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.0",
|
||||
"@ohif/extension-vtk": "^0.52.14",
|
||||
"@ohif/extension-vtk": "^0.52.17",
|
||||
"@ohif/i18n": "^0.51.0",
|
||||
"@ohif/ui": "^0.56.0",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
@@ -82,7 +82,7 @@
|
||||
"redux-oidc": "3.1.x",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"reselect": "^4.0.0",
|
||||
"vtk.js": "^11.6.0"
|
||||
"vtk.js": "^11.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^3.3.1",
|
||||
|
||||
@@ -32,6 +32,11 @@ const ViewportGrid = function(props) {
|
||||
|
||||
const ViewportPanes = layout.viewports.map((layout, viewportIndex) => {
|
||||
const displaySet = viewportData[viewportIndex];
|
||||
|
||||
if (!displaySet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = {
|
||||
displaySet,
|
||||
studies,
|
||||
@@ -108,6 +113,9 @@ ViewportGrid.propTypes = {
|
||||
setViewportData: PropTypes.func.isRequired,
|
||||
studies: PropTypes.array,
|
||||
children: PropTypes.node,
|
||||
defaultPlugin: PropTypes.string,
|
||||
numRows: PropTypes.number.isRequired,
|
||||
numColumns: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
ViewportGrid.defaultProps = {
|
||||
|
||||
@@ -22,8 +22,6 @@ class ViewerMain extends Component {
|
||||
this.state = {
|
||||
displaySets: [],
|
||||
};
|
||||
|
||||
this.cachedViewportData = {};
|
||||
}
|
||||
|
||||
getDisplaySets(studies) {
|
||||
@@ -65,63 +63,75 @@ class ViewerMain extends Component {
|
||||
this.setState({
|
||||
displaySets,
|
||||
});
|
||||
|
||||
this.fillEmptyViewportPanes();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.studies !== prevProps.studies) {
|
||||
const prevViewportAmount = prevProps.layout.viewports.length;
|
||||
const viewportAmount = this.props.layout.viewports.length;
|
||||
const isVtk = this.props.layout.viewports.some(vp => !!vp.vtk);
|
||||
|
||||
if (
|
||||
this.props.studies !== prevProps.studies ||
|
||||
(viewportAmount !== prevViewportAmount && !isVtk)
|
||||
) {
|
||||
const displaySets = this.getDisplaySets(this.props.studies);
|
||||
|
||||
this.setState({
|
||||
displaySets,
|
||||
});
|
||||
|
||||
this.fillEmptyViewportPanes();
|
||||
}
|
||||
}
|
||||
|
||||
getViewportData = () => {
|
||||
const viewportData = [];
|
||||
fillEmptyViewportPanes = () => {
|
||||
const dirtyViewportPanes = [];
|
||||
const { layout, viewportSpecificData } = this.props;
|
||||
const { displaySets } = this.state;
|
||||
|
||||
for (
|
||||
let viewportIndex = 0;
|
||||
viewportIndex < layout.viewports.length;
|
||||
viewportIndex++
|
||||
) {
|
||||
let displaySet = viewportSpecificData[viewportIndex];
|
||||
|
||||
// Use the cached display set in viewport if the new one is empty
|
||||
if (displaySet && !displaySet.displaySetInstanceUid) {
|
||||
displaySet = this.cachedViewportData[viewportIndex];
|
||||
}
|
||||
|
||||
if (
|
||||
displaySet &&
|
||||
displaySet.studyInstanceUid &&
|
||||
displaySet.displaySetInstanceUid
|
||||
) {
|
||||
// Get missing fields from original display set
|
||||
const originalDisplaySet = this.findDisplaySet(
|
||||
this.props.studies,
|
||||
displaySet.studyInstanceUid,
|
||||
displaySet.displaySetInstanceUid
|
||||
);
|
||||
viewportData.push(Object.assign({}, originalDisplaySet, displaySet));
|
||||
} else {
|
||||
// If the viewport is empty, get one available in study
|
||||
const { displaySets } = this.state;
|
||||
displaySet = displaySets.find(
|
||||
ds =>
|
||||
!viewportData.some(
|
||||
v => v.displaySetInstanceUid === ds.displaySetInstanceUid
|
||||
)
|
||||
);
|
||||
viewportData.push(Object.assign({}, displaySet));
|
||||
}
|
||||
if (!displaySets || !displaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cachedViewportData = viewportData;
|
||||
for (let i = 0; i < layout.viewports.length; i++) {
|
||||
const viewportPane = viewportSpecificData[i];
|
||||
const isNonEmptyViewport =
|
||||
viewportPane &&
|
||||
viewportPane.studyInstanceUid &&
|
||||
viewportPane.displaySetInstanceUid;
|
||||
|
||||
return viewportData;
|
||||
if (isNonEmptyViewport) {
|
||||
dirtyViewportPanes.push({
|
||||
studyInstanceUid: viewportPane.studyInstanceUid,
|
||||
displaySetInstanceUid: viewportPane.displaySetInstanceUid,
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const foundDisplaySet =
|
||||
displaySets.find(
|
||||
ds =>
|
||||
!dirtyViewportPanes.some(
|
||||
v => v.displaySetInstanceUid === ds.displaySetInstanceUid
|
||||
)
|
||||
) || displaySets[displaySets.length - 1];
|
||||
|
||||
dirtyViewportPanes.push(foundDisplaySet);
|
||||
}
|
||||
|
||||
dirtyViewportPanes.forEach((vp, i) => {
|
||||
if (vp && vp.studyInstanceUid) {
|
||||
this.setViewportData({
|
||||
viewportIndex: i,
|
||||
studyInstanceUid: vp.studyInstanceUid,
|
||||
displaySetInstanceUid: vp.displaySetInstanceUid,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
setViewportData = ({
|
||||
@@ -139,12 +149,17 @@ class ViewerMain extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { viewportSpecificData } = this.props;
|
||||
const viewportData = viewportSpecificData
|
||||
? Object.values(viewportSpecificData)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<div className="ViewerMain">
|
||||
{this.state.displaySets.length && (
|
||||
<ConnectedViewportGrid
|
||||
studies={this.props.studies}
|
||||
viewportData={this.getViewportData()}
|
||||
viewportData={viewportData}
|
||||
setViewportData={this.setViewportData}
|
||||
>
|
||||
{/* Children to add to each viewport that support children */}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
export default function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
}, [value]);
|
||||
|
||||
return ref.current;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useContext } from 'react';
|
||||
import GoogleCloudApi from '../googleCloud/api/GoogleCloudApi';
|
||||
import usePrevious from './usePrevious';
|
||||
|
||||
import * as GoogleCloudUtilServers from '../googleCloud/utils/getServers';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
// Contexts
|
||||
import AppContext from '../context/AppContext';
|
||||
@@ -32,28 +34,68 @@ const getServers = (appConfig, project, location, dataset, dicomStore) => {
|
||||
wadoRoot: pathUrl,
|
||||
};
|
||||
servers = GoogleCloudUtilServers.getServers(data, dicomStore);
|
||||
if (!isValidServer(servers[0], appConfig)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return servers;
|
||||
};
|
||||
|
||||
const updateServer = (
|
||||
const isValidServer = (server, appConfig) => {
|
||||
if (appConfig.enableGoogleCloudAdapter) {
|
||||
return GoogleCloudUtilServers.isValidServer(server);
|
||||
}
|
||||
|
||||
return !!server;
|
||||
};
|
||||
|
||||
const setServers = (dispatch, servers) => {
|
||||
const action = {
|
||||
type: 'SET_SERVERS',
|
||||
servers,
|
||||
};
|
||||
dispatch(action);
|
||||
};
|
||||
|
||||
const useServerFromUrl = (
|
||||
servers = [],
|
||||
previousServers,
|
||||
activeServer,
|
||||
urlBasedServers,
|
||||
appConfig,
|
||||
dispatch,
|
||||
project,
|
||||
location,
|
||||
dataset,
|
||||
dicomStore
|
||||
) => {
|
||||
const servers = getServers(appConfig, project, location, dataset, dicomStore);
|
||||
|
||||
if (servers && servers.length) {
|
||||
const action = {
|
||||
type: 'SET_SERVERS',
|
||||
servers,
|
||||
};
|
||||
dispatch(action);
|
||||
// update state from url available only when gcloud on
|
||||
if (!appConfig.enableGoogleCloudAdapter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const serverHasChanged = previousServers !== servers && previousServers;
|
||||
|
||||
// do not update from url. use state instead.
|
||||
if (serverHasChanged) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if no valid urlbased servers
|
||||
if (!urlBasedServers || !urlBasedServers.length) {
|
||||
return false;
|
||||
} else if (!servers.length || !activeServer) {
|
||||
// no current valid server
|
||||
return true;
|
||||
}
|
||||
|
||||
const newServer = urlBasedServers[0];
|
||||
|
||||
let exists = servers.some(
|
||||
GoogleCloudUtilServers.isEqualServer.bind(undefined, newServer)
|
||||
);
|
||||
|
||||
return !exists;
|
||||
};
|
||||
|
||||
export default function useServer({
|
||||
@@ -64,14 +106,29 @@ export default function useServer({
|
||||
} = {}) {
|
||||
// Hooks
|
||||
const servers = useSelector(state => state && state.servers);
|
||||
const previousServers = usePrevious(servers);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { appConfig = {} } = useContext(AppContext);
|
||||
|
||||
const server = getActiveServer(servers);
|
||||
const activeServer = getActiveServer(servers);
|
||||
const urlBasedServers =
|
||||
getServers(appConfig, project, location, dataset, dicomStore) || [];
|
||||
const shouldUpdateServer = useServerFromUrl(
|
||||
servers.servers,
|
||||
previousServers,
|
||||
activeServer,
|
||||
urlBasedServers,
|
||||
appConfig,
|
||||
project,
|
||||
location,
|
||||
dataset,
|
||||
dicomStore
|
||||
);
|
||||
|
||||
if (!server) {
|
||||
updateServer(appConfig, dispatch, project, location, dataset, dicomStore);
|
||||
} else {
|
||||
return server;
|
||||
if (shouldUpdateServer) {
|
||||
setServers(dispatch, urlBasedServers);
|
||||
} else if (isValidServer(activeServer, appConfig)) {
|
||||
return activeServer;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,10 @@ class GoogleCloudApi {
|
||||
);
|
||||
}
|
||||
|
||||
getUrlPath(project, location, dataset, dicomStore) {
|
||||
`/projects/${project}/locations/${location}/datasets/${dataset}/dicomStores/${dicomStore}`;
|
||||
}
|
||||
|
||||
async doRequest(urlStr, config = {}, params = {}) {
|
||||
const url = new URL(urlStr);
|
||||
let data = null;
|
||||
|
||||
@@ -29,4 +29,31 @@ const getServers = (data, name) => {
|
||||
];
|
||||
};
|
||||
|
||||
export { getServers };
|
||||
const isValidServer = server => {
|
||||
return (
|
||||
server &&
|
||||
!!server.dataset &&
|
||||
!!server.dicomStore &&
|
||||
!!server.location &&
|
||||
!!server.project
|
||||
);
|
||||
};
|
||||
|
||||
const isEqualServer = (server = {}, toCompare = {}) => {
|
||||
const serverLength = Object.keys(server).length;
|
||||
const toCompareLength = Object.keys(toCompare).length;
|
||||
|
||||
if (!serverLength || !toCompareLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
server.dataset === toCompare.dataset &&
|
||||
server.dataset === toCompare.dataset &&
|
||||
server.dicomStore === toCompare.dicomStore &&
|
||||
server.location === toCompare.location &&
|
||||
server.project === toCompare.project
|
||||
);
|
||||
};
|
||||
|
||||
export { getServers, isValidServer, isEqualServer };
|
||||
@@ -65,6 +65,19 @@ const ROUTES_DEF = {
|
||||
return !!appConfig.enableGoogleCloudAdapter;
|
||||
},
|
||||
},
|
||||
list: {
|
||||
path:
|
||||
'/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore',
|
||||
component: StudyListRouting,
|
||||
condition: appConfig => {
|
||||
const showList =
|
||||
appConfig.showStudyList !== undefined
|
||||
? appConfig.showStudyList
|
||||
: true;
|
||||
|
||||
return showList && !!appConfig.enableGoogleCloudAdapter;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -92,23 +105,33 @@ const getRoutes = appConfig => {
|
||||
return routes;
|
||||
};
|
||||
|
||||
const parsePath = (path, server, params) => {
|
||||
let _path = path;
|
||||
const _paramsCopy = Object.assign({}, server, params);
|
||||
|
||||
for (let key in _paramsCopy) {
|
||||
_path = UrlUtil.paramString.replaceParam(_path, key, _paramsCopy[key]);
|
||||
}
|
||||
|
||||
return _path;
|
||||
};
|
||||
|
||||
const parseViewerPath = (appConfig = {}, server = {}, params) => {
|
||||
let viewerPath = ROUTES_DEF.default.viewer.path;
|
||||
if (appConfig.enableGoogleCloudAdapter) {
|
||||
viewerPath = ROUTES_DEF.gcloud.viewer.path;
|
||||
}
|
||||
|
||||
const _paramsCopy = Object.assign({}, server, params);
|
||||
|
||||
for (let key in _paramsCopy) {
|
||||
viewerPath = UrlUtil.paramString.replaceParam(
|
||||
viewerPath,
|
||||
key,
|
||||
_paramsCopy[key]
|
||||
);
|
||||
}
|
||||
|
||||
return viewerPath;
|
||||
return parsePath(viewerPath, server, params);
|
||||
};
|
||||
|
||||
export { getRoutes, parseViewerPath, reload };
|
||||
const parseStudyListPath = (appConfig = {}, server = {}, params) => {
|
||||
let studyListPath = ROUTES_DEF.default.list.path;
|
||||
if (appConfig.enableGoogleCloudAdapter) {
|
||||
studyListPath = ROUTES_DEF.gcloud.list.path || studyListPath;
|
||||
}
|
||||
|
||||
return parsePath(studyListPath, server, params);
|
||||
};
|
||||
|
||||
export { getRoutes, parseViewerPath, parseStudyListPath, reload };
|
||||
@@ -2,17 +2,26 @@ import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import ConnectedStudyList from './ConnectedStudyList';
|
||||
|
||||
import useServer from '../customHooks/useServer';
|
||||
import OHIF from '@ohif/core';
|
||||
const { urlUtil: UrlUtil } = OHIF.utils;
|
||||
|
||||
// Contexts
|
||||
import AppContext from '../context/AppContext';
|
||||
|
||||
function StudyListRouting({ location }) {
|
||||
function StudyListRouting({ match: routeMatch, location: routeLocation }) {
|
||||
const {
|
||||
project,
|
||||
location,
|
||||
dataset,
|
||||
dicomStore,
|
||||
studyInstanceUids,
|
||||
seriesInstanceUids,
|
||||
} = routeMatch.params;
|
||||
const server = useServer({ project, location, dataset, dicomStore });
|
||||
const { appConfig = {} } = useContext(AppContext);
|
||||
|
||||
const filters = UrlUtil.queryString.getQueryFilters(location);
|
||||
const filters = UrlUtil.queryString.getQueryFilters(routeLocation);
|
||||
|
||||
let studyListFunctionsEnabled = false;
|
||||
if (appConfig.studyListFunctionsEnabled) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploa
|
||||
import ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker';
|
||||
import filesToStudies from '../lib/filesToStudies.js';
|
||||
|
||||
const { urlUtil: UrlUtil } = OHIF.utils;
|
||||
// Contexts
|
||||
import UserManagerContext from '../context/UserManagerContext';
|
||||
import WhiteLabellingContext from '../context/WhiteLabellingContext';
|
||||
@@ -202,9 +203,27 @@ class StudyListWithData extends Component {
|
||||
const viewerPath = RoutesUtil.parseViewerPath(appConfig, server, {
|
||||
studyInstanceUids: studyInstanceUID,
|
||||
});
|
||||
this.props.history.push(viewerPath);
|
||||
|
||||
if (UrlUtil.paramString.isValidPath(viewerPath)) {
|
||||
this.props.history.push(viewerPath);
|
||||
}
|
||||
};
|
||||
|
||||
updateURL(modalOpened) {
|
||||
if (!modalOpened) {
|
||||
const { appConfig = {} } = this.context;
|
||||
const { server } = this.props;
|
||||
const listPath = RoutesUtil.parseStudyListPath(appConfig, server);
|
||||
|
||||
if (UrlUtil.paramString.isValidPath(listPath)) {
|
||||
const { location = {} } = this.props.history;
|
||||
if (location.pathname !== listPath) {
|
||||
this.props.history.replace(listPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSearch = searchData => {
|
||||
this.searchForStudies(searchData);
|
||||
};
|
||||
@@ -235,9 +254,12 @@ class StudyListWithData extends Component {
|
||||
let healthCareApiWindows = null;
|
||||
|
||||
if (appConfig.enableGoogleCloudAdapter) {
|
||||
const modalOpened = this.state.modalComponentId === 'DicomStorePicker';
|
||||
this.updateURL(modalOpened);
|
||||
|
||||
healthCareApiWindows = (
|
||||
<ConnectedDicomStorePicker
|
||||
isOpen={this.state.modalComponentId === 'DicomStorePicker'}
|
||||
isOpen={modalOpened}
|
||||
onClose={this.closeModals}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -7272,6 +7272,13 @@ escodegen@^1.8.1, escodegen@^1.9.1:
|
||||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-prettier@^6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz#0a04f147e31d33c6c161b2dd0971418ac52d0477"
|
||||
integrity sha512-YrKucoFdc7SEko5Sxe4r6ixqXPDP1tunGw91POeZTTRKItf/AMFYt/YLEQtZMkR2LVpAVhcAcZgcWpm1oGPW7w==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
eslint-config-react-app@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-4.0.1.tgz#23fd0fd7ea89442ef1e733f66a7207674b23c8db"
|
||||
@@ -7354,6 +7361,13 @@ eslint-plugin-node@^9.1.0:
|
||||
resolve "^1.10.1"
|
||||
semver "^6.1.0"
|
||||
|
||||
eslint-plugin-prettier@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz#507b8562410d02a03f0ddc949c616f877852f2ba"
|
||||
integrity sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
eslint-plugin-promise@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
|
||||
@@ -7829,6 +7843,11 @@ fast-deep-equal@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
||||
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
|
||||
|
||||
fast-diff@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
|
||||
|
||||
fast-glob@^2.0.2, fast-glob@^2.2.2, fast-glob@^2.2.6:
|
||||
version "2.2.7"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
|
||||
@@ -8519,6 +8538,11 @@ get-stdin@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
||||
integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
|
||||
|
||||
get-stdin@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
|
||||
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
|
||||
|
||||
get-stdin@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6"
|
||||
@@ -14670,6 +14694,13 @@ prepend-http@^1.0.0, prepend-http@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||
|
||||
prettier-linter-helpers@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
|
||||
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^1.14.2, prettier@^1.17.0, prettier@^1.18.2:
|
||||
version "1.18.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
|
||||
@@ -15495,10 +15526,10 @@ react-transition-group@^4.1.1:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-vtkjs-viewport@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.3.1.tgz#8451364fb1ae1120281d1dddd113a79e5541bf8d"
|
||||
integrity sha512-W27REfFbl2zsDXzpKRkV1GSwyU4jWHEhG6rnRdPjWq5dYkEwyvdyPhcz7qcGbsU1MTqyNRwVDP/9YV/vR+BWNg==
|
||||
react-vtkjs-viewport@^0.3.6:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.3.6.tgz#69df6c5c8808a64dc005d77e8058f9a99eaaca02"
|
||||
integrity sha512-tK6thFTiStFhfYuKBf6xEPRUYf+iZVcujzDElbMFqggRC5PgzcLfYZFAI/bI+9ju7VVyuVazhADu2KHJZYsuEg==
|
||||
dependencies:
|
||||
date-fns "^2.2.1"
|
||||
gl-matrix "^3.1.0"
|
||||
@@ -19043,10 +19074,10 @@ void-elements@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||
|
||||
vtk.js@^11.6.0:
|
||||
version "11.7.0"
|
||||
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-11.7.0.tgz#c06782080a3077a5380d438d576b6aae7f776f67"
|
||||
integrity sha512-nQ6dTdAUNICiTtU1bFBSgYPccOd8zEeatMBF/XrWGfDhC0QX63FbrgcuFRXWRjzMofC5wLiclYFqlMI6PswxIQ==
|
||||
vtk.js@^11.7.1:
|
||||
version "11.7.1"
|
||||
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-11.7.1.tgz#e9527c75d84e292c5d25fc4cacf62d42c777930e"
|
||||
integrity sha512-r6KTDYRF+XBq8gvoi27HJs+1DN4E39FX1k9i15el6JXRuZQebnBjWZ0W8XlfM8Nc6AAAax6DdauBFZdhyG0s/Q==
|
||||
dependencies:
|
||||
blueimp-md5 "2.10.0"
|
||||
commander "2.11.0"
|
||||
|
||||
Reference in new issue
Block a user