Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 39 additions & 33 deletions administrator/components/com_media/resources/scripts/app/Api.es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,41 +181,47 @@ class Api {
}

/**
* Upload a file
* @param name
* @param parent
* @param content base64 encoded string
* @param override boolean whether or not we should override existing files
* @return {Promise.<T>}
*/
upload(name, parent, content, override) {
// Wrap the ajax call into a real promise
return new Promise((resolve, reject) => {
const url = new URL(`${this.baseUrl}&task=api.files&path=${encodeURIComponent(parent)}`);
const data = {
[this.csrfToken]: '1',
name,
content,
};
* Upload a file,
* In opposite to other API calls the Upload call uses Content-type: application/x-www-form-urlencoded
*
* @param {string} name File name
* @param {string} parent Parent folder path
* @param {File} content File instance
* @param {boolean} override whether we should override existing files or not
* @param {Function} progressCalback Progress callback
* @return {Promise.<T>}
*/
upload(name, parent, content, override, progressCalback) {
const url = `${this.baseUrl}&task=api.files&path=${encodeURIComponent(parent)}`;
const data = new FormData();
data.append('name', name);
data.append('content', content);

// Append override
if (override === true) {
data.override = true;
}
// Append override
if (override === true) {
data.append('override', 1);
}

Joomla.request({
url: url.toString(),
method: 'POST',
data: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
onSuccess: (response) => {
notifications.success('COM_MEDIA_UPLOAD_SUCCESS');
resolve(normalizeItem(JSON.parse(response).data));
},
onError: (xhr) => {
reject(xhr);
},
});
return Joomla.request({
url,
method: 'POST',
data,
promise: true,
onBefore: (xhr) => {
if (progressCalback) {
xhr.upload.addEventListener('progress', (event) => {
let progress = 100;
if (event.lengthComputable) {
progress = Math.round((event.loaded / event.total) * 100);
}
progressCalback(progress);
});
}
},
}).then((xhr) => {
const response = xhr.responseText;
notifications.success('COM_MEDIA_UPLOAD_SUCCESS');
return normalizeItem(JSON.parse(response).data);
}).catch(handleError);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,24 +247,11 @@ export default {

/* Upload files */
upload(file) {
// Create a new file reader instance
const reader = new FileReader();

// Add the on load callback
reader.onload = (progressEvent) => {
const { result } = progressEvent.target;
const splitIndex = result.indexOf('base64') + 7;
const content = result.slice(splitIndex, result.length);

// Upload the file
this.$store.dispatch('uploadFile', {
name: file.name,
parent: this.$store.state.selectedDirectory,
content,
});
};

reader.readAsDataURL(file);
this.$store.dispatch('uploadFile', {
name: file.name,
parent: this.$store.state.selectedDirectory,
content: file,
});
},

// Logic for the dropped file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
v-if="isLoading"
class="media-loader"
/>
<div
v-if="isUploading"
class="media-upload-progress"
>
<div
class="progress-bar"
:style="{ 'width': uploadProgress + '%' }"
/>
</div>
<div class="media-view-icons">
<input
ref="mediaToolbarSelectAll"
Expand Down Expand Up @@ -169,6 +178,13 @@ export default {
isLoading() {
return this.$store.state.isLoading;
},
isUploading() {
return this.$store.state.activeUploads.length > 0;
},
uploadProgress() {
// Use extra 2 for initial visibility
return Math.max(this.$store.state.uploadProgress, 2);
},
atLeastOneItemSelected() {
return this.$store.state.selectedItems.length > 0;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,11 @@ export default {

// Loop through array of files and upload each file
Array.from(files).forEach((file) => {
// Create a new file reader instance
const reader = new FileReader();

// Add the on load callback
reader.onload = (progressEvent) => {
const { result } = progressEvent.target;
const splitIndex = result.indexOf('base64') + 7;
const content = result.slice(splitIndex, result.length);

// Upload the file
this.$store.dispatch('uploadFile', {
name: file.name,
parent: this.$store.state.selectedDirectory,
content,
});
};

reader.readAsDataURL(file);
this.$store.dispatch('uploadFile', {
name: file.name,
parent: this.$store.state.selectedDirectory,
content: file,
});
});
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,29 @@ export const createDirectory = (context, payload) => {
};

/**
* Create a new folder
* Upload a file
* @param context
* @param payload object with the new folder name and its parent directory
* @param payload object with the new file data
*/
export const uploadFile = (context, payload) => {
if (!api.canCreate) {
return;
}
context.commit(types.SET_IS_LOADING, true);
api.upload(payload.name, payload.parent, payload.content, payload.override || false)

// Commit the progress
context.commit(types.UPDATE_ACTIVE_UPLOADS, { name: payload.name, progress: 0 });
const uploadProgress = (progress) => {
context.commit(types.UPDATE_ACTIVE_UPLOADS, { name: payload.name, progress });
};

// Do file upload
api.upload(payload.name, payload.parent, payload.content, payload.override || false, uploadProgress)
.then((file) => {
context.commit(types.UPLOAD_SUCCESS, file);
context.commit(types.SET_IS_LOADING, false);
context.commit(types.UPDATE_ACTIVE_UPLOADS, { name: payload.name, completed: true });
})
.catch((error) => {
context.commit(types.SET_IS_LOADING, false);
context.commit(types.UPDATE_ACTIVE_UPLOADS, { name: payload.name, completed: true });

// Handle file exists
if (error.status === 409) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@ export const UPDATE_SORT_BY = 'UPDATE_SORT_BY';

// Update sorting direction
export const UPDATE_SORT_DIRECTION = 'UPDATE_SORT_DIRECTION';

// Update list of files currently uploading and their progress
export const UPDATE_ACTIVE_UPLOADS = 'UPDATE_ACTIVE_UPLOADS';
Original file line number Diff line number Diff line change
Expand Up @@ -493,4 +493,43 @@ export default {
[types.UPDATE_SORT_DIRECTION]: (state, payload) => {
state.sortDirection = payload === 'asc' ? 'asc' : 'desc';
},

/**
* Update list of active uploads
* @param state
* @param payload As following {name: fileName, progress: 0} or {name: fileName, completed: true}
*/
[types.UPDATE_ACTIVE_UPLOADS]: (state, payload) => {
let isNew = true;
let progress = 0;
let toRemove = -1;

// Collect progress for active uploads
state.activeUploads.forEach((item, idx) => {
if (item.name === payload.name) {
isNew = false;
item.progress = Math.max(item.progress, Math.min(100, payload.progress || 0));

if (payload.completed) {
toRemove = idx;
}
}
// Pick element with the smallest progress
if (item.progress > 0) {
progress = !progress ? item.progress : Math.min(progress, item.progress);
}
});

// Add new item to the list
if (isNew) {
state.activeUploads.push({ name: payload.name, progress: payload.progress });
}

// Remove completed item from the list
if (toRemove !== -1) {
state.activeUploads.splice(toRemove, 1);
}

state.uploadProgress = progress;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,8 @@ export default {
sortBy: storedState && storedState.sortBy ? storedState.sortBy : 'name',
// The sorting direction
sortDirection: storedState && storedState.sortDirection ? storedState.sortDirection : 'asc',
// Active uploads, [{name: fileName, progress: 0}]
activeUploads: [],
// Upload progress, from 0 to 100
uploadProgress: 0,
};
Loading
Loading