Removing progressDialog from DOM

This commit is contained in:
Bruno Alves de Faria 2017-03-06 17:05:03 -03:00
parent cb2d0d18aa
commit c7f31ddb0a
11 changed files with 186 additions and 313 deletions

View File

@ -28,5 +28,4 @@
<div class="tab-pane" id="viewerTab">
</div>
</div>
{{>progressDialog}}
</template>

View File

@ -1,82 +1,87 @@
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { ReactiveVar } from 'meteor/reactive-var';
import { _ } from 'meteor/underscore';
Template.dialogProgress.onCreated(() => {
const instance = Template.instance();
const instance = Template.instance();
Session.set('progressDialogState', {
processed: 0,
total: instance.data.total,
message: instance.data.message
});
})
instance.state = new ReactiveVar({
processed: 0,
total: instance.data.total,
message: instance.data.message
});
});
Template.dialogProgress.onRendered(() => {
const instance = Template.instance();
const task = instance.data.task;
const state = Session.get('progressDialogState');
const instance = Template.instance();
const task = instance.data.task;
const progressDialog = {
promise: instance.data.promise,
const progressDialog = {
promise: instance.data.promise,
done: value => {
// Hide the modal, removing the backdrop
instance.$('.modal').on('hidden.bs.modal', event => {
instance.data.promiseResolve(value);
}).modal('hide');
},
done: value => {
// Hide the modal, removing the backdrop
instance.$('.modal').on('hidden.bs.modal', event => {
instance.data.promiseResolve(value);
}).modal('hide');
},
cancel: () => {
// Hide the modal, removing the backdrop
instance.$('.modal').on('hidden.bs.modal', event => {
instance.data.promiseReject();
}).modal('hide');
},
cancel: () => {
// Hide the modal, removing the backdrop
instance.$('.modal').on('hidden.bs.modal', event => {
instance.data.promiseReject();
}).modal('hide');
},
update: _.throttle(processed => {
const state = Session.get('progressDialogState');
state.processed = Math.max(0, processed);
update: _.throttle(processed => {
const state = instance.state.get();
state.processed = Math.max(0, processed);
Session.set('progressDialogState', state);
}, 100),
instance.state.set(state);
}, 100),
setTotal: _.throttle(total => {
const state = Session.get('progressDialogState');
state.total = total;
setTotal: _.throttle(total => {
const state = instance.state.get();
state.total = total;
Session.set('progressDialogState', state);
}, 100),
instance.state.set(state);
}, 100),
setMessage: _.throttle(message => {
const state = Session.get('progressDialogState');
state.message = message;
setMessage: _.throttle(message => {
const state = instance.state.get();
state.message = message;
Session.set('progressDialogState', state);
}, 100)
}
instance.state.set(state);
}, 100)
};
task.run(progressDialog);
task.run(progressDialog);
});
Template.dialogProgress.helpers({
progress() {
const state = Session.get('progressDialogState');
progress() {
const instance = Template.instance();
const state = instance.state.get();
if (!state || !state.total) {
return 0;
if (!state || !state.total) {
return 0;
}
return Math.min(1, state.processed / state.total) * 100;
},
message() {
const instance = Template.instance();
const state = instance.state.get();
if (!state) {
return;
}
if (typeof state.message === 'function') {
return state.message(state);
}
return state.message;
}
return Math.min(1, state.processed / state.total) * 100;
},
message() {
const state = Session.get('progressDialogState');
if (!state) {
return;
}
return state.message;
}
})
});

View File

@ -1,4 +1,3 @@
import './progressDialog';
import './seriesDetailsModal';
import './serverInformation';
import './studyContextMenu';

View File

@ -1,3 +0,0 @@
import './progressDialog.html';
import './progressDialog.styl';
import './progressDialog.js';

View File

