Removing progressDialog from DOM
This commit is contained in:
parent
cb2d0d18aa
commit
c7f31ddb0a
@ -28,5 +28,4 @@
|
||||
<div class="tab-pane" id="viewerTab">
|
||||
</div>
|
||||
</div>
|
||||
{{>progressDialog}}
|
||||
</template>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import './progressDialog';
|
||||
import './seriesDetailsModal';
|
||||
import './serverInformation';
|
||||
import './studyContextMenu';
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
import './progressDialog.html';
|
||||
import './progressDialog.styl';
|
||||
import './progressDialog.js';
|
||||
@ -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>
|
||||
@ -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;
|
||||
}
|
||||
});
|
||||
@ -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%
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user