diff --git a/README.md b/README.md index edc142726..6f74abd22 100644 --- a/README.md +++ b/README.md @@ -119,8 +119,7 @@ window.config = { }; ``` -- Install the viewer: - `window.OHIFStandaloneViewer.installViewer(window.config);` +- Install the viewer: `window.OHIFStandaloneViewer.installViewer(window.config);` This exact setup is demonstrated in this [CodeSandbox](https://codesandbox.io/s/ohif-viewer-script-tag-usage-c4u4t) and diff --git a/docs/latest/deployment/recipes/nginx--image-archive.md b/docs/latest/deployment/recipes/nginx--image-archive.md index baaf64ae4..beb1fec33 100644 --- a/docs/latest/deployment/recipes/nginx--image-archive.md +++ b/docs/latest/deployment/recipes/nginx--image-archive.md @@ -121,11 +121,7 @@ likely want to update: #### OHIF Viewer -The OHIF Viewer's configuration is imported from a static `.js` file and made -available globally at `window.config`. The configuration we use is set to a -specific file when we build the viewer, and determined by the env variable: -`APP_CONFIG`. You can see where we set its value in the `dockerfile` for this -solution: +The OHIF Viewer's configuration is imported from a static `.js` file. The configuration we use is set to a specific file when we build the viewer, and determined by the env variable: `APP_CONFIG`. You can see where we set its value in the `dockerfile` for this solution: `ENV APP_CONFIG=config/docker_openresty-orthanc.js` diff --git a/docs/latest/deployment/recipes/user-account-control.md b/docs/latest/deployment/recipes/user-account-control.md index 609c54995..bed05787d 100644 --- a/docs/latest/deployment/recipes/user-account-control.md +++ b/docs/latest/deployment/recipes/user-account-control.md @@ -122,11 +122,7 @@ likely want to update: #### OHIF Viewer -The OHIF Viewer's configuration is imported from a static `.js` file and made -available globally at `window.config`. The configuration we use is set to a -specific file when we build the viewer, and determined by the env variable: -`APP_CONFIG`. You can see where we set its value in the `dockerfile` for this -solution: +The OHIF Viewer's configuration is imported from a static `.js` file. The configuration we use is set to a specific file when we build the viewer, and determined by the env variable: `APP_CONFIG`. You can see where we set its value in the `dockerfile` for this solution: `ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js` diff --git a/platform/viewer/src/App.js b/platform/viewer/src/App.js index b39dc6421..d20504700 100644 --- a/platform/viewer/src/App.js +++ b/platform/viewer/src/App.js @@ -25,12 +25,15 @@ import { OidcProvider } from 'redux-oidc'; import PropTypes from 'prop-types'; import { Provider } from 'react-redux'; import { BrowserRouter as Router } from 'react-router-dom'; -import WhiteLabellingContext from './WhiteLabellingContext'; import { getActiveContexts } from './store/layout/selectors.js'; import i18n from '@ohif/i18n'; import setupTools from './setupTools.js'; import store from './store'; -import UserManagerContext from './UserManagerContext'; + +// Contexts +import WhiteLabellingContext from './context/WhiteLabellingContext'; +import UserManagerContext from './context/UserManagerContext'; +import AppContext from './context/AppContext'; // ~~~~ APP SETUP initCornerstoneTools({ @@ -74,10 +77,66 @@ class App extends Component { extensions: [], }; + _appConfig; + _userManager; + constructor(props) { super(props); - if (this.props.oidc.length) { + this.appConfig = props; + const { servers, extensions, hotkeys, oidc } = props; + + this.initUserManager(oidc); + _initExtensions(extensions, hotkeys); + _initServers(servers); + initWebWorkers(); + } + + render() { + const userManager = this._userManager; + const config = { + appConfig: this._appConfig, + }; + + if (userManager) { + return ( + + + + + + + + + + + + + + + + ); + } + + return ( + + + + + + + + + + + + ); + } + + initUserManager(oidc) { + if (oidc && !!oidc.length) { const firstOpenIdClient = this.props.oidc[0]; const { protocol, host } = window.location; @@ -102,58 +161,18 @@ class App extends Component { ), }); - this.userManager = getUserManagerForOpenIdConnectClient( + this._userManager = getUserManagerForOpenIdConnectClient( store, openIdConnectConfiguration ); } - - _initExtensions(this.props.extensions); - _initServers(this.props.servers); - initWebWorkers(); - } - - render() { - const userManager = this.userManager; - - if (userManager) { - return ( - - - - - - - - - - - - - - ); - } - - return ( - - - - - - - - - - ); } } /** * @param */ -function _initExtensions(extensions) { +function _initExtensions(extensions, hotkeys) { const defaultExtensions = [ GenericViewerCommands, MeasurementsPanel, @@ -163,8 +182,8 @@ function _initExtensions(extensions) { extensionManager.registerExtensions(mergedExtensions); // Must run after extension commands are registered - if (window.config.hotkeys) { - hotkeysManager.setHotkeys(window.config.hotkeys, true); + if (hotkeys) { + hotkeysManager.setHotkeys(hotkeys, true); } } diff --git a/platform/viewer/src/OHIFStandaloneViewer.js b/platform/viewer/src/OHIFStandaloneViewer.js index c8f1007da..9d6bdd685 100644 --- a/platform/viewer/src/OHIFStandaloneViewer.js +++ b/platform/viewer/src/OHIFStandaloneViewer.js @@ -20,6 +20,9 @@ import './OHIFStandaloneViewer.css'; import './variables.css'; import './theme-tide.css'; +// Contexts +import AppContext from './context/AppContext'; + // Dynamic Import Routes (CodeSplitting) // const IHEInvokeImageDisplay = asyncComponent(() => // import('./routes/IHEInvokeImageDisplay.js') @@ -37,6 +40,7 @@ import './theme-tide.css'; const reload = () => window.location.reload(); class OHIFStandaloneViewer extends Component { + static contextType = AppContext; state = { isLoading: false, }; @@ -63,7 +67,7 @@ class OHIFStandaloneViewer extends Component { render() { const { user, userManager } = this.props; - + const { appConfig = {} } = this.context; const userNotLoggedIn = userManager && (!user || user.expired); if (userNotLoggedIn) { const pathname = this.props.location.pathname; @@ -140,9 +144,7 @@ class OHIFStandaloneViewer extends Component { ]; const showStudyList = - window.config && window.config.showStudyList !== undefined - ? window.config.showStudyList - : true; + appConfig.showStudyList !== undefined ? appConfig.showStudyList : true; if (showStudyList) { routes.push({ path: '/studylist', diff --git a/platform/viewer/src/components/Header/Header.js b/platform/viewer/src/components/Header/Header.js index d618606c7..4ac7d81f6 100644 --- a/platform/viewer/src/components/Header/Header.js +++ b/platform/viewer/src/components/Header/Header.js @@ -11,13 +11,17 @@ import { AboutModal } from '@ohif/ui'; import { hotkeysManager } from './../../App.js'; import { withTranslation } from 'react-i18next'; +// Context +import AppContext from './../../context/AppContext'; + class Header extends Component { + static contextType = AppContext; static propTypes = { home: PropTypes.bool.isRequired, location: PropTypes.object.isRequired, children: PropTypes.node, t: PropTypes.func.isRequired, - userManager: PropTypes.object + userManager: PropTypes.object, }; static defaultProps = { @@ -63,10 +67,10 @@ class Header extends Component { if (this.props.user && this.props.userManager) { this.options.push({ title: t('Logout'), - icon: { name: 'power-off' }, - onClick: () => { - this.props.userManager.signoutRedirect(); - }, + icon: { name: 'power-off' }, + onClick: () => { + this.props.userManager.signoutRedirect(); + }, }); } @@ -82,10 +86,9 @@ class Header extends Component { render() { const { t } = this.props; + const { appConfig = {} } = this.context; const showStudyList = - window.config.showStudyList !== undefined - ? window.config.showStudyList - : true; + appConfig.showStudyList !== undefined ? appConfig.showStudyList : true; return (
diff --git a/platform/viewer/src/connectedComponents/Viewer.js b/platform/viewer/src/connectedComponents/Viewer.js index f0363ed65..fe2a0ee8b 100644 --- a/platform/viewer/src/connectedComponents/Viewer.js +++ b/platform/viewer/src/connectedComponents/Viewer.js @@ -5,7 +5,6 @@ import classNames from 'classnames'; import { MODULE_TYPES } from '@ohif/core'; import OHIF from '@ohif/core'; import moment from 'moment'; -import WhiteLabellingContext from '../WhiteLabellingContext.js'; import ConnectedHeader from './ConnectedHeader.js'; import ConnectedToolbarRow from './ConnectedToolbarRow.js'; import ConnectedLabellingOverlay from './ConnectedLabellingOverlay'; @@ -13,7 +12,11 @@ import ConnectedStudyBrowser from './ConnectedStudyBrowser.js'; import ConnectedViewerMain from './ConnectedViewerMain.js'; import SidePanel from './../components/SidePanel.js'; import { extensionManager } from './../App.js'; -import UserManagerContext from '../UserManagerContext'; + +// Contexts +import WhiteLabellingContext from '../context/WhiteLabellingContext.js'; +import UserManagerContext from '../context/UserManagerContext'; + import './Viewer.css'; /** * Inits OHIF Hanging Protocol's onReady. diff --git a/platform/viewer/src/context/AppContext.js b/platform/viewer/src/context/AppContext.js new file mode 100644 index 000000000..82bec40c4 --- /dev/null +++ b/platform/viewer/src/context/AppContext.js @@ -0,0 +1,5 @@ +import React from 'react'; + +let AppContext = React.createContext({}); + +export default AppContext; diff --git a/platform/viewer/src/UserManagerContext.js b/platform/viewer/src/context/UserManagerContext.js similarity index 100% rename from platform/viewer/src/UserManagerContext.js rename to platform/viewer/src/context/UserManagerContext.js diff --git a/platform/viewer/src/WhiteLabellingContext.js b/platform/viewer/src/context/WhiteLabellingContext.js similarity index 77% rename from platform/viewer/src/WhiteLabellingContext.js rename to platform/viewer/src/context/WhiteLabellingContext.js index 345b767fc..d13f9246b 100644 --- a/platform/viewer/src/WhiteLabellingContext.js +++ b/platform/viewer/src/context/WhiteLabellingContext.js @@ -1,4 +1,4 @@ -import OHIFLogo from './components/OHIFLogo/OHIFLogo.js'; +import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js'; import React from 'react'; const defaultContextValues = { diff --git a/platform/viewer/src/studylist/StudyListRouting.js b/platform/viewer/src/studylist/StudyListRouting.js index cd23827e6..8023df157 100644 --- a/platform/viewer/src/studylist/StudyListRouting.js +++ b/platform/viewer/src/studylist/StudyListRouting.js @@ -1,9 +1,12 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; import queryString from 'query-string'; import ConnectedStudyList from './ConnectedStudyList'; +// Contexts +import AppContext from '../context/AppContext'; + // TODO: Move to @ohif/ui function toLowerCaseFirstLetter(word) { @@ -22,16 +25,20 @@ function getFilters({ search }) { } function StudyListRouting({ location }) { + const { appConfig = {} } = useContext(AppContext); + const filters = location ? getFilters(location) : undefined; let studyListFunctionsEnabled = false; - if (window.config && window.config.studyListFunctionsEnabled) { - studyListFunctionsEnabled = window.config.studyListFunctionsEnabled; + if (appConfig.studyListFunctionsEnabled) { + studyListFunctionsEnabled = appConfig.studyListFunctionsEnabled; } - return ; + return ( + + ); } StudyListRouting.propTypes = { diff --git a/platform/viewer/src/studylist/StudyListWithData.js b/platform/viewer/src/studylist/StudyListWithData.js index ddf5956ed..656cf64f0 100644 --- a/platform/viewer/src/studylist/StudyListWithData.js +++ b/platform/viewer/src/studylist/StudyListWithData.js @@ -10,10 +10,14 @@ import moment from 'moment'; import ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploader'; import ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker'; import filesToStudies from '../lib/filesToStudies.js'; -import UserManagerContext from '../UserManagerContext'; -import WhiteLabellingContext from '../WhiteLabellingContext'; + +// Contexts +import UserManagerContext from '../context/UserManagerContext'; +import WhiteLabellingContext from '../context/WhiteLabellingContext'; +import AppContext from '../context/AppContext'; class StudyListWithData extends Component { + static contextType = AppContext; state = { searchData: {}, studies: [], @@ -51,9 +55,10 @@ class StudyListWithData extends Component { }; componentDidMount() { + const { appConfig = {} } = this.context; // TODO: Avoid using timepoints here //const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} }; - if (!this.props.server && window.config.enableGoogleCloudAdapter) { + if (!this.props.server && appConfig.enableGoogleCloudAdapter) { this.setState({ modalComponentId: 'DicomStorePicker', }); @@ -184,6 +189,7 @@ class StudyListWithData extends Component { }; render() { + const { appConfig = {} } = this.context; const onDrop = async acceptedFiles => { try { const studies = await filesToStudies(acceptedFiles); @@ -203,8 +209,7 @@ class StudyListWithData extends Component { let healthCareApiButtons = null; let healthCareApiWindows = null; - // TODO: This should probably be a prop - if (window.config.enableGoogleCloudAdapter) { + if (appConfig.enableGoogleCloudAdapter) { healthCareApiWindows = (