ohif-viewer/platform/viewer/src/App.js
Rodrigo Antinarelli 2f30e7a821
fix: Combined Hotkeys for special characters (#1233)
* fix: Combined Hotkeys for special characters

* add record method to hotkey manager

* fix record plugin

* remove unused component

* add record to modal props

* rename record method

* replace handlers to use hotkeyRecord

* fix combined keys

* change expected result count from 18 to 17

* autoformat

* Remove duplicate test, that was testing the wrong things; fix label; update configs

* Revert "Remove duplicate test, that was testing the wrong things; fix label; update configs"

This reverts commit 4292f4fe67351962d61cae623b920dcbd87dd71d.

* Fix the record plugin's registration

* fix exposed record method usage

* adding logging for info level items

* Hotkey definitions don't need to be globally reactive; use localstorage/appconfig as sources of truth; not redux

* Tidy up test

* Remove unused code from UserPreferencesForm

* Log info when we run a command

* fix hotkey preference restore

* use application configured hotkeys if there are no user preferred

* Avoid logging circular ref

* Fix callouts

* Fix small issue with array

* Fix langua issue after refactor and merge

* Refactor on recordCurrentCombo as Rodrigo did before

* Separating components in 2 files

* WIP Refactor to simplify the user preferences and move into each form the save and controll functionalities

* Remove context

* Remove unused import

* Initial work on Field treatment

* Refactor General preferences

* Small refactor removing type from HotkeyField

* small update on style

* Refactor and layout fixed

* Make hotkeys preferences working with old hotkeys row

* Move error handling out of hotkey row/input component

* WIP custom form

* Moving validation function to component

* Exposing hotkeyRecord as it does not depend on HotkeyManager Class

* Making hotkeyField as much detached possible from parent component

* Small refactors

* Refactor on user preferences

* Clean up into the changes

* Small fix to let save working

* Style finish

* move about docs into about folder

* Fix double tap on single keys

* Style refactor

* Remove log

* Fix log issues on unit tests

* Fix unit test breaking on ohif/core index

* Fixing hotkeys unpause unit test issue

* Rename file to adopt lowercase

* Rename file to adopt lowercase

* Fixing callouts

* Big refactor miving some of the components into viewer and creating small components into ohif/ui

* Typo on folder name

* Updating ohif ui docs

* Remove comments

* Fix binding of combo keys

* Fix some cypress tests failures

* Fixing onCancel button

* Fixing e2e tests

* Small style update

* Fixing unit tests failing after fix issue

* Remove some not used code

* Remove left over after debug

* Adding prevent default on hotkeys events

* Fixinf existing hotkeys validator with 3 keys pressed

* Exposing hotkeys as root level on ohif-core

* Clean up

* Exposing all availableLanguages with labels and fixing an issue on language switcher

* Fixing e2e cypress tests

* Preveinting some simple errors

* Treating error once we try to set hotkey definitions

* Adding ui notification on setHotkeys errors

* Implementing a service queue request to hold until functions are implemented

* Making sure toFixed is only called on Numbers

Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
Co-authored-by: Gustavo André Lelis <galelis@gmail.com>
2020-02-12 15:35:04 -05:00

320 lines
8.8 KiB
JavaScript

import React, { Component } from 'react';
import { OidcProvider } from 'redux-oidc';
import { I18nextProvider } from 'react-i18next';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import { hot } from 'react-hot-loader/root';
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
import {
SnackbarProvider,
ModalProvider,
DialogProvider,
OHIFModal,
} from '@ohif/ui';
import {
CommandsManager,
ExtensionManager,
ServicesManager,
HotkeysManager,
UINotificationService,
UIModalService,
UIDialogService,
MeasurementService,
utils,
redux as reduxOHIF,
} from '@ohif/core';
import i18n from '@ohif/i18n';
// TODO: This should not be here
import './config';
/** Utils */
import {
getUserManagerForOpenIdConnectClient,
initWebWorkers,
} from './utils/index.js';
/** Extensions */
import { GenericViewerCommands, MeasurementsPanel } from './appExtensions';
/** Viewer */
import OHIFStandaloneViewer from './OHIFStandaloneViewer';
/** Store */
import { getActiveContexts } from './store/layout/selectors.js';
import store from './store';
/** Contexts */
import WhiteLabellingContext from './context/WhiteLabellingContext';
import UserManagerContext from './context/UserManagerContext';
import AppContext from './context/AppContext';
/** ~~~~~~~~~~~~~ Application Setup */
const commandsManagerConfig = {
getAppState: () => store.getState(),
getActiveContexts: () => getActiveContexts(store.getState()),
};
/** Managers */
const commandsManager = new CommandsManager(commandsManagerConfig);
const servicesManager = new ServicesManager();
const hotkeysManager = new HotkeysManager(commandsManager, servicesManager);
let extensionManager;
/** ~~~~~~~~~~~~~ End Application Setup */
// TODO[react] Use a provider when the whole tree is React
window.store = store;
class App extends Component {
static propTypes = {
config: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({
routerBasename: PropTypes.string.isRequired,
oidc: PropTypes.array,
whiteLabelling: PropTypes.object,
extensions: PropTypes.array,
}),
]).isRequired,
defaultExtensions: PropTypes.array,
};
static defaultProps = {
config: {
whiteLabelling: {},
oidc: [],
extensions: [],
},
defaultExtensions: [],
};
_appConfig;
_userManager;
constructor(props) {
super(props);
const { config, defaultExtensions } = props;
const appDefaultConfig = {
cornerstoneExtensionConfig: {},
extensions: [],
routerBasename: '/',
whiteLabelling: {},
};
this._appConfig = {
...appDefaultConfig,
...(typeof config === 'function' ? config({ servicesManager }) : config),
};
const {
servers,
hotkeys: appConfigHotkeys,
cornerstoneExtensionConfig,
extensions,
oidc,
} = this._appConfig;
this.initUserManager(oidc);
_initServices([
UINotificationService,
UIModalService,
UIDialogService,
MeasurementService,
]);
_initExtensions(
[...defaultExtensions, ...extensions],
cornerstoneExtensionConfig,
this._appConfig
);
/*
* Must run after extension commands are registered
* if there is no hotkeys from localStorage set up from config.
*/
_initHotkeys(appConfigHotkeys);
_initServers(servers);
initWebWorkers();
}
render() {
const { whiteLabelling, routerBasename } = this._appConfig;
const {
UINotificationService,
UIDialogService,
UIModalService,
MeasurementService,
} = servicesManager.services;
if (this._userManager) {
return (
<AppContext.Provider value={{ appConfig: this._appConfig }}>
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
modal={OHIFModal}
service={UIModalService}
>
<OHIFStandaloneViewer
userManager={this._userManager}
/>
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabellingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
</I18nextProvider>
</Provider>
</AppContext.Provider>
);
}
return (
<AppContext.Provider value={{ appConfig: this._appConfig }}>
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
<OHIFStandaloneViewer />
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabellingContext.Provider>
</Router>
</I18nextProvider>
</Provider>
</AppContext.Provider>
);
}
initUserManager(oidc) {
if (oidc && !!oidc.length) {
const firstOpenIdClient = this._appConfig.oidc[0];
const { protocol, host } = window.location;
const { routerBasename } = this._appConfig;
const baseUri = `${protocol}//${host}${routerBasename}`;
const redirect_uri = firstOpenIdClient.redirect_uri || '/callback';
const silent_redirect_uri =
firstOpenIdClient.silent_redirect_uri || '/silent-refresh.html';
const post_logout_redirect_uri =
firstOpenIdClient.post_logout_redirect_uri || '/';
const openIdConnectConfiguration = Object.assign({}, firstOpenIdClient, {
redirect_uri: _makeAbsoluteIfNecessary(redirect_uri, baseUri),
silent_redirect_uri: _makeAbsoluteIfNecessary(
silent_redirect_uri,
baseUri
),
post_logout_redirect_uri: _makeAbsoluteIfNecessary(
post_logout_redirect_uri,
baseUri
),
});
this._userManager = getUserManagerForOpenIdConnectClient(
store,
openIdConnectConfiguration
);
}
}
}
function _initServices(services) {
servicesManager.registerServices(services);
}
/**
* @param
*/
function _initExtensions(extensions, cornerstoneExtensionConfig, appConfig) {
extensionManager = new ExtensionManager({
commandsManager,
servicesManager,
appConfig,
});
const requiredExtensions = [
GenericViewerCommands,
[OHIFCornerstoneExtension, cornerstoneExtensionConfig],
/* WARNING: MUST BE REGISTERED _AFTER_ OHIFCornerstoneExtension */
MeasurementsPanel,
];
const mergedExtensions = requiredExtensions.concat(extensions);
extensionManager.registerExtensions(mergedExtensions);
}
/**
*
* @param {Object} appConfigHotkeys - Default hotkeys, as defined by app config
*/
function _initHotkeys(appConfigHotkeys) {
// TODO: Use something more resilient
// TODO: Mozilla has a special library for this
const userPreferredHotkeys = JSON.parse(
localStorage.getItem('hotkey-definitions') || '{}'
);
// TODO: hotkeysManager.isValidDefinitionObject(/* */)
const hasUserPreferences =
userPreferredHotkeys && Object.keys(userPreferredHotkeys).length > 0;
if (hasUserPreferences) {
hotkeysManager.setHotkeys(userPreferredHotkeys);
} else {
hotkeysManager.setHotkeys(appConfigHotkeys);
}
hotkeysManager.setDefaultHotKeys(appConfigHotkeys);
}
function _initServers(servers) {
if (servers) {
utils.addServers(servers, store);
}
}
function _isAbsoluteUrl(url) {
return url.includes('http://') || url.includes('https://');
}
function _makeAbsoluteIfNecessary(url, base_url) {
if (_isAbsoluteUrl(url)) {
return url;
}
/*
* Make sure base_url and url are not duplicating slashes.
*/
if (base_url[base_url.length - 1] === '/') {
base_url = base_url.slice(0, base_url.length - 1);
}
return base_url + url;
}
/*
* Only wrap/use hot if in dev.
*/
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
export default ExportedApp;
export { commandsManager, extensionManager, hotkeysManager, servicesManager };