fix(crop): persist on unmount + handle image-load failure
Two follow-up fixes flagged by the post-merge review: - persist() on `onUnmounted` so a mid-drag navigation (Back button, hot-reload) doesn't lose the in-progress crop. The local refs already hold the latest coords; we just need to flush them through to the store + cache before the component goes away. - Add `el.onerror` to the image load and a small fallback UI offering Back-to-Deskew. Without it, a corrupt deskew blob would leave the canvas permanently blank with no way to recover short of refresh.
This commit is contained in:
parent
565baddfbf
commit
9f54bc62bd
@ -32,6 +32,11 @@ const overlayRef = ref<HTMLCanvasElement | null>(null)
|
|||||||
|
|
||||||
const img = ref<HTMLImageElement | null>(null)
|
const img = ref<HTMLImageElement | null>(null)
|
||||||
const imgUrl = ref<string | null>(null)
|
const imgUrl = ref<string | null>(null)
|
||||||
|
// Set when the deskew blob fails to decode — corrupt result, OOM in the
|
||||||
|
// decoder, or unsupported format. We surface a small fallback UI so the
|
||||||
|
// user can navigate back to step 4 and retry instead of staring at a
|
||||||
|
// permanently blank canvas.
|
||||||
|
const loadError = ref(false)
|
||||||
|
|
||||||
// Rotation in degrees. Crop fractions of the rotated bbox.
|
// Rotation in degrees. Crop fractions of the rotated bbox.
|
||||||
const rotationDeg = ref(0)
|
const rotationDeg = ref(0)
|
||||||
@ -73,10 +78,14 @@ function persist() {
|
|||||||
function loadImage(url: string) {
|
function loadImage(url: string) {
|
||||||
const el = new Image()
|
const el = new Image()
|
||||||
el.onload = () => {
|
el.onload = () => {
|
||||||
|
loadError.value = false
|
||||||
img.value = el
|
img.value = el
|
||||||
fitToContainer()
|
fitToContainer()
|
||||||
redraw()
|
redraw()
|
||||||
}
|
}
|
||||||
|
el.onerror = () => {
|
||||||
|
loadError.value = true
|
||||||
|
}
|
||||||
el.src = url
|
el.src = url
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +401,11 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
// If the user navigated away mid-drag (e.g. tapped Back during a corner
|
||||||
|
// pull), the local refs hold the latest crop coords but `persist()` only
|
||||||
|
// fires on `pointerup`. Flush here so cache + store match what was on
|
||||||
|
// screen.
|
||||||
|
persist()
|
||||||
resizeObs?.disconnect()
|
resizeObs?.disconnect()
|
||||||
if (imgUrl.value) URL.revokeObjectURL(imgUrl.value)
|
if (imgUrl.value) URL.revokeObjectURL(imgUrl.value)
|
||||||
})
|
})
|
||||||
@ -500,6 +514,24 @@ function next() {
|
|||||||
@pointerup="onPointerUp"
|
@pointerup="onPointerUp"
|
||||||
@pointercancel="onPointerUp"
|
@pointercancel="onPointerUp"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-if="loadError"
|
||||||
|
class="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-background/90 p-6 text-center"
|
||||||
|
>
|
||||||
|
<p class="text-sm font-medium text-destructive">
|
||||||
|
Failed to load the deskew result.
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
The image blob couldn't be decoded. Re-run the
|
||||||
|
perspective correction in the previous step.
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
@click="store.goToStep(4)"
|
||||||
|
>Back to Deskew</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user