fix(tiptap): clean up pending upload on NodeView.destroy to prevent blob leak
This commit is contained in:
parent
2e239908a4
commit
6b44a9c036
@ -2,10 +2,12 @@ import { Image } from '@tiptap/extension-image'
|
|||||||
import type { Node as PMNode } from '@tiptap/pm/model'
|
import type { Node as PMNode } from '@tiptap/pm/model'
|
||||||
import type { UploadCoordinator } from './upload-coordinator'
|
import type { UploadCoordinator } from './upload-coordinator'
|
||||||
|
|
||||||
/** NodeView 按钮点击回调注入接口。 */
|
/** NodeView 按钮点击 / destroy 回调注入接口。 */
|
||||||
export interface UploadNodeViewCallbacks {
|
export interface UploadNodeViewCallbacks {
|
||||||
onRetry: (uploadId: string) => void
|
onRetry: (uploadId: string) => void
|
||||||
onRemove: (uploadId: string) => void
|
onRemove: (uploadId: string) => void
|
||||||
|
/** NodeView.destroy 兜底:节点被 PM 删除时清理 pending + revoke blob。 */
|
||||||
|
onDestroyed: (uploadId: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,6 +24,7 @@ export interface UploadNodeViewCallbacks {
|
|||||||
class UploadImageNodeView {
|
class UploadImageNodeView {
|
||||||
private node: PMNode
|
private node: PMNode
|
||||||
private callbacks: UploadNodeViewCallbacks
|
private callbacks: UploadNodeViewCallbacks
|
||||||
|
private uploadId: string | null
|
||||||
|
|
||||||
private container: HTMLDivElement
|
private container: HTMLDivElement
|
||||||
private img: HTMLImageElement
|
private img: HTMLImageElement
|
||||||
@ -34,6 +37,7 @@ class UploadImageNodeView {
|
|||||||
}) {
|
}) {
|
||||||
this.node = opts.node
|
this.node = opts.node
|
||||||
this.callbacks = opts.callbacks
|
this.callbacks = opts.callbacks
|
||||||
|
this.uploadId = (this.node.attrs['data-upload-id'] as string | null) ?? null
|
||||||
|
|
||||||
this.container = document.createElement('div')
|
this.container = document.createElement('div')
|
||||||
this.container.classList.add('upload-image-container')
|
this.container.classList.add('upload-image-container')
|
||||||
@ -66,6 +70,7 @@ class UploadImageNodeView {
|
|||||||
const oldSrc = this.node.attrs.src
|
const oldSrc = this.node.attrs.src
|
||||||
const newSrc = node.attrs.src
|
const newSrc = node.attrs.src
|
||||||
this.node = node
|
this.node = node
|
||||||
|
this.uploadId = (node.attrs['data-upload-id'] as string | null) ?? null
|
||||||
if (oldSrc !== newSrc && newSrc != null) {
|
if (oldSrc !== newSrc && newSrc != null) {
|
||||||
this.img.src = newSrc
|
this.img.src = newSrc
|
||||||
}
|
}
|
||||||
@ -130,6 +135,11 @@ class UploadImageNodeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
// 节点被 ProseMirror 删除(退格/剪切等):兜底清理 pending + revoke blob,
|
||||||
|
// 避免 upload-coordinator 的 pending Map 和 blob URL 泄漏(尤其 error 态节点)。
|
||||||
|
if (this.uploadId) {
|
||||||
|
this.callbacks.onDestroyed(this.uploadId)
|
||||||
|
}
|
||||||
this.overlay?.remove()
|
this.overlay?.remove()
|
||||||
this.overlay = null
|
this.overlay = null
|
||||||
this.container.remove()
|
this.container.remove()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user