From 6b17deabcb2e8fbf95298816d7d80a1b07102eb1 Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Wed, 15 Feb 2017 17:49:32 -0200 Subject: [PATCH] Adding loading state and error message when saving measurements --- .../components/bootstrap/dialog/form.js | 13 +++-- .../components/bootstrap/dialog/info.html | 13 +++++ .../components/bootstrap/dialog/info.js | 9 +++ .../components/bootstrap/dialog/loading.html | 7 +++ .../components/bootstrap/dialog/loading.js | 14 +++++ .../components/bootstrap/dialog/loading.styl | 9 +++ .../client/components/bootstrap/index.js | 8 ++- .../ohif-core/client/ui/dialog/display.js | 57 ++++++++++++++----- Packages/ohif-core/package.js | 1 + .../both/configuration/measurements.js | 2 +- .../components/caseProgress/caseProgress.js | 28 ++++++--- 11 files changed, 129 insertions(+), 32 deletions(-) create mode 100644 Packages/ohif-core/client/components/bootstrap/dialog/info.html create mode 100644 Packages/ohif-core/client/components/bootstrap/dialog/info.js create mode 100644 Packages/ohif-core/client/components/bootstrap/dialog/loading.html create mode 100644 Packages/ohif-core/client/components/bootstrap/dialog/loading.js create mode 100644 Packages/ohif-core/client/components/bootstrap/dialog/loading.styl diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/form.js b/Packages/ohif-core/client/components/bootstrap/dialog/form.js index 227d294ab..fa57dd5d5 100644 --- a/Packages/ohif-core/client/components/bootstrap/dialog/form.js +++ b/Packages/ohif-core/client/components/bootstrap/dialog/form.js @@ -1,5 +1,6 @@ import { Template } from 'meteor/templating'; import { _ } from 'meteor/underscore'; +import { OHIF } from 'meteor/ohif:core'; Template.dialogForm.onCreated(() => { const instance = Template.instance(); @@ -14,7 +15,7 @@ Template.dialogForm.onCreated(() => { } // Hide the modal, removing the backdrop - instance.$('.modal').on('hidden.bs.modal', event => { + instance.$('.modal').one('hidden.bs.modal', event => { // Get the form value and call the confirm callback or resolve the promise const formData = form.value(); if (_.isFunction(instance.data.confirmCallback)) { @@ -27,7 +28,7 @@ Template.dialogForm.onCreated(() => { cancel() { // Hide the modal, removing the backdrop - instance.$('.modal').on('hidden.bs.modal', event => { + instance.$('.modal').one('hidden.bs.modal', event => { // Call the cancel callback or resolve the promise if (_.isFunction(instance.data.cancelCallback)) { instance.data.cancelCallback(instance.data.promiseReject); @@ -61,21 +62,21 @@ Template.dialogForm.onRendered(() => { }); Template.dialogForm.events({ - 'keydown'(event) { + keydown(event) { const instance = Template.instance(), keyCode = event.keyCode || event.which; let handled = false; - if(keyCode === 27) { + if (keyCode === 27) { instance.$('.btn.btn-cancel').click(); handled = true; - } else if(keyCode === 13) { + } else if (keyCode === 13) { instance.$('.btn.btn-confirm').click(); handled = true; } - if(handled) { + if (handled) { event.stopPropagation(); } } diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/info.html b/Packages/ohif-core/client/components/bootstrap/dialog/info.html new file mode 100644 index 000000000..9491e2e80 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/dialog/info.html @@ -0,0 +1,13 @@ + diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/info.js b/Packages/ohif-core/client/components/bootstrap/dialog/info.js new file mode 100644 index 000000000..bc8797375 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/dialog/info.js @@ -0,0 +1,9 @@ +import { Template } from 'meteor/templating'; + +Template.dialogInfo.onRendered(() => { + const instance = Template.instance(); + + const $modal = instance.$('.modal'); + + $modal.one('hidden.bs.modal', () => instance.data.promiseResolve()); +}); diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/loading.html b/Packages/ohif-core/client/components/bootstrap/dialog/loading.html new file mode 100644 index 000000000..069ad7326 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/dialog/loading.html @@ -0,0 +1,7 @@ + diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/loading.js b/Packages/ohif-core/client/components/bootstrap/dialog/loading.js new file mode 100644 index 000000000..cbcef6b85 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/dialog/loading.js @@ -0,0 +1,14 @@ +import { Template } from 'meteor/templating'; + +Template.dialogLoading.onRendered(() => { + const instance = Template.instance(); + + const $modal = instance.$('.modal'); + + // Create the bootstrap modal + $modal.modal({ + backdrop: 'static', + keyboard: false, + modal: true + }); +}); diff --git a/Packages/ohif-core/client/components/bootstrap/dialog/loading.styl b/Packages/ohif-core/client/components/bootstrap/dialog/loading.styl new file mode 100644 index 000000000..5b8af1858 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/dialog/loading.styl @@ -0,0 +1,9 @@ +@import "{ohif:design}/app" + +.modal .loading-text + theme('color', '$textSecondaryColor') + font-size: 30px + height: 100vh + line-height: 100vh + text-align: center + text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, -1px 1px 0 #000, 1px -1px 0 #000, -1px 0 0 #000, 0 -1px 0 #000, 1px 0 0 #000, 0 1px 0 #000 diff --git a/Packages/ohif-core/client/components/bootstrap/index.js b/Packages/ohif-core/client/components/bootstrap/index.js index 54858771b..8cf23160d 100644 --- a/Packages/ohif-core/client/components/bootstrap/index.js +++ b/Packages/ohif-core/client/components/bootstrap/index.js @@ -1,10 +1,14 @@ import './dialog/confirm.html'; -import './dialog/progress.html'; -import './dialog/progress.js'; import './dialog/form.html'; import './dialog/form.js'; +import './dialog/info.html'; +import './dialog/info.js'; +import './dialog/loading.html'; +import './dialog/loading.js'; import './dialog/login.html'; import './dialog/login.js'; +import './dialog/progress.html'; +import './dialog/progress.js'; import './dialog/simple.html'; import './dialog/simple.js'; import './dialog/unsavedChangesDialog.html'; diff --git a/Packages/ohif-core/client/ui/dialog/display.js b/Packages/ohif-core/client/ui/dialog/display.js index 12127d551..21c509bf2 100644 --- a/Packages/ohif-core/client/ui/dialog/display.js +++ b/Packages/ohif-core/client/ui/dialog/display.js @@ -1,6 +1,7 @@ import { Template } from 'meteor/templating'; import { Blaze } from 'meteor/blaze'; import { _ } from 'meteor/underscore'; +import { $ } from 'meteor/jquery'; import { OHIF } from 'meteor/ohif:core'; OHIF.ui.showDialog = (templateName, dialogData) => { @@ -13,25 +14,51 @@ OHIF.ui.showDialog = (templateName, dialogData) => { }; } - // Create a new promise to control the modal and store its resolve and reject callbacks - let promiseResolve; - let promiseReject; - const promise = new Promise((resolve, reject) => { - promiseResolve = resolve; - promiseReject = reject; - }); + let promise; + let templateData; + if (dialogData && dialogData.promise instanceof Promise) { + // Use the given promise to control the modal + promise = dialogData.promise; + templateData = dialogData; + } else { + // Create a new promise to control the modal and store its resolve and reject callbacks + let promiseResolve; + let promiseReject; + promise = new Promise((resolve, reject) => { + promiseResolve = resolve; + promiseReject = reject; + }); + + // Render the dialog with the given template passing the promise object and callbacks + templateData = _.extend({}, dialogData, { + promise, + promiseResolve, + promiseReject + }); + } - // Render the dialog with the given template passing the promise object and callbacks - const templateData = _.extend({}, dialogData, { - promise, - promiseResolve, - promiseReject - }); const view = Blaze.renderWithData(template, templateData, document.body); + const node = view.firstNode(); + const $node = node && $(node); + + let $modal; + if ($node && $node.hasClass('modal')) { + $modal = $node; + } else if ($node && $node.has('.modal')) { + $modal = $node.find('.modal:first'); + } + // Destroy the created dialog view when the promise is either resolved or rejected - const dismissModal = () => Blaze.remove(view); - promise.then(dismissModal, dismissModal); + const dismissModal = () => { + if (dialogData.promise && $modal) { + $modal.one('hidden.bs.modal', () => Blaze.remove(view)).modal('hide'); + } else { + Blaze.remove(view); + } + }; + + promise.then(dismissModal).catch(dismissModal); // Return the promise to allow callbacks stacking from outside return promise; diff --git a/Packages/ohif-core/package.js b/Packages/ohif-core/package.js index ecb6ee825..13a80274b 100644 --- a/Packages/ohif-core/package.js +++ b/Packages/ohif-core/package.js @@ -26,6 +26,7 @@ Package.onUse(function(api) { api.addFiles([ 'client/ui/dimensional/dimensional.styl', 'client/ui/resizable/resizable.styl', + 'client/components/bootstrap/dialog/loading.styl', 'client/components/bootstrap/dialog/progress.styl', 'client/components/bootstrap/dialog/unsavedChangesDialog.styl', 'client/components/bootstrap/dropdown/dropdown.styl' diff --git a/Packages/ohif-measurements/both/configuration/measurements.js b/Packages/ohif-measurements/both/configuration/measurements.js index 497b03802..091956514 100644 --- a/Packages/ohif-measurements/both/configuration/measurements.js +++ b/Packages/ohif-measurements/both/configuration/measurements.js @@ -250,7 +250,7 @@ class MeasurementApi { }; OHIF.log.info('Saving Measurements for timepoints:', timepoints); - storeFn(measurementData, filter).then(() => { + return storeFn(measurementData, filter).then(() => { OHIF.log.info('Measurement storage completed'); }); } diff --git a/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js b/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js index 2cfdd0510..0e47e8684 100644 --- a/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js +++ b/Packages/ohif-measurements/client/components/caseProgress/caseProgress.js @@ -9,12 +9,27 @@ Template.caseProgress.onCreated(() => { instance.progressPercent = new ReactiveVar(); instance.progressText = new ReactiveVar(); instance.isLocked = new ReactiveVar(); + instance.path = 'viewer.studyViewer.measurements'; instance.saveData = () => { - instance.data.measurementApi.storeMeasurements(); + const api = instance.data.measurementApi; // Clear signaled unsaved changes... - OHIF.ui.unsavedChanges.clear('viewer.studyViewer.measurements.*'); + const successHandler = () => { + OHIF.ui.unsavedChanges.clear(`${instance.path}.*`); + }; + + // Display the error messages + const errorHandler = data => OHIF.ui.showDialog('dialogInfo', data); + + const promise = api.storeMeasurements(); + promise.then(successHandler).catch(errorHandler); + OHIF.ui.showDialog('dialogLoading', { + promise, + text: 'Saving measurements data' + }); + + return promise; }; instance.unsavedChangesHandler = () => { @@ -25,14 +40,13 @@ Template.caseProgress.onCreated(() => { }; // Attach handler for unsaved changes dialog... - OHIF.ui.unsavedChanges.attachHandler('viewer.studyViewer.measurements', 'save', instance.unsavedChangesHandler); - + OHIF.ui.unsavedChanges.attachHandler(instance.path, 'save', instance.unsavedChangesHandler); }); Template.caseProgress.onDestroyed(() => { const instance = Template.instance(); // Remove unsaved changes handler after this view has been destroyed... - OHIF.ui.unsavedChanges.removeHandler('viewer.studyViewer.measurements', 'save', instance.unsavedChangesHandler); + OHIF.ui.unsavedChanges.removeHandler(instance.path, 'save', instance.unsavedChangesHandler); }); Template.caseProgress.onRendered(() => { @@ -169,8 +183,6 @@ Template.caseProgress.events({ return; } - instance.saveData(); - switchToTab('studylistTab'); - + instance.saveData().then(() => switchToTab('studylistTab')); } });