Răsfoiți Sursa

Merge pull request #27 from ckeditor/t/2

Other: Aborting upload when image is removed and removing image on upload error. Closes #2.
Piotr Jasiun 8 ani în urmă
părinte
comite
d2bdc52e36

+ 18 - 5
packages/ckeditor5-upload/src/imageuploadengine.js

@@ -33,6 +33,7 @@ export default class ImageUploadEngine extends Plugin {
 		const editor = this.editor;
 		const doc = editor.document;
 		const schema = doc.schema;
+		const fileRepository = editor.plugins.get( FileRepository );
 
 		// Setup schema to allow uploadId for images.
 		schema.allow( { name: 'image', attributes: [ 'uploadId' ], inside: '$root' } );
@@ -52,20 +53,27 @@ export default class ImageUploadEngine extends Plugin {
 			}
 		} );
 
-		// Listen on document changes and start upload process when image with `uploadId` attribute is present.
 		doc.on( 'change', ( evt, type, data, batch ) => {
-			if ( type === 'insert' ) {
+			// Listen on document changes and:
+			// * start upload process when image with `uploadId` attribute is inserted,
+			// * abort upload process when image `uploadId` attribute is removed.
+			if ( type === 'insert' || type === 'reinsert' || type === 'remove' ) {
 				for ( const value of data.range ) {
 					if ( value.type === 'elementStart' && value.item.name === 'image' ) {
 						const imageElement = value.item;
 						const uploadId = imageElement.getAttribute( 'uploadId' );
-						const fileRepository = editor.plugins.get( FileRepository );
 
 						if ( uploadId ) {
 							const loader = fileRepository.loaders.get( uploadId );
 
-							if ( loader && loader.status == 'idle' ) {
-								this.load( loader, batch, imageElement );
+							if ( loader ) {
+								if ( type === 'insert' && loader.status == 'idle' ) {
+									this.load( loader, batch, imageElement );
+								}
+
+								if ( type === 'remove' ) {
+									loader.abort();
+								}
 							}
 						}
 					}
@@ -123,6 +131,11 @@ export default class ImageUploadEngine extends Plugin {
 				}
 
 				clean();
+
+				// Remove image from insertion batch.
+				doc.enqueueChanges( () => {
+					batch.remove( imageElement );
+				} );
 			} );
 
 		function clean() {

+ 6 - 5
packages/ckeditor5-upload/src/imageuploadprogress.js

@@ -59,17 +59,18 @@ export default class ImageUploadProgress extends Plugin {
 	 */
 	uploadStatusChange( evt, data, consumable ) {
 		const editor = this.editor;
-		const fileRepository = editor.plugins.get( FileRepository );
-		const placeholder = this.placeholder;
+		const modelImage = data.item;
+		const uploadId = modelImage.getAttribute( 'uploadId' );
 
-		if ( !consumable.consume( data.item, eventNameToConsumableType( evt.name ) ) ) {
+		if ( !consumable.consume( data.item, eventNameToConsumableType( evt.name ) ) || !uploadId ) {
 			return;
 		}
 
+		const fileRepository = editor.plugins.get( FileRepository );
+		const placeholder = this.placeholder;
 		const status = data.attributeNewValue;
-		const modelImage = data.item;
 		const viewFigure = editor.editing.mapper.toViewElement( modelImage );
-		const uploadId = modelImage.getAttribute( 'uploadId' );
+
 		const loader = fileRepository.loaders.get( uploadId );
 
 		if ( !loader ) {

+ 45 - 0
packages/ckeditor5-upload/tests/imageuploadengine.js

@@ -187,4 +187,49 @@ describe( 'ImageUploadEngine', () => {
 			'[]<figure class="image ck-widget" contenteditable="false"><img src="image.png"></img></figure>'
 		);
 	} );
+
+	it( 'should remove image in case of upload error', ( done ) => {
+		const file = createNativeFileMock();
+		const spy = testUtils.sinon.spy();
+		const notification = editor.plugins.get( Notification );
+		setModelData( document, '<paragraph>{}foo bar</paragraph>' );
+
+		notification.on( 'show:warning', evt => {
+			spy();
+			evt.stop();
+		}, { priority: 'high' } );
+
+		editor.execute( 'imageUpload', { file } );
+
+		document.once( 'changesDone', () => {
+			document.once( 'changesDone', () => {
+				expect( getModelData( document ) ).to.equal( '<paragraph>[]foo bar</paragraph>' );
+				sinon.assert.calledOnce( spy );
+
+				done();
+			} );
+		} );
+
+		nativeReaderMock.mockError( 'Upload error.' );
+	} );
+
+	it( 'should abort upload if image is removed', () => {
+		const file = createNativeFileMock();
+		setModelData( document, '<paragraph>{}foo bar</paragraph>' );
+		editor.execute( 'imageUpload', { file } );
+		const abortSpy = testUtils.sinon.spy( loader, 'abort' );
+
+		expect( loader.status ).to.equal( 'reading' );
+		nativeReaderMock.mockSuccess( base64Sample );
+
+		const image = document.getRoot().getChild( 0 );
+		document.enqueueChanges( () => {
+			const batch = document.batch();
+
+			batch.remove( image );
+		} );
+
+		expect( loader.status ).to.equal( 'aborted' );
+		sinon.assert.calledOnce( abortSpy );
+	} );
 } );

+ 27 - 5
packages/ckeditor5-upload/tests/manual/imageupload.js

@@ -52,26 +52,48 @@ function createProgressButton( loader, adapterMock ) {
 	const container = document.createElement( 'div' );
 	const progressInfo = document.createElement( 'span' );
 	progressInfo.innerHTML = `File: ${ fileName }. Progress: 0%.`;
-	const button = document.createElement( 'button' );
-	button.innerHTML = 'Upload progress';
+	const progressButton = document.createElement( 'button' );
+	const errorButton = document.createElement( 'button' );
+	const abortButton = document.createElement( 'button' );
+	progressButton.innerHTML = 'Upload progress';
+	errorButton.innerHTML = 'Simulate error';
+	abortButton.innerHTML = 'Simulate aborting';
 
-	container.appendChild( button );
+	container.appendChild( progressButton );
+	container.appendChild( errorButton );
+	container.appendChild( abortButton );
 	container.appendChild( progressInfo );
 
 	buttonContainer.appendChild( container );
 
 	let progress = 0;
 	const total = 500;
-	button.addEventListener( 'click', () => {
+	progressButton.addEventListener( 'click', () => {
 		progress += 100;
 		adapterMock.mockProgress( progress, total );
 
 		if ( progress == total ) {
-			button.setAttribute( 'disabled', 'true' );
+			disableButtons();
 			adapterMock.mockSuccess( { original: './sample.jpg' } );
 		}
 
 		progressInfo.innerHTML = `File: ${ fileName }. Progress: ${ loader.uploadedPercent }%.`;
 	} );
+
+	errorButton.addEventListener( 'click', () => {
+		adapterMock.mockError( 'Upload error!' );
+		disableButtons();
+	} );
+
+	abortButton.addEventListener( 'click', () => {
+		loader.abort();
+		disableButtons();
+	} );
+
+	function disableButtons() {
+		progressButton.setAttribute( 'disabled', 'true' );
+		errorButton.setAttribute( 'disabled', 'true' );
+		abortButton.setAttribute( 'disabled', 'true' );
+	}
 }
 

+ 3 - 1
packages/ckeditor5-upload/tests/manual/imageupload.md

@@ -7,4 +7,6 @@
 
 Repeat all the steps with:
 * dropping multiple images,
-* using toolbar button to add one and multiple images.
+* using toolbar button to add one and multiple images,
+* using `Simulate error` button to stop upload, show error and remove image,
+* using `Simulate aborting` button to stop upload and remove image.