diff --git a/extensions/default/src/ViewerLayout/index.jsx b/extensions/default/src/ViewerLayout/index.jsx index 83f8c2475..21f89d91e 100644 --- a/extensions/default/src/ViewerLayout/index.jsx +++ b/extensions/default/src/ViewerLayout/index.jsx @@ -1,39 +1,22 @@ import React, { useEffect, useState, useCallback } from 'react'; import PropTypes from 'prop-types'; import { SidePanel, Toolbar } from '@ohif/ui'; -import { useToolbarLayout } from '@ohif/core'; // import Header from './Header.jsx'; -import { displaySetManager } from '@ohif/core'; - -// function ViewportDataCreator({ setViewportData }) { -// const { displaySetInstanceUIDs } = useViewModel(); -// console.log(displaySetInstanceUIDs); - -// useEffect(() => { -// setViewportData([ -// displaySetManager.getDisplaySetByUID(displaySetInstanceUIDs[0]), -// ]); -// }, [displaySetInstanceUIDs, setViewportData]); - -// return null; -// } function ViewerLayout({ // From Extension Module Params extensionManager, servicesManager, + commandsManager, // From Modes leftPanels, rightPanels, viewports, children, - ViewportGridComp + ViewportGridComp, }) { - const [displaySets, setDisplaySets] = useState({}); - - //const ViewportGrid = React.Children.only(children); - + const { ToolBarService } = servicesManager.services; /** * Set body classes (tailwindcss) that don't allow vertical * or horizontal overflow (no scrolling). Also guarantee window @@ -47,6 +30,7 @@ function ViewerLayout({ document.body.classList.remove('overflow-hidden'); }; }, []); + const getPanelData = id => { const entry = extensionManager.getModuleEntry(id); // TODO, not sure why sidepanel content has to be JSX, and not a children prop? @@ -70,27 +54,84 @@ function ViewerLayout({ }; }; + const handleToolBarSubscription = newToolBarLayout => { + // Get buttons to pass to toolbars. + console.log(commandsManager); + + const firstTool = newToolBarLayout[0].tools[0]; + + const toolBarLayout = []; + + newToolBarLayout.forEach(newToolBar => { + const toolBar = { tools: [], moreTools: [] }; + + Object.keys(newToolBar).forEach(key => { + if (newToolBar[key].length) { + newToolBar[key].forEach(tool => { + const commandOptions = tool.commandOptions || {}; + + toolBar[key].push({ + context: tool.context, + icon: tool.icon, + id: tool.id, + label: tool.label, + type: 'setToolActive', + onClick: () => { + debugger; + commandsManager.runCommand(tool.commandName, commandOptions); + }, + }); + }); + } + }); + + toolBarLayout.push(toolBar); + + // if (newToolBar.moreTools && newToolBar.moreTools.length) { + // newToolBar.moreTools.forEach(tool => { + // const commandOptions = tool.commandOptions || {}; + + // toolBar.push({ + // context: tool.context, + // icon: tool.icon, + // id: tool.id, + // label: tool.label, + // type: 'setToolActive', + // command: () => + // commandsManager.runCommand(tool.commandName, commandOptions), + // }); + // }); + // } + }); + + debugger; + + setToolBarLayout(toolBarLayout); + }; + + const [toolBarLayout, setToolBarLayout] = useState([ + { tools: [], moreTools: [] }, + { tools: [] }, + ]); + + useEffect(() => { + const { unsubscribe } = ToolBarService.subscribe( + ToolBarService.EVENTS.TOOL_BAR_MODIFIED, + handleToolBarSubscription + ); + + return unsubscribe; + }, []); + const leftPanelComponents = leftPanels.map(getPanelData); const rightPanelComponents = rightPanels.map(getPanelData); const viewportComponents = viewports.map(getViewportComponentData); - // let { toolBarLayout } = useToolbarLayout(); - // if (!toolBarLayout.length) { - // return null; - // } - - // TODO -> make toolbar service - const toolBarLayout = { tools: [], moreTools: [] }; - - //const [primaryToolBarLayout, secondaryToolBarLayout] = toolBarLayout; - const primaryToolBarLayout = toolBarLayout; - const secondaryToolBarLayout = toolBarLayout; - return (
- +
{ - toolBarManager.addButtons([ + init: ({ servicesManager, extensionManager }) => { + const { ToolBarService } = servicesManager.services; + ToolBarService.init(extensionManager); + ToolBarService.addButtons([ { id: 'Zoom', namespace: 'org.ohif.cornerstone.toolbarModule.Zoom', @@ -53,7 +55,7 @@ export default function mode({ modeConfiguration }) { ]); // Could import layout selector here from org.ohif.default (when it exists!) - toolBarManager.setToolBarLayout([ + ToolBarService.setToolBarLayout([ // Primary { tools: ['Zoom', 'Levels', 'Pan', 'Capture', 'Layout'], diff --git a/platform/core/src/ToolBarManager.js b/platform/core/src/ToolBarManager.js deleted file mode 100644 index 0c585d8d0..000000000 --- a/platform/core/src/ToolBarManager.js +++ /dev/null @@ -1,46 +0,0 @@ -export default class toolBarManager { - constructor(extensionManager) { - this.buttons = {}; - this.extensionManager = extensionManager; - } - - addButtons(buttons) { - buttons.forEach(button => { - const buttonDefinition = this.extensionManager.getModuleEntry( - button.namespace - ); - - const id = button.id || buttonDefinition.id; - - this.buttons[id] = buttonDefinition; - }); - } - - setToolBarLayout(layouts) { - const toolBarLayout = []; - - layouts.forEach(layout => { - const toolBarDefinitions = { tools: [], moreTools: [] }; - - const { tools, moreTools } = layout; - - tools && - tools.forEach(element => { - const button = this.buttons[element]; - - toolBarDefinitions.tools.push(button); - }); - - moreTools && - moreTools.forEach(element => { - const button = this.buttons[element]; - - toolBarDefinitions.moreTools.push(button); - }); - - toolBarLayout.push(toolBarDefinitions); - }); - - // TODO -> Change this to a service. => emit an event to subscribers to update the toolbar layout. - } -} diff --git a/platform/core/src/ToolbarLayoutContext.js b/platform/core/src/ToolbarLayoutContext.js deleted file mode 100644 index c88419831..000000000 --- a/platform/core/src/ToolbarLayoutContext.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component, useContext } from 'react'; - -/// TODO MAKE THIS PRETTY DANNY - -const ToolbarLayoutContext = React.createContext({ - toolBarLayout: [], - setToolBarLayout: () => {}, -}); - -ToolbarLayoutContext.displayName = 'ToolbarLayoutContext'; - -class ToolbarLayoutProvider extends Component { - state = { - toolBarLayout: [], - }; - - render() { - const setToolBarLayout = toolBarLayout => { - this.setState({ toolBarLayout }); - }; - - return ( - - {this.props.children} - - ); - } -} - -const useToolbarLayout = () => useContext(ToolbarLayoutContext); - -export default ToolbarLayoutContext; - -export { ToolbarLayoutProvider, useToolbarLayout }; diff --git a/platform/core/src/index.js b/platform/core/src/index.js index a00f76d99..09067349b 100644 --- a/platform/core/src/index.js +++ b/platform/core/src/index.js @@ -20,12 +20,7 @@ import studies from './studies/'; import ui from './ui'; import user from './user.js'; import dicomMetadataStore from './dicomMetadataStore'; -import ToolBarManager from './ToolBarManager'; import { ViewModelProvider, useViewModel } from './ViewModelContext'; -import { - ToolbarLayoutProvider, - useToolbarLayout, -} from './ToolbarLayoutContext'; import utils, { hotkeys } from './utils/'; import { @@ -35,6 +30,7 @@ import { UINotificationService, UIViewportDialogService, DisplaySetService, + ToolBarSerivce, } from './services'; import IWebApiDataSource from './DataSources/IWebApiDataSource'; @@ -73,10 +69,10 @@ const OHIF = { UIViewportDialogService, DisplaySetService, MeasurementService, + ToolBarSerivce, IWebApiDataSource, dicomMetadataStore, // - ToolBarManager, ViewModelProvider, useViewModel, }; @@ -114,13 +110,11 @@ export { UIViewportDialogService, DisplaySetService, MeasurementService, + ToolBarSerivce, IWebApiDataSource, dicomMetadataStore, - ToolBarManager, ViewModelProvider, useViewModel, - ToolbarLayoutProvider, - useToolbarLayout, }; export { OHIF }; diff --git a/platform/core/src/services/ToolBarService/ToolBarService.js b/platform/core/src/services/ToolBarService/ToolBarService.js index 00650b066..0eb0056ef 100644 --- a/platform/core/src/services/ToolBarService/ToolBarService.js +++ b/platform/core/src/services/ToolBarService/ToolBarService.js @@ -1,5 +1,9 @@ import pubSubServiceInterface from '../pubSubServiceInterface'; +const EVENTS = { + TOOL_BAR_MODIFIED: 'event::toolBarService:toolBarModified', +}; + export default class ToolBarService { constructor() { this.displaySets = {}; @@ -26,6 +30,23 @@ export default class ToolBarService { }); } + /** + * Broadcasts displaySetService changes. + * + * @param {string} eventName The event name + * @return void + */ + _broadcastChange = (eventName, callbackProps) => { + const hasListeners = Object.keys(this.listeners).length > 0; + const hasCallbacks = Array.isArray(this.listeners[eventName]); + + if (hasListeners && hasCallbacks) { + this.listeners[eventName].forEach(listener => { + listener.callback(callbackProps); + }); + } + }; + setToolBarLayout(layouts) { const toolBarLayout = []; @@ -51,6 +72,8 @@ export default class ToolBarService { toolBarLayout.push(toolBarDefinitions); }); - // TODO -> Change this to a service. => emit an event to subscribers to update the toolbar layout. + this.toolBarLayout = toolBarLayout; + + this._broadcastChange(this.EVENTS.TOOL_BAR_MODIFIED, toolBarLayout); } } diff --git a/platform/core/src/services/ToolBarService/index.js b/platform/core/src/services/ToolBarService/index.js new file mode 100644 index 000000000..bbe3dd317 --- /dev/null +++ b/platform/core/src/services/ToolBarService/index.js @@ -0,0 +1,8 @@ +import ToolBarService from './ToolBarService'; + +export default { + name: 'ToolBarService', + create: ({ configuration = {} }) => { + return new ToolBarService(); + }, +}; diff --git a/platform/core/src/services/index.js b/platform/core/src/services/index.js index 6487ed1c3..70eadd0d7 100644 --- a/platform/core/src/services/index.js +++ b/platform/core/src/services/index.js @@ -5,6 +5,7 @@ import UIModalService from './UIModalService'; import UINotificationService from './UINotificationService'; import UIViewportDialogService from './UIViewportDialogService'; import DisplaySetService from './DisplaySetService'; +import ToolBarSerivce from './ToolBarService'; export { MeasurementService, @@ -14,4 +15,5 @@ export { UINotificationService, UIViewportDialogService, DisplaySetService, + ToolBarSerivce, }; diff --git a/platform/viewer/public/config/idc.js b/platform/viewer/public/config/idc.js index 204fa4d0e..07c9383c6 100644 --- a/platform/viewer/public/config/idc.js +++ b/platform/viewer/public/config/idc.js @@ -1,19 +1,48 @@ -window.config = function(props) { - var servicesManager = props.servicesManager; - - return { - routerBasename: '/', - enableGoogleCloudAdapter: true, - enableGoogleCloudAdapterUI: false, - showStudyList: true, - httpErrorHandler: error => { - // This is 429 when rejected from the public idc sandbox too often. - console.warn(error.status); - - // Could use services manager here to bring up a dialog/modal if needed. - console.warn('test, navigate to https://ohif.org/'); - window.location = 'https://ohif.org/'; +window.config = { + routerBasename: '/', + enableGoogleCloudAdapter: true, + servers: { + // This is an array, but we'll only use the first entry for now + dicomWeb: [], + }, + // This is an array, but we'll only use the first entry for now + oidc: [ + { + // ~ REQUIRED + // Authorization Server URL + authority: 'https://accounts.google.com', + client_id: + '723928408739-k9k9r3i44j32rhu69vlnibipmmk9i57p.apps.googleusercontent.com', + redirect_uri: '/callback', // `OHIFStandaloneViewer.js` + response_type: 'id_token token', + scope: + 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid + // ~ OPTIONAL + post_logout_redirect_uri: '/logout-redirect.html', + revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=', + automaticSilentRenew: true, + revokeAccessTokenOnSignout: true, }, - healthcareApiEndpoint: 'https://idc-sandbox-002.appspot.com/v1beta1', - }; + ], + studyListFunctionsEnabled: true, }; + +// window.config = function(props) { +// var servicesManager = props.servicesManager; + +// return { +// routerBasename: '/', +// enableGoogleCloudAdapter: true, +// enableGoogleCloudAdapterUI: false, +// showStudyList: true, +// httpErrorHandler: error => { +// // This is 429 when rejected from the public idc sandbox too often. +// console.warn(error.status); + +// // Could use services manager here to bring up a dialog/modal if needed. +// console.warn('test, navigate to https://ohif.org/'); +// window.location = 'https://ohif.org/'; +// }, +// healthcareApiEndpoint: 'https://idc-sandbox-002.appspot.com/v1beta1', +// }; +// }; diff --git a/platform/viewer/src/appInit.js b/platform/viewer/src/appInit.js index e47abcce5..62fcf2c51 100644 --- a/platform/viewer/src/appInit.js +++ b/platform/viewer/src/appInit.js @@ -8,6 +8,7 @@ import { UIDialogService, MeasurementService, DisplaySetService, + ToolBarSerivce, // utils, // redux as reduxOHIF, } from '@ohif/core'; @@ -45,6 +46,7 @@ function appInit(appConfigOrFunc, defaultExtensions) { UIDialogService, MeasurementService, DisplaySetService, + ToolBarSerivce, ]); /** diff --git a/platform/viewer/src/routes/Mode/DisplaySetCreator.jsx b/platform/viewer/src/routes/Mode/DisplaySetCreator.jsx deleted file mode 100644 index 19b24647b..000000000 --- a/platform/viewer/src/routes/Mode/DisplaySetCreator.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useEffect, useCallback } from 'react'; -import { - displaySetManager, - ToolBarManager, - useViewModel, - useToolbarLayout, -} from '@ohif/core'; - -export default function DisplaySetCreator({ - location, - mode, - dataSourceName, - extensionManager, - DisplaySetService, -}) { - console.warn('DisplaySetCreator rerendering'); - const { routes, sopClassHandlers } = mode; - const dataSources = extensionManager.getDataSources(dataSourceName); - // TODO: For now assume one unique datasource. - - const dataSource = dataSources[0]; - const route = routes[0]; - - // Add toolbar state to the view model context? - const { displaySetInstanceUIDs, setDisplaySetInstanceUIDs } = useViewModel(); - - const { toolBarLayout, setToolBarLayout } = useToolbarLayout(); - - useEffect(() => { - let toolBarManager = new ToolBarManager(extensionManager, setToolBarLayout); - route.init({ toolBarManager }); - }, [mode, dataSourceName, location]); - - const createDisplaySets = useCallback(() => { - // Add SOPClassHandlers to a new SOPClassManager. - displaySetManager.init(extensionManager, sopClassHandlers, { - displaySetInstanceUIDs, - setDisplaySetInstanceUIDs, - }); - - const queryParams = location.search; - - // Call the data source to start building the view model? - dataSource.retrieve.series.metadata( - queryParams, - displaySetManager.makeDisplaySets - ); - }, [displaySetInstanceUIDs, location]); - - useEffect(() => { - createDisplaySets(); - }, [mode, dataSourceName, location]); - - return null; -} diff --git a/platform/viewer/src/routes/Mode/Mode.jsx b/platform/viewer/src/routes/Mode/Mode.jsx index d8c0ed175..ba0cc7caf 100644 --- a/platform/viewer/src/routes/Mode/Mode.jsx +++ b/platform/viewer/src/routes/Mode/Mode.jsx @@ -2,13 +2,11 @@ import React, { useEffect, useCallback } from 'react'; import { useParams } from 'react-router'; import PropTypes from 'prop-types'; // -import { ToolBarManager } from '@ohif/core'; import { DragAndDropProvider } from '@ohif/ui'; // import { useQuery } from '@hooks'; import ViewportGrid from '@components/ViewportGrid'; import Compose from './Compose'; -//import DisplaySetCreator from './DisplaySetCreator'; export default function ModeRoute({ location, @@ -69,9 +67,7 @@ export default function ModeRoute({ } useEffect(() => { - // TODO -> Make this into a service - let toolBarManager = new ToolBarManager(extensionManager); //, setToolBarLayout); - route.init({ toolBarManager }); + route.init({ servicesManager, extensionManager }); }, [mode, dataSourceName, location]); // This queries for series, but... What does it do with them?