Adding loading state and error message when saving measurements

This commit is contained in:
Bruno Alves de Faria 2017-02-15 17:49:32 -02:00
parent 5d8c608e70
commit 6b17deabcb
11 changed files with 129 additions and 32 deletions

View File

@ -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();
}
}

View File

@ -0,0 +1,13 @@
<template name="dialogInfo">
{{#dialogSimple (extend this
title=(choose this.title 'Error')
)}}
<div class="messages">
{{#each message in this.messages}}
<div class="message">{{{message}}}</div>
{{else}}
<div class="message">An error has ocurred.</div>
{{/each}}
</div>
{{/dialogSimple}}
</template>

View File

@ -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());
});

View File

@ -0,0 +1,7 @@
<template name="dialogLoading">
<div id="{{this.id}}" class="modal fade" tabindex="-1" role="dialog">
<div class="loading-text noselect">
{{choose this.text 'Loading...'}} <i class="fa fa-spin fa-circle-o-notch fa-fw"></i>
</div>
</div>
</template>

View File

@ -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
});
});

View File

@ -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

View File

@ -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';

View File

@ -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;

View File

@ -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'

View File

@ -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');
});
}

View File

@ -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'));
}
});