Repository navigation
fix!: reset cropped image when the image source changes - #39
Conversation
|
No actionable comments were generated in the recent review. ℹ️ Recent review info
WalkthroughThe change updates cropped-image state for string image sources. It also updates tests and examples to handle a null cropped-image data URI. ChangesCropped-image state lifecycle
Priority: Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Low Merge Risk:
|
javier-godoy
left a comment
There was a problem hiding this comment.
Thanks for the fix! Requesting changes for the stale-event handling and a few demo/README issues (inline).
Tie each cropped image to the image it came from. A cropped-image event doesn't say which image it was produced from, so resetting the field in the setters isn't enough:
- A late event undoes the reset. If the user crops image A and, before that event is processed, server code calls
setImageSrc("b.png")(e.g. another listener in the same request, or aui.accesspush), the field is cleared and then the late event sets it back to A's crop. While B is shown,getCroppedImageDataUri()returns a crop of A, and if B has no crop defined it stays that way. (The same race already exists insetImageSrc(DownloadHandler).) - A → B → A in one round trip loses the crop.
sameImageonly compares against the server state, sosetImageSrc("b.png"); setImageSrc("a.png");clears the field, but the client never sees a change, never reloads and never re-crops.getCroppedImageDataUri()returnsnullalthough A still has a crop, contradicting the new Javadoc. Onmasterthis case works.
Suggested approach: include the source of the cropped image in the event detail, store the crop together with that source, and have getCroppedImageDataUri()/getCroppedImageBase64() return it only when it matches getImageSrc() (or ignore non-matching events in updateCroppedImage). That fixes both cases and removes the need for the sameImage bookkeeping.
Please record that source in onLoad (the image whose pixels are actually on the canvas) rather than reading image.src when encoding: that way the same change also fixes #41, where image.src already points to the new image while the old one is still being encoded. Also keep in mind that the DownloadHandler resource URL is regenerated in onAttach, so the stored source should follow it there (or use a server-controlled id instead of the URL).
|
@javier-godoy Done in 96f5051. Each |
javier-godoy
left a comment
There was a problem hiding this comment.
The main concern is the first three inline comments: the image source is sent back with every crop event, and checking it on the server is expensive for data-URI sources. The revision counter suggested on onAttach would address both.
One more comment, on a line outside the diff:
The null return isn't new: the isBlank check is already on master. What this PR changes is how often it happens. Before, the String constructor stored the image URL as the "cropped image", so this method had something to decode (and failed on a URL, which this PR fixes). Now it returns null until the user crops, or until a configured crop is applied when the image loads.
A caller who reads the current @return ("the decoded byte array of the cropped image") will assume it never returns null, and calls like getCroppedImageBase64().length will throw NullPointerException. Suggested text: "@return the decoded bytes of the cropped image, or null if the image has not been cropped yet".
| croppedImageDataUri: croppedImageDataUri | ||
| croppedImageDataUri: croppedImageDataUri, | ||
| // lets the server discard a crop of an image that is no longer the source | ||
| imageSrc: this.loadedImgSrc |
There was a problem hiding this comment.
Everything in the event's detail is serialized to JSON and sent from the browser to the server. Adding imageSrc sends the image source along with every cropped-image event.
With a normal URL (images/photo.png) that's a few bytes. A data URI is different: it contains the entire image file encoded as base64, which makes it about 33% bigger than the file. UploadImageCropDemo builds exactly that from an upload of up to 10 MB, so imageSrc can be about 13 MB. The event fires on every onComplete, which happens each time the user releases the mouse after moving or resizing the selection. Every one of those uploads the full original image back to the server, which already has it.
It also costs memory on the server. Vaadin turns the JSON into a new String, and that is stored here:
The component state already holds one copy, so each session now holds two copies of the source.
To see it: run the upload demo, upload a large photo, open the browser DevTools Network tab, and drag the selection a few times. Each request carries the whole image.
The server doesn't need the image back, only something that says which image was cropped. The comment on onAttach suggests a way to do that.
There was a problem hiding this comment.
Done in 3199648: the event now carries the image revision recorded in onLoad instead of the source.
a8bddb9 to
da62b90
Compare
|
@javier-godoy Updated the |
javier-godoy
left a comment
There was a problem hiding this comment.
LGTM, please squash the WIP commits, and update the commit message so that the BREAKING CHANGE footer also covers the removal of addCroppedImageListener and the new CroppedImageEvent constructor, and add Close #41.
Also, please add "This event is for internal use only." to the CroppedImageEvent class Javadoc.
3199648 to
7e31a1c
Compare
|



Changing the image source now discards the cropped image of the previous one. Each cropped image is tied to the image it was cropped from through a revision counter: the server increments it when the image changes, the client records it when the image loads and sends it back with every crop, and getCroppedImageDataUri() only returns a crop whose revision matches. This also ignores late crops of a previous image, and detects the output format from the image actually drawn instead of image.src.
Breaking changes:
The demos show "Select an area first" when there is no cropped image yet.
Close #38
Close #41