@wordpress/upload-media
Version:
Core media upload logic.
352 lines (332 loc) • 8.22 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addItem = addItem;
exports.finishOperation = finishOperation;
exports.pauseQueue = pauseQueue;
exports.prepareItem = prepareItem;
exports.processItem = processItem;
exports.removeItem = removeItem;
exports.resumeQueue = resumeQueue;
exports.revokeBlobUrls = revokeBlobUrls;
exports.updateSettings = updateSettings;
exports.uploadItem = uploadItem;
var _uuid = require("uuid");
var _blob = require("@wordpress/blob");
var _utils = require("../utils");
var _stubFile = require("../stub-file");
var _types = require("./types");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/**
* Adds a new item to the upload queue.
*
* @param $0
* @param $0.file File
* @param [$0.batchId] Batch ID.
* @param [$0.onChange] Function called each time a file or a temporary representation of the file is available.
* @param [$0.onSuccess] Function called after the file is uploaded.
* @param [$0.onBatchSuccess] Function called after a batch of files is uploaded.
* @param [$0.onError] Function called when an error happens.
* @param [$0.additionalData] Additional data to include in the request.
* @param [$0.sourceUrl] Source URL. Used when importing a file from a URL or optimizing an existing file.
* @param [$0.sourceAttachmentId] Source attachment ID. Used when optimizing an existing file for example.
* @param [$0.abortController] Abort controller for upload cancellation.
* @param [$0.operations] List of operations to perform. Defaults to automatically determined list, based on the file.
*/
function addItem({
file: fileOrBlob,
batchId,
onChange,
onSuccess,
onBatchSuccess,
onError,
additionalData = {},
sourceUrl,
sourceAttachmentId,
abortController,
operations
}) {
return async ({
dispatch
}) => {
const itemId = (0, _uuid.v4)();
// Hardening in case a Blob is passed instead of a File.
// See https://github.com/WordPress/gutenberg/pull/65693 for an example.
const file = (0, _utils.convertBlobToFile)(fileOrBlob);
let blobUrl;
// StubFile could be coming from addItemFromUrl().
if (!(file instanceof _stubFile.StubFile)) {
blobUrl = (0, _blob.createBlobURL)(file);
dispatch({
type: _types.Type.CacheBlobUrl,
id: itemId,
blobUrl
});
}
dispatch({
type: _types.Type.Add,
item: {
id: itemId,
batchId,
status: _types.ItemStatus.Processing,
sourceFile: (0, _utils.cloneFile)(file),
file,
attachment: {
url: blobUrl
},
additionalData: {
convert_format: false,
...additionalData
},
onChange,
onSuccess,
onBatchSuccess,
onError,
sourceUrl,
sourceAttachmentId,
abortController: abortController || new AbortController(),
operations: Array.isArray(operations) ? operations : [_types.OperationType.Prepare]
}
});
dispatch.processItem(itemId);
};
}
/**
* Processes a single item in the queue.
*
* Runs the next operation in line and invokes any callbacks.
*
* @param id Item ID.
*/
function processItem(id) {
return async ({
select,
dispatch
}) => {
if (select.isPaused()) {
return;
}
const item = select.getItem(id);
const {
attachment,
onChange,
onSuccess,
onBatchSuccess,
batchId
} = item;
const operation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0];
if (attachment) {
onChange?.([attachment]);
}
/*
If there are no more operations, the item can be removed from the queue,
but only if there are no thumbnails still being side-loaded,
or if itself is a side-loaded item.
*/
if (!operation) {
if (attachment) {
onSuccess?.([attachment]);
}
// dispatch.removeItem( id );
dispatch.revokeBlobUrls(id);
if (batchId && select.isBatchUploaded(batchId)) {
onBatchSuccess?.();
}
/*
At this point we are dealing with a parent whose children haven't fully uploaded yet.
Do nothing and let the removal happen once the last side-loaded item finishes.
*/
return;
}
if (!operation) {
// This shouldn't really happen.
return;
}
dispatch({
type: _types.Type.OperationStart,
id,
operation
});
switch (operation) {
case _types.OperationType.Prepare:
dispatch.prepareItem(item.id);
break;
case _types.OperationType.Upload:
dispatch.uploadItem(id);
break;
}
};
}
/**
* Returns an action object that pauses all processing in the queue.
*
* Useful for testing purposes.
*
* @return Action object.
*/
function pauseQueue() {
return {
type: _types.Type.PauseQueue
};
}
/**
* Resumes all processing in the queue.
*
* Dispatches an action object for resuming the queue itself,
* and triggers processing for each remaining item in the queue individually.
*/
function resumeQueue() {
return async ({
select,
dispatch
}) => {
dispatch({
type: _types.Type.ResumeQueue
});
for (const item of select.getAllItems()) {
dispatch.processItem(item.id);
}
};
}
/**
* Removes a specific item from the queue.
*
* @param id Item ID.
*/
function removeItem(id) {
return async ({
select,
dispatch
}) => {
const item = select.getItem(id);
if (!item) {
return;
}
dispatch({
type: _types.Type.Remove,
id
});
};
}
/**
* Finishes an operation for a given item ID and immediately triggers processing the next one.
*
* @param id Item ID.
* @param updates Updated item data.
*/
function finishOperation(id, updates) {
return async ({
dispatch
}) => {
dispatch({
type: _types.Type.OperationFinish,
id,
item: updates
});
dispatch.processItem(id);
};
}
/**
* Prepares an item for initial processing.
*
* Determines the list of operations to perform for a given image,
* depending on its media type.
*
* For example, HEIF images first need to be converted, resized,
* compressed, and then uploaded.
*
* Or videos need to be compressed, and then need poster generation
* before upload.
*
* @param id Item ID.
*/
function prepareItem(id) {
return async ({
dispatch
}) => {
const operations = [_types.OperationType.Upload];
dispatch({
type: _types.Type.AddOperations,
id,
operations
});
dispatch.finishOperation(id, {});
};
}
/**
* Uploads an item to the server.
*
* @param id Item ID.
*/
function uploadItem(id) {
return async ({
select,
dispatch
}) => {
const item = select.getItem(id);
select.getSettings().mediaUpload({
filesList: [item.file],
additionalData: item.additionalData,
signal: item.abortController?.signal,
onFileChange: ([attachment]) => {
if (!(0, _blob.isBlobURL)(attachment.url)) {
dispatch.finishOperation(id, {
attachment
});
}
},
onSuccess: ([attachment]) => {
dispatch.finishOperation(id, {
attachment
});
},
onError: error => {
dispatch.cancelItem(id, error);
}
});
};
}
/**
* Revokes all blob URLs for a given item, freeing up memory.
*
* @param id Item ID.
*/
function revokeBlobUrls(id) {
return async ({
select,
dispatch
}) => {
const blobUrls = select.getBlobUrls(id);
for (const blobUrl of blobUrls) {
(0, _blob.revokeBlobURL)(blobUrl);
}
dispatch({
type: _types.Type.RevokeBlobUrls,
id
});
};
}
/**
* Returns an action object that pauses all processing in the queue.
*
* Useful for testing purposes.
*
* @param settings
* @return Action object.
*/
function updateSettings(settings) {
return {
type: _types.Type.UpdateSettings,
settings
};
}
//# sourceMappingURL=private-actions.js.map