feat: expose UiNotifications service (#1172)
* playing around * Change extension init and preRegistration signature * Clear test code * Update core index test * Fix test * Tricking the bug * Renaming file back to trick some weird bug
This commit is contained in:
parent
c59c5b3f14
commit
5c04e34c8f
@ -11,7 +11,7 @@ export default {
|
|||||||
* LIFECYCLE HOOKS
|
* LIFECYCLE HOOKS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
preRegistration(extensionConfiguration) {},
|
preRegistration({ serviceManager, configuration: extensionConfiguration }) {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MODULE GETTERS
|
* MODULE GETTERS
|
||||||
|
|||||||
@ -24,8 +24,8 @@ export default {
|
|||||||
* @param {object} [configuration={}]
|
* @param {object} [configuration={}]
|
||||||
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
|
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
|
||||||
*/
|
*/
|
||||||
preRegistration(configuration = {}) {
|
preRegistration({ serviceManager, configuration = {} }) {
|
||||||
init(configuration);
|
init({ serviceManager, configuration });
|
||||||
},
|
},
|
||||||
getViewportModule() {
|
getViewportModule() {
|
||||||
return OHIFCornerstoneViewport;
|
return OHIFCornerstoneViewport;
|
||||||
|
|||||||
@ -4,16 +4,18 @@ import csTools from 'cornerstone-tools';
|
|||||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||||
import queryString from 'query-string';
|
import queryString from 'query-string';
|
||||||
|
|
||||||
function fallbackMetaDataProvider (type, imageId) {
|
function fallbackMetaDataProvider(type, imageId) {
|
||||||
if (!imageId.includes('wado?requestType=WADO')) {
|
if (!imageId.includes('wado?requestType=WADO')) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you call for an WADO-URI imageId and get no
|
// If you call for an WADO-URI imageId and get no
|
||||||
// metadata, try reformatting to WADO-RS imageId
|
// metadata, try reformatting to WADO-RS imageId
|
||||||
const qs = queryString.parse(imageId);
|
const qs = queryString.parse(imageId);
|
||||||
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot
|
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot;
|
||||||
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${qs.seriesUID}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
|
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${
|
||||||
|
qs.seriesUID
|
||||||
|
}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
|
||||||
|
|
||||||
return cornerstone.metaData.get(type, wadoRsImageId);
|
return cornerstone.metaData.get(type, wadoRsImageId);
|
||||||
}
|
}
|
||||||
@ -21,13 +23,12 @@ function fallbackMetaDataProvider (type, imageId) {
|
|||||||
// Add this fallback provider with a low priority so it is handled last
|
// Add this fallback provider with a low priority so it is handled last
|
||||||
cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
|
cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} configuration
|
* @param {object} configuration
|
||||||
* @param {Object|Array} configuration.csToolsConfig
|
* @param {Object|Array} configuration.csToolsConfig
|
||||||
*/
|
*/
|
||||||
export default function init(configuration = {}) {
|
export default function init({ serviceManager, configuration = {} }) {
|
||||||
const { csToolsConfig } = configuration;
|
const { csToolsConfig } = configuration;
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
||||||
@ -97,6 +98,7 @@ export default function init(configuration = {}) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
tools.forEach(tool => csTools.addTool(tool));
|
tools.forEach(tool => csTools.addTool(tool));
|
||||||
|
|
||||||
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
||||||
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
||||||
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
||||||
|
|||||||
@ -2,12 +2,13 @@ import MODULE_TYPES from './MODULE_TYPES.js';
|
|||||||
import log from './../log.js';
|
import log from './../log.js';
|
||||||
|
|
||||||
export default class ExtensionManager {
|
export default class ExtensionManager {
|
||||||
constructor({ commandsManager }) {
|
constructor({ commandsManager, servicesManager }) {
|
||||||
this.modules = {};
|
this.modules = {};
|
||||||
this.registeredExtensionIds = [];
|
this.registeredExtensionIds = [];
|
||||||
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
||||||
//
|
//
|
||||||
this._commandsManager = commandsManager;
|
this._commandsManager = commandsManager;
|
||||||
|
this._servicesManager = servicesManager;
|
||||||
|
|
||||||
this.moduleTypeNames.forEach(moduleType => {
|
this.moduleTypeNames.forEach(moduleType => {
|
||||||
this.modules[moduleType] = [];
|
this.modules[moduleType] = [];
|
||||||
@ -66,7 +67,10 @@ export default class ExtensionManager {
|
|||||||
|
|
||||||
// preRegistrationHook
|
// preRegistrationHook
|
||||||
if (extension.preRegistration) {
|
if (extension.preRegistration) {
|
||||||
extension.preRegistration(configuration);
|
extension.preRegistration({
|
||||||
|
serviceManager: this._servicesManager,
|
||||||
|
configuration,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register Modules
|
// Register Modules
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import './lib';
|
import './lib';
|
||||||
|
|
||||||
import { ExtensionManager, MODULE_TYPES } from './extensions';
|
import { ExtensionManager, MODULE_TYPES } from './extensions';
|
||||||
|
import { ServicesManager } from './services';
|
||||||
import classes, { CommandsManager, HotkeysManager } from './classes/';
|
import classes, { CommandsManager, HotkeysManager } from './classes/';
|
||||||
|
|
||||||
import DICOMWeb from './DICOMWeb';
|
import DICOMWeb from './DICOMWeb';
|
||||||
@ -18,12 +19,15 @@ import ui from './ui';
|
|||||||
import user from './user.js';
|
import user from './user.js';
|
||||||
import utils from './utils/';
|
import utils from './utils/';
|
||||||
|
|
||||||
|
import { createUiNotificationService } from './services';
|
||||||
|
|
||||||
const OHIF = {
|
const OHIF = {
|
||||||
MODULE_TYPES,
|
MODULE_TYPES,
|
||||||
//
|
//
|
||||||
CommandsManager,
|
CommandsManager,
|
||||||
ExtensionManager,
|
ExtensionManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
ServicesManager,
|
||||||
//
|
//
|
||||||
utils,
|
utils,
|
||||||
studies,
|
studies,
|
||||||
@ -41,6 +45,8 @@ const OHIF = {
|
|||||||
viewer: {},
|
viewer: {},
|
||||||
measurements,
|
measurements,
|
||||||
hangingProtocols,
|
hangingProtocols,
|
||||||
|
//
|
||||||
|
createUiNotificationService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -49,6 +55,7 @@ export {
|
|||||||
CommandsManager,
|
CommandsManager,
|
||||||
ExtensionManager,
|
ExtensionManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
ServicesManager,
|
||||||
//
|
//
|
||||||
utils,
|
utils,
|
||||||
studies,
|
studies,
|
||||||
@ -65,6 +72,8 @@ export {
|
|||||||
DICOMWeb,
|
DICOMWeb,
|
||||||
measurements,
|
measurements,
|
||||||
hangingProtocols,
|
hangingProtocols,
|
||||||
|
//
|
||||||
|
createUiNotificationService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { OHIF };
|
export { OHIF };
|
||||||
|
|||||||
@ -8,6 +8,9 @@ describe('Top level exports', () => {
|
|||||||
'CommandsManager',
|
'CommandsManager',
|
||||||
'ExtensionManager',
|
'ExtensionManager',
|
||||||
'HotkeysManager',
|
'HotkeysManager',
|
||||||
|
'ServicesManager',
|
||||||
|
//
|
||||||
|
'createUiNotificationService',
|
||||||
//
|
//
|
||||||
'utils',
|
'utils',
|
||||||
'studies',
|
'studies',
|
||||||
|
|||||||
9
platform/core/src/services/ServicesManager.js
Normal file
9
platform/core/src/services/ServicesManager.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default class ServicesManager {
|
||||||
|
constructor() {
|
||||||
|
this.services = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
register(service) {
|
||||||
|
this.services[service.name] = service;
|
||||||
|
}
|
||||||
|
}
|
||||||
84
platform/core/src/services/UINotificationService/index.js
Normal file
84
platform/core/src/services/UINotificationService/index.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* A UI Notification
|
||||||
|
*
|
||||||
|
* @typedef {Object} Notification
|
||||||
|
* @property {string} title -
|
||||||
|
* @property {string} message -
|
||||||
|
* @property {number} [duration=5000] - in ms
|
||||||
|
* @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
|
||||||
|
* @property {string} [type="info"] - "info" | "error" | "warning" | "success"
|
||||||
|
* @property {boolean} [autoClose=true]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uiNotificationServicePublicApi = {
|
||||||
|
name: 'UINotificationService',
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
setServiceImplementation,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uiNotificationServiceImplementation = {
|
||||||
|
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
|
||||||
|
_show: () => console.warn('show() NOT IMPLEMENTED'),
|
||||||
|
};
|
||||||
|
|
||||||
|
function createUiNotificationService() {
|
||||||
|
return uiNotificationServicePublicApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and show a new UI notification; returns the
|
||||||
|
* ID of the created notification.
|
||||||
|
*
|
||||||
|
* @param {Notification} notification { title, message, duration, position, type, autoClose}
|
||||||
|
* @returns {number} id
|
||||||
|
*/
|
||||||
|
function show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
duration = 5000,
|
||||||
|
position = 'bottomRight',
|
||||||
|
type = 'info',
|
||||||
|
autoClose = true,
|
||||||
|
}) {
|
||||||
|
return uiNotificationServiceImplementation._show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
duration,
|
||||||
|
position,
|
||||||
|
type,
|
||||||
|
autoClose,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides/dismisses the notification, if currently shown
|
||||||
|
*
|
||||||
|
* @param {number} id - id of the notification to hide/dismiss
|
||||||
|
* @returns undefined
|
||||||
|
*/
|
||||||
|
function hide(id) {
|
||||||
|
return uiNotificationServiceImplementation._hide({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param {*} {
|
||||||
|
* hide: hideImplementation,
|
||||||
|
* show: showImplementation,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function setServiceImplementation({
|
||||||
|
hide: hideImplementation,
|
||||||
|
show: showImplementation,
|
||||||
|
}) {
|
||||||
|
if (hideImplementation) {
|
||||||
|
uiNotificationServiceImplementation._hide = hideImplementation;
|
||||||
|
}
|
||||||
|
if (showImplementation) {
|
||||||
|
uiNotificationServiceImplementation._show = showImplementation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createUiNotificationService;
|
||||||
4
platform/core/src/services/index.js
Normal file
4
platform/core/src/services/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import ServicesManager from './ServicesManager.js';
|
||||||
|
import createUiNotificationService from './UINotificationService';
|
||||||
|
|
||||||
|
export { createUiNotificationService, ServicesManager };
|
||||||
@ -1,4 +1,10 @@
|
|||||||
import React, { useState, createContext, useContext } from 'react';
|
import React, {
|
||||||
|
useState,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
} from 'react';
|
||||||
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
|
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
|
||||||
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
|
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
|
||||||
|
|
||||||
@ -6,7 +12,7 @@ const SnackbarContext = createContext(null);
|
|||||||
|
|
||||||
export const useSnackbarContext = () => useContext(SnackbarContext);
|
export const useSnackbarContext = () => useContext(SnackbarContext);
|
||||||
|
|
||||||
const SnackbarProvider = ({ children }) => {
|
const SnackbarProvider = ({ children, service }) => {
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
title: '',
|
title: '',
|
||||||
message: '',
|
message: '',
|
||||||
@ -19,7 +25,11 @@ const SnackbarProvider = ({ children }) => {
|
|||||||
const [count, setCount] = useState(1);
|
const [count, setCount] = useState(1);
|
||||||
const [snackbarItems, setSnackbarItems] = useState([]);
|
const [snackbarItems, setSnackbarItems] = useState([]);
|
||||||
|
|
||||||
const show = options => {
|
useEffect(() => {
|
||||||
|
service.setServiceImplementation({ hide, show });
|
||||||
|
}, [service, hide, show]);
|
||||||
|
|
||||||
|
const show = useCallback(options => {
|
||||||
if (!options || (!options.title && !options.message)) {
|
if (!options || (!options.title && !options.message)) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Snackbar cannot be rendered without required parameters: title | message'
|
'Snackbar cannot be rendered without required parameters: title | message'
|
||||||
@ -37,9 +47,9 @@ const SnackbarProvider = ({ children }) => {
|
|||||||
|
|
||||||
setSnackbarItems(state => [...state, newItem]);
|
setSnackbarItems(state => [...state, newItem]);
|
||||||
setCount(count + 1);
|
setCount(count + 1);
|
||||||
};
|
});
|
||||||
|
|
||||||
const hide = id => {
|
const hide = useCallback(id => {
|
||||||
const hideItem = items => {
|
const hideItem = items => {
|
||||||
const newItems = items.map(item => {
|
const newItems = items.map(item => {
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
@ -57,7 +67,7 @@ const SnackbarProvider = ({ children }) => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
|
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
});
|
||||||
|
|
||||||
const hideAll = () => {
|
const hideAll = () => {
|
||||||
// reset count
|
// reset count
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import './config';
|
|||||||
import {
|
import {
|
||||||
CommandsManager,
|
CommandsManager,
|
||||||
ExtensionManager,
|
ExtensionManager,
|
||||||
|
ServicesManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
createUiNotificationService,
|
||||||
utils,
|
utils,
|
||||||
} from '@ohif/core';
|
} from '@ohif/core';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
@ -41,9 +43,16 @@ const commandsManagerConfig = {
|
|||||||
getActiveContexts: () => getActiveContexts(store.getState()),
|
getActiveContexts: () => getActiveContexts(store.getState()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Services
|
||||||
|
const UINotificationService = createUiNotificationService();
|
||||||
|
|
||||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||||
const hotkeysManager = new HotkeysManager(commandsManager);
|
const hotkeysManager = new HotkeysManager(commandsManager);
|
||||||
const extensionManager = new ExtensionManager({ commandsManager });
|
const servicesManager = new ServicesManager();
|
||||||
|
const extensionManager = new ExtensionManager({
|
||||||
|
commandsManager,
|
||||||
|
servicesManager,
|
||||||
|
});
|
||||||
// ~~~~ END APP SETUP
|
// ~~~~ END APP SETUP
|
||||||
|
|
||||||
// TODO[react] Use a provider when the whole tree is React
|
// TODO[react] Use a provider when the whole tree is React
|
||||||
@ -76,9 +85,11 @@ class App extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._appConfig = props;
|
this._appConfig = props;
|
||||||
|
|
||||||
const { servers, extensions, hotkeys, oidc } = props;
|
const { servers, extensions, hotkeys, oidc } = props;
|
||||||
|
|
||||||
this.initUserManager(oidc);
|
this.initUserManager(oidc);
|
||||||
|
_initServices([UINotificationService]);
|
||||||
_initExtensions(extensions, hotkeys);
|
_initExtensions(extensions, hotkeys);
|
||||||
_initServers(servers);
|
_initServers(servers);
|
||||||
initWebWorkers();
|
initWebWorkers();
|
||||||
@ -100,7 +111,7 @@ class App extends Component {
|
|||||||
<UserManagerContext.Provider value={userManager}>
|
<UserManagerContext.Provider value={userManager}>
|
||||||
<Router basename={routerBasename}>
|
<Router basename={routerBasename}>
|
||||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||||
<SnackbarProvider>
|
<SnackbarProvider service={UINotificationService}>
|
||||||
<ModalProvider modal={OHIFModal}>
|
<ModalProvider modal={OHIFModal}>
|
||||||
<OHIFStandaloneViewer userManager={userManager} />
|
<OHIFStandaloneViewer userManager={userManager} />
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
@ -121,7 +132,7 @@ class App extends Component {
|
|||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<Router basename={routerBasename}>
|
<Router basename={routerBasename}>
|
||||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||||
<SnackbarProvider>
|
<SnackbarProvider service={UINotificationService}>
|
||||||
<ModalProvider modal={OHIFModal}>
|
<ModalProvider modal={OHIFModal}>
|
||||||
<OHIFStandaloneViewer />
|
<OHIFStandaloneViewer />
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
@ -168,6 +179,10 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _initServices(services) {
|
||||||
|
services.forEach(service => servicesManager.register(service));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param
|
* @param
|
||||||
*/
|
*/
|
||||||
@ -214,4 +229,4 @@ function _makeAbsoluteIfNecessary(url, base_url) {
|
|||||||
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
|
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
|
||||||
|
|
||||||
export default ExportedApp;
|
export default ExportedApp;
|
||||||
export { commandsManager, extensionManager, hotkeysManager };
|
export { commandsManager, extensionManager, hotkeysManager, servicesManager };
|
||||||
|
|||||||
@ -7,8 +7,8 @@ export default {
|
|||||||
*/
|
*/
|
||||||
id: 'measurements-table',
|
id: 'measurements-table',
|
||||||
|
|
||||||
preRegistration(configuration = {}) {
|
preRegistration({ serviceManager, configuration = {} }) {
|
||||||
init(configuration);
|
init({ serviceManager, configuration });
|
||||||
},
|
},
|
||||||
getPanelModule() {
|
getPanelModule() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -34,7 +34,7 @@ const MEASUREMENT_ACTION_MAP = {
|
|||||||
* @export
|
* @export
|
||||||
* @param {*} configuration
|
* @param {*} configuration
|
||||||
*/
|
*/
|
||||||
export default function init(configuration) {
|
export default function init({ serviceManager, configuration = {} }) {
|
||||||
// If these tools were already added by a different extension, we want to replace
|
// If these tools were already added by a different extension, we want to replace
|
||||||
// them with the same tools that have an alternative configuration. By passing in
|
// them with the same tools that have an alternative configuration. By passing in
|
||||||
// our custom `getMeasurementLocationCallback`, we can...
|
// our custom `getMeasurementLocationCallback`, we can...
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user