@ -1,18 +0,0 @@
<template name="progressDialog">
<div id="progressDialog" tabindex="0">
<div class="dialogHeader">
<h5>{{progressDialogTitle}}</h5>
</div>
<div class="dialogContent">
<div class="progress">
<div class="progress-bar progress-bar-striped active"
role="progressbar"
style="width: {{progressStatus}}%"
>{{formatNumberPrecision progressStatus 0}}%</div>
</div>
<div id="message">
{{{progressMessage}}}
</div>
</div>
</div>
</template>

View File

@ -1,78 +0,0 @@
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { _ } from 'meteor/underscore';
import { OHIF } from 'meteor/ohif:core';
const updateProgress = numberOfCompleted => {
const progressDialogSettings = Session.get('progressDialogSettings');
progressDialogSettings.numberOfCompleted = numberOfCompleted;
Session.set('progressDialogSettings', progressDialogSettings);
if (progressDialogSettings.numberOfCompleted === progressDialogSettings.numberOfTotal) {
OHIF.studylist.progressDialog.close();
}
};
const setProgressMessage = message => {
let progressDialogSettings = Session.get('progressDialogSettings');
progressDialogSettings.message = message;
Session.set('progressDialogSettings', progressDialogSettings);
};
OHIF.studylist.progressDialog = {
update: _.debounce(updateProgress, 100),
setMessage: _.debounce(setProgressMessage, 100),
show: function(title, numberOfTotal) {
Session.set('progressDialogSettings', {
title: title,
numberOfCompleted: 0,
numberOfTotal: numberOfTotal
});
$('#progressDialog').css('display', 'block');
},
close: function() {
Session.set('progressDialogSettings', {
title: '',
numberOfCompleted: 0,
numberOfTotal: 1
});
$('#progressDialog').css('display', 'none');
}
};
Template.progressDialog.helpers({
progressDialogTitle() {
const settings = Session.get('progressDialogSettings');
if (!settings) {
return;
}
return settings.title;
},
progressStatus() {
const settings = Session.get('progressDialogSettings');
if (!settings) {
return;
}
if (settings.numberOfCompleted === undefined ||
settings.numberOfTotal === undefined) {
return;
}
return (settings.numberOfCompleted / settings.numberOfTotal) * 100;
},
progressMessage() {
const settings = Session.get('progressDialogSettings');
if (!settings) {
return;
}
return settings.message;
}
});

View File

@ -1,23 +0,0 @@
#progressDialog
display: none
position: fixed
left: 0
right: 0
margin: auto
width: 20%
min-height: 85px;
z-index: 100
border-radius: 5px
padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1)
outline: none
.dialogContent
margin-bottom: 20px
#message
width: 100%
span
display: inline-block;
width: 100%

View File

@ -2,7 +2,7 @@
<div id="studylistTabs" class="tab-content">
<div role="tabpanel" class="tab-pane active" id="studylistTab">
<div class="studylistContainer">
{{> studylistResult (clone this)}}
{{>studylistResult (clone this)}}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="viewerTab">
@ -11,7 +11,6 @@
</div>
</div>
{{#each templateName in additionalTemplates}}
{{> UI.dynamic template=templateName}}
{{>Template.dynamic template=templateName}}
{{/each}}
{{>progressDialog}}
</template>

View File

@ -1,12 +1,12 @@
<template name="studylistToolbar">
<div class='studylistToolbar'>
{{#if importSupported }}
<div class="addNewStudy btn-file">
<label for="btnImport">
<i class="fa fa-plus"></i> Import study
</label>
<input id="btnImport" class='js-import-files' type="file" webkitdirectory directory multiple>
</div>
{{/if }}
{{#if importSupported}}
<div class="addNewStudy btn-file">
<label for="btnImport">
<i class="fa fa-plus"></i> Import study
</label>
<input id="btnImport" class='js-import-files' type="file" webkitdirectory directory multiple>
</div>
{{/if}}
</div>
</template>
</template>

View File

@ -19,7 +19,7 @@ Template.studylistToolbar.events({
// Get selected files located in the client machine
const selectedFiles = $.map(event.currentTarget.files, value => value);
importStudies(selectedFiles);
OHIF.studylist.importStudies(selectedFiles);
},
'click .js-import-files'(event) {

View File

@ -1,133 +1,126 @@
import { Meteor } from 'meteor/meteor';
import { OHIF } from 'meteor/ohif:core';
/**
* Imports selected studies from local into studylist
* @param filesToImport Files located in the client machine to import
*/
importStudies = function(filesToImport, importCallback) {
if (filesToImport.length < 1) {
return;
OHIF.studylist.importStudies = filesToImport => {
const numberOfFiles = filesToImport && filesToImport.length;
if (!numberOfFiles) {
return new Promise((resolve, reject) => reject('No files to upload'));
}
var fileUploadStatus = {
numberOfFilesUploaded: 0,
numberOfFilesFailed: 0
const uploadMessage = ({ processed, total }) => `Uploaded files: ${processed} / ${total}`;
const taskRunHandler = dialog => {
const uploadErrorHandler = fileNames => {
const names = fileNames.join('; ');
dialog.setMessage(`Failed to upload files: ${names}`);
};
const uploadSuccessHandler = studiesToImport => {
importStudiesInternal(studiesToImport, dialog).then(() => {
dialog.done();
}).catch(errorMessage => {
dialog.setMessage(errorMessage);
});
};
uploadFiles(filesToImport, dialog).then(uploadSuccessHandler).catch(uploadErrorHandler);
};
var numberOfFilesToUpload = filesToImport.length;
var studiesToImport = [];
OHIF.studylist.progressDialog.show({
title: "Uploading Files...",
numberOfCompleted: 0,
numberOfTotal: numberOfFilesToUpload
});
// Upload files to the server
filesToImport.forEach(function(fileToUpload) {
var xhr = new XMLHttpRequest();
xhr.open('POST', "/uploadFilesToImport", true);
xhr.setRequestHeader("filename", fileToUpload.name);
xhr.onload = function() {
// Failed to upload a file
if (xhr.readyState === 4 && xhr.status !== 200) {
updateFileUploadStatus(fileUploadStatus, false);
return;
}
studiesToImport.push(xhr.responseText);
updateFileUploadStatus(fileUploadStatus, true);
var numberOfFilesProcessedToUpload = fileUploadStatus.numberOfFilesUploaded + fileUploadStatus.numberOfFilesFailed;
OHIF.studylist.progressDialog.update(numberOfFilesProcessedToUpload);
if (numberOfFilesToUpload === numberOfFilesProcessedToUpload) {
// The upload is completed, so import files
importStudiesInternal(studiesToImport, importCallback);
if (fileUploadStatus.numberOfFilesFailed > 0) {
//TODO: Some files failed to upload, so let user know
OHIF.log.info("Failed to upload " + fileUploadStatus.numberOfFilesFailed + " of " + numberOfFilesToUpload + " files");
}
}
};
// Failed to upload a file
xhr.onerror = function() {
updateFileUploadStatus(fileUploadStatus, false);
};
xhr.send(fileToUpload);
return OHIF.ui.showDialog('dialogProgress', {
title: 'Importing Studies...',
message: uploadMessage,
total: numberOfFiles,
task: { run: taskRunHandler }
});
};
function updateFileUploadStatus(fileUploadStatus, isSuccess) {
if (!isSuccess) {
fileUploadStatus.numberOfFilesFailed++;
} else {
fileUploadStatus.numberOfFilesUploaded++;
}
}
function importStudiesInternal(studiesToImport, importCallback) {
if (!studiesToImport) {
return;
}
const uploadFiles = (files, dialog) => {
let processed = 0;
var numberOfStudiesToImport = studiesToImport.length;
const promise = new Promise((resolve, reject) => {
const promises = [];
OHIF.studylist.progressDialog.show({
title: "Importing Studies...",
numberOfCompleted: 0,
numberOfTotal: numberOfStudiesToImport
});
// Create/Insert a new study import status item
Meteor.call("createStudyImportStatus", function(err, studyImportStatusId) {
if (err) {
// Hide dialog
OHIF.studylist.progressDialog.close();
console.log(err.message);
return;
}
// Handle when StudyImportStatus collection is updated
OHIF.studylist.collections.StudyImportStatus.find(studyImportStatusId).observe({
changed: function(studyImportStatus) {
if (!studyImportStatus) {
return;
}
var numberOfStudiesProcessedToImport = studyImportStatus.numberOfStudiesImported + studyImportStatus.numberOfStudiesFailed;
// Show number of imported files
var successMessage = 'Imported '+studyImportStatus.numberOfStudiesImported+' of '+numberOfStudiesToImport;
OHIF.studylist.progressDialog.setMessage({
message: successMessage,
messageType: 'success'
});
OHIF.studylist.progressDialog.update(numberOfStudiesProcessedToImport);
// Show number of failed files if there is at least one failed file
if (studyImportStatus.numberOfStudiesFailed > 0) {
var successMessage = 'Failed '+studyImportStatus.numberOfStudiesFailed+' of '+numberOfStudiesToImport;
OHIF.studylist.progressDialog.setMessage({
message: successMessage,
messageType: 'warning'
});
}
if (numberOfStudiesProcessedToImport == numberOfStudiesToImport) {
// The entire import operation is completed, so remove the study import status item
Meteor.call("removeStudyImportStatus", studyImportStatus._id);
// Let the caller know that import operation is completed
if (importCallback) {
importCallback();
}
}
}
// Upload files to the server
files.forEach(file => {
const filePromise = uploadFile(file, dialog);
filePromise.then(() => dialog.update(++processed));
promises.push(filePromise);
});
// Import studies with study import status id to get callbacks
Meteor.call("importStudies", studiesToImport, studyImportStatusId);
Promise.all(promises).then(resolve).catch(reject);
});
}
return promise;
};
const uploadFile = file => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/uploadFilesToImport', true);
xhr.setRequestHeader('filename', file.name);
xhr.onload = () => {
if (xhr.readyState === 4 && xhr.status !== 200) {
// Failed to upload the file
reject(file.name);
} else {
// Success uploading the file
resolve(xhr.responseText);
}
};
// Failed to upload the file
xhr.onerror = () => reject(file.name);
xhr.send(file);
});
};
const importStudiesInternal = (studiesToImport, dialog) => {
const numberOfStudies = studiesToImport && studiesToImport.length;
if (!numberOfStudies) {
return new Promise((resolve, reject) => reject('No studies to import'));
}
let processed = 0;
dialog.update(processed);
dialog.setTotal(numberOfStudies);
dialog.setMessage(({ processed, total }) => `Imported: ${processed} / ${total}`);
return new Promise((resolve, reject) => {
// Create/Insert a new study import status item
Meteor.call('createStudyImportStatus', (error, studyImportStatusId) => {
if (error) {
return reject(error.message);
}
// Handle when StudyImportStatus collection is updated
OHIF.studylist.collections.StudyImportStatus.find(studyImportStatusId).observe({
changed(studyImportStatus) {
const { numberOfStudiesImported, numberOfStudiesFailed } = studyImportStatus;
dialog.update(numberOfStudiesImported);
if (numberOfStudiesImported === numberOfStudies) {
// The entire import operation is completed, so remove the study import status item
Meteor.call('removeStudyImportStatus', studyImportStatus._id);
// Show number of failed files if there is at least one failed file
if (studyImportStatus.numberOfStudiesFailed > 0) {
const failed = numberOfStudiesFailed;
reject(`Failed to import ${failed} of ${numberOfStudies} studies`);
} else {
resolve();
}
}
}
});
// Import studies with study import status id to get callbacks
Meteor.call('importStudies', studiesToImport, studyImportStatusId);
});
});
};