fix(upload): address code review findings
final code review 后的修复:
1. blob 检查收紧(Important): md.contains("blob:") 误伤合法讨论 blob URL
的代码块/正文,改为检测 markdown 图片语法 ](blob: 才算占位符泄漏
2. 重试错误原地更新(Minor): seen_error_ids 去重逻辑导致重试后再失败时
顶部提示停留在旧错误文案;改为已存在 id 时原地更新 message
3. destroy 清理 coordinatorRef(Minor): destroy() 调 setUploadCoordinator(null),
避免 NodeView 指向已销毁的 coordinator(防御性)
4. 删除 NodeView 未用的 editor 字段(Nit): 死代码清理
验证: cargo test (384 passed) / clippy (本次零警告) / dx check / tiptap build 全过
This commit is contained in:
parent
a10f79d47e
commit
cee4f3f3ef
@ -235,6 +235,8 @@ class TiptapEditorInstance {
|
||||
this.editor?.destroy()
|
||||
this.editor = null
|
||||
this.coordinator = null
|
||||
// 清除 NodeView 的 coordinator 引用,避免指向已销毁的实例
|
||||
setUploadCoordinator(null)
|
||||
// 清理源码模式相关引用(容器 innerHTML 已清空,DOM 会随之移除)
|
||||
this.sourceTextarea = null
|
||||
this.toggleButton = null
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import { mergeAttributes, type Editor } from '@tiptap/core'
|
||||
import { mergeAttributes } from '@tiptap/core'
|
||||
import type { Node as PMNode } from '@tiptap/pm/model'
|
||||
|
||||
/** NodeView 按钮点击回调注入接口。 */
|
||||
@ -20,7 +20,6 @@ export interface UploadNodeViewCallbacks {
|
||||
* 属性变化时 ProseMirror 调 update(node),NodeView 比较新旧 data-upload-state 重渲染遮罩。
|
||||
*/
|
||||
class UploadImageNodeView {
|
||||
private editor: Editor
|
||||
private node: PMNode
|
||||
private callbacks: UploadNodeViewCallbacks
|
||||
|
||||
@ -30,12 +29,10 @@ class UploadImageNodeView {
|
||||
|
||||
constructor(opts: {
|
||||
node: PMNode
|
||||
editor: Editor
|
||||
HTMLAttributes: Record<string, unknown>
|
||||
callbacks: UploadNodeViewCallbacks
|
||||
}) {
|
||||
this.node = opts.node
|
||||
this.editor = opts.editor
|
||||
this.callbacks = opts.callbacks
|
||||
|
||||
this.container = document.createElement('div')
|
||||
@ -183,10 +180,9 @@ export const UploadImage = Image.configure({ allowBase64: true }).extend({
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ({ node, HTMLAttributes, editor }) => {
|
||||
return ({ node, HTMLAttributes }) => {
|
||||
return new UploadImageNodeView({
|
||||
node,
|
||||
editor,
|
||||
HTMLAttributes,
|
||||
callbacks: {
|
||||
onRetry: (id) => coordinatorRef?.retryUpload(id),
|
||||
|
||||
@ -332,14 +332,21 @@ fn write_editor(post_id: Option<i32>) -> Element {
|
||||
for ev in parsed.events {
|
||||
match ev.kind.as_str() {
|
||||
"error" => {
|
||||
if !seen_error_ids.contains(&ev.upload_id) {
|
||||
seen_error_ids.insert(ev.upload_id.clone());
|
||||
let msg = ev.error_msg.unwrap_or_else(|| "上传失败".to_string());
|
||||
if seen_error_ids.insert(ev.upload_id.clone()) {
|
||||
// 新失败:追加提示
|
||||
upload_errors.write().push(UploadErrorEntry {
|
||||
id: ev.upload_id,
|
||||
file_name: ev.file_name,
|
||||
message: ev.error_msg
|
||||
.unwrap_or_else(|| "上传失败".to_string()),
|
||||
message: msg,
|
||||
});
|
||||
} else {
|
||||
// 已存在的 id(重试后再失败):原地更新消息,
|
||||
// 避免顶部提示停留在旧错误文案。
|
||||
let mut errors = upload_errors.write();
|
||||
if let Some(entry) = errors.iter_mut().find(|e| e.id == ev.upload_id) {
|
||||
entry.message = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
"success" | "removed" => {
|
||||
@ -392,8 +399,10 @@ fn write_editor(post_id: Option<i32>) -> Element {
|
||||
})()
|
||||
"#).ok().and_then(|v| v.as_string()).unwrap_or_default();
|
||||
|
||||
// 兜底:扫描残留的 blob: 或 data-upload-state(轮询窗口期漏判防护)
|
||||
if md.contains("blob:") || md.contains("data-upload-state") {
|
||||
// 兜底:扫描残留的上传占位符标记(轮询窗口期漏判防护)
|
||||
// 检测  形式的泄漏图片 src,而非裸 "blob:" 字符串,
|
||||
// 避免误伤合法讨论 blob URL 的代码块/正文。
|
||||
if md.contains("](blob:") || md.contains("data-upload-state") {
|
||||
error.set(Some("检测到未完成上传的图片,请处理后保存".to_string()));
|
||||
return;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user