Commit 1bc1c545 by tan

MA-333 Added ability to refresh uploaded videos

This adds ability to refresh the list of uploaded videos without refreshing the whole page.

Added a refresh button that when clicked:
- fetches a fresh list of uploaded files from the server
- updates `PreviousVideoUploadListView`
- removes the successfully completed uploads from `ActiveVideoUploadListView`
- retains the ongoing or failed uploads in `ActiveVideoUploadListView` so that they can be monitored/retried

The view can also be refreshed without user action, but I felt it may be less surprising to have a button instead.

MA-333 update: auto-refresh list, fix test failure

Changes:
1. Refresh of file list triggered by upload completion. Refresh button retained and label changed to "Refresh List".
2. Added `aria-live="polite"` to `.active-video-upload-list` and `.assets-table`.
3. Removed unused parameter `evt`.
4. Added self to the `AUTHORS` file.

MA-333 update: added tests

MA-333 update: removed refresh button

MA-333 update: address review comments of @mushtaqak

MA-333 update: simplify nested `_each`

MA-333 update: rename viewRefresh to isViewRefresh

MA-333 update: doc string for `clearSuccesful`

MA-333 update: fix accessibility

MA-333 update: update only successfully uploaded videos

MA-333 update: use window.SR feature to notify screen readers that video upload was successful (@pomegranited)
parent c012bd6c
......@@ -276,3 +276,4 @@ Kevin Kim <kkim@edx.org>
Albert St. Aubin Jr. <astaubin@edx.org>
Casey Litton <caseylitton@gmail.com>
Jhony Avella <jhony.avella@edunext.co>
Tanmay Mohapatra <tanmaykm@gmail.com>
......@@ -336,7 +336,7 @@ def videos_post(course, request):
"courses": [course.id]
})
resp_files.append({"file_name": file_name, "upload_url": upload_url})
resp_files.append({"file_name": file_name, "upload_url": upload_url, "edx_video_id": edx_video_id})
return JsonResponse({"files": resp_files}, status=200)
......
define(
['jquery', 'backbone', 'js/views/active_video_upload_list', 'js/views/previous_video_upload_list'],
function($, Backbone, ActiveVideoUploadListView, PreviousVideoUploadListView) {
'use strict';
var VideosIndexFactory = function(
$contentWrapper,
postUrl,
encodingsDownloadUrl,
concurrentUploadLimit,
uploadButton,
previousUploads
) {
var activeView = new ActiveVideoUploadListView({
define([
'jquery', 'backbone', 'js/views/active_video_upload_list',
'js/views/previous_video_upload_list', 'js/views/active_video_upload'
], function($, Backbone, ActiveVideoUploadListView, PreviousVideoUploadListView, ActiveVideoUpload) {
'use strict';
var VideosIndexFactory = function(
$contentWrapper,
postUrl,
encodingsDownloadUrl,
concurrentUploadLimit,
uploadButton,
previousUploads
) {
var activeView = new ActiveVideoUploadListView({
postUrl: postUrl,
concurrentUploadLimit: concurrentUploadLimit,
uploadButton: uploadButton
});
$contentWrapper.append(activeView.render().$el);
var previousCollection = new Backbone.Collection(previousUploads);
var previousView = new PreviousVideoUploadListView({
collection: previousCollection,
uploadButton: uploadButton,
onFileUploadDone: function(activeVideos) {
$.ajax({
url: postUrl,
contentType: 'application/json',
dataType: 'json',
type: 'GET'
}).done(function(responseData) {
var updatedCollection = new Backbone.Collection(responseData.videos).filter(function(video) {
// Include videos that are not in the active video upload list,
// or that are marked as Upload Complete
var isActive = activeVideos.where({videoId: video.get('edx_video_id')});
return isActive.length === 0 ||
isActive[0].get('status') === ActiveVideoUpload.STATUS_COMPLETE;
}),
updatedView = new PreviousVideoUploadListView({
collection: updatedCollection,
encodingsDownloadUrl: encodingsDownloadUrl
});
$contentWrapper.find('.wrapper-assets').replaceWith(updatedView.render().$el);
});
}
}),
previousView = new PreviousVideoUploadListView({
collection: new Backbone.Collection(previousUploads),
encodingsDownloadUrl: encodingsDownloadUrl
});
$contentWrapper.append(previousView.render().$el);
};
$contentWrapper.append(activeView.render().$el);
$contentWrapper.append(previousView.render().$el);
};
return VideosIndexFactory;
}
);
return VideosIndexFactory;
});
......@@ -19,6 +19,7 @@ define(
var ActiveVideoUpload = Backbone.Model.extend(
{
defaults: {
videoId: null,
status: statusStrings.STATUS_QUEUED,
progress: 0
}
......
......@@ -18,6 +18,7 @@ define(
this.listenTo(this.collection, 'add', this.addUpload);
this.concurrentUploadLimit = options.concurrentUploadLimit || 0;
this.postUrl = options.postUrl;
this.onFileUploadDone = options.onFileUploadDone;
if (options.uploadButton) {
options.uploadButton.click(this.chooseFile.bind(this));
}
......@@ -99,9 +100,10 @@ define(
// individual file uploads, using the extra `redirected` field to
// indicate that the correct upload url has already been retrieved
fileUploadAdd: function(event, uploadData) {
var view = this;
var view = this,
model;
if (uploadData.redirected) {
var model = new ActiveVideoUpload({fileName: uploadData.files[0].name});
model = new ActiveVideoUpload({fileName: uploadData.files[0].name, videoId: uploadData.videoId});
this.collection.add(model);
uploadData.cid = model.cid;
uploadData.submit();
......@@ -126,6 +128,7 @@ define(
view.$uploadForm.fileupload('add', {
files: [uploadData.files[index]],
url: file['upload_url'],
videoId: file.edx_video_id,
multipart: false,
global: false, // Do not trigger global AJAX error handler
redirected: true
......@@ -156,10 +159,48 @@ define(
fileUploadDone: function(event, data) {
this.setStatus(data.cid, ActiveVideoUpload.STATUS_COMPLETED);
this.setProgress(data.cid, 1);
if (this.onFileUploadDone) {
this.onFileUploadDone(this.collection);
this.clearSuccessful();
}
},
fileUploadFail: function(event, data) {
this.setStatus(data.cid, ActiveVideoUpload.STATUS_FAILED);
},
removeViewAt: function(index) {
this.itemViews.splice(index);
this.$('.active-video-upload-list li').eq(index).remove();
},
// Removes the upload progress view for files that have been
// uploaded successfully. Also removes the corresponding models
// from `collection`, keeping both in sync.
clearSuccessful: function() {
var idx,
completedIndexes = [],
completedModels = [],
completedMessages = [];
this.collection.each(function(model, index) {
if (model.get('status') === ActiveVideoUpload.STATUS_COMPLETED) {
completedModels.push(model);
completedIndexes.push(index - completedIndexes.length);
completedMessages.push(model.get('fileName') +
gettext(': video upload complete.'));
}
});
for (idx = 0; idx < completedIndexes.length; idx++) {
this.removeViewAt(completedIndexes[idx]);
this.collection.remove(completedModels[idx]);
}
// Alert screen readers that the uploads were successful
if (completedMessages.length) {
completedMessages.push(gettext('Previous Uploads table has been updated.'));
if ($(window).prop('SR') !== undefined) {
$(window).prop('SR').readTexts(completedMessages);
}
}
}
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment