cleanup (broken files)

This commit is contained in:
2026-03-12 17:23:28 -05:00
parent f53030294b
commit df94685c96
6 changed files with 65 additions and 47 deletions
+2 -3
View File
@@ -32,7 +32,6 @@ export const FileUpload = Extension.create({
input.onchange = async () => { input.onchange = async () => {
if (input.files?.length) { if (input.files?.length) {
const file = input.files[0]; const file = input.files[0];
const filename = file.name;
const filesize = file.size.toString(); const filesize = file.size.toString();
toast.promise( toast.promise(
@@ -45,11 +44,11 @@ export const FileUpload = Extension.create({
})(), })(),
{ {
loading: "Uploading file...", loading: "Uploading file...",
success: (url) => { success: ({ url, filename: pbFilename }) => {
// Insert file attchment UI card // Insert file attchment UI card
editor.chain() editor.chain()
.focus() .focus()
.insertFileAttachment({ src: url, filename, filesize }) .insertFileAttachment({ src: url, filename: pbFilename, filesize })
.run(); .run();
// Manually force a new paragraph at the end so the user can keep typing // Manually force a new paragraph at the end so the user can keep typing
+2 -2
View File
@@ -42,11 +42,11 @@ export const ImageUpload = Extension.create({
})(), })(),
{ {
loading: "Processing & uploading image...", loading: "Processing & uploading image...",
success: (url) => { success: ({ url, filename }) => {
// Insert image // Insert image
editor.chain() editor.chain()
.focus() .focus()
.setImage({ src: url }) .setImage({ src: url, filename })
.run(); .run();
// Manually force a new paragraph at the end so the user can keep typing // Manually force a new paragraph at the end so the user can keep typing
+24 -20
View File
@@ -7,27 +7,19 @@ export const CustomImage = Image.extend({
selectable: true, selectable: true,
draggable: true, draggable: true,
addKeyboardShortcuts() { addAttributes() {
return { return {
Backspace: () => { ...this.parent?.(),
const { selection } = this.editor.state; src: {
if (!selection.empty) { default: null,
const selectedNode = this.editor.state.doc.nodeAt(selection.from);
if (selectedNode?.type.name === this.name) {
return true; // Prevent default deletion if image is selected
}
}
return false;
}, },
Delete: () => { filename: {
const { selection } = this.editor.state; default: null,
if (!selection.empty) { parseHTML: element => element.getAttribute('data-filename'),
const selectedNode = this.editor.state.doc.nodeAt(selection.from); renderHTML: attributes => {
if (selectedNode?.type.name === this.name) { if (!attributes.filename) return {};
return true; // Prevent default deletion if image is selected return { 'data-filename': attributes.filename };
} },
}
return false;
}, },
}; };
}, },
@@ -43,7 +35,11 @@ export const CustomImage = Image.extend({
const img = document.createElement('img'); const img = document.createElement('img');
Object.entries(node.attrs).forEach(([key, value]) => { Object.entries(node.attrs).forEach(([key, value]) => {
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
img.setAttribute(key, value); if (key === 'filename') {
img.setAttribute('data-filename', value);
} else {
img.setAttribute(key, value);
}
} }
}); });
img.classList.add('rounded-lg', 'border', 'border-border', 'max-w-full', 'h-auto', 'cursor-default'); img.classList.add('rounded-lg', 'border', 'border-border', 'max-w-full', 'h-auto', 'cursor-default');
@@ -79,3 +75,11 @@ export const CustomImage = Image.extend({
}; };
}, },
}); });
declare module '@tiptap/core' {
interface Commands<ReturnType> {
image: {
setImage: (options: { src: string, alt?: string, title?: string, filename?: string }) => ReturnType;
};
}
}
+2 -2
View File
@@ -41,11 +41,11 @@ export const VideoUpload = Extension.create({
})(), })(),
{ {
loading: "Uploading video...", loading: "Uploading video...",
success: (url) => { success: ({ url, filename }) => {
// Insert video // Insert video
editor.chain() editor.chain()
.focus() .focus()
.setVideo({ src: url }) .setVideo({ src: url, filename })
.run(); .run();
// Manually force a new paragraph at the end so the user can keep typing // Manually force a new paragraph at the end so the user can keep typing
+25 -13
View File
@@ -27,6 +27,25 @@ export const Video = Node.create<VideoOptions>({
src: { src: {
default: null, default: null,
}, },
filename: {
default: null,
parseHTML: element => element.getAttribute('data-filename'),
renderHTML: attributes => {
if (!attributes.filename) return {};
return { 'data-filename': attributes.filename };
},
},
};
},
addCommands() {
return {
setVideo: (options: { src: string, filename?: string }) => ({ commands }) => {
return commands.insertContent({
type: this.name,
attrs: options,
});
},
}; };
}, },
@@ -76,7 +95,11 @@ export const Video = Node.create<VideoOptions>({
const video = document.createElement('video'); const video = document.createElement('video');
Object.entries(node.attrs).forEach(([key, value]) => { Object.entries(node.attrs).forEach(([key, value]) => {
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
video.setAttribute(key, value); if (key === 'filename') {
video.setAttribute('data-filename', value);
} else {
video.setAttribute(key, value);
}
} }
}); });
// Apply default attributes if not present // Apply default attributes if not present
@@ -115,23 +138,12 @@ export const Video = Node.create<VideoOptions>({
}; };
}; };
}, },
addCommands() {
return {
setVideo: (options: { src: string }) => ({ commands }) => {
return commands.insertContent({
type: this.name,
attrs: options,
});
},
};
},
}); });
declare module '@tiptap/core' { declare module '@tiptap/core' {
interface Commands<ReturnType> { interface Commands<ReturnType> {
video: { video: {
setVideo: (options: { src: string }) => ReturnType; setVideo: (options: { src: string, filename?: string }) => ReturnType;
}; };
} }
} }
+10 -7
View File
@@ -576,19 +576,19 @@ const mapRecordToTask = (r: any): Task => {
}; };
}; };
export const uploadTaskAttachment = async (taskId: string, file: File): Promise<string> => { export const uploadTaskAttachment = async (taskId: string, file: File): Promise<{ url: string, filename: string }> => {
try { try {
const formData = new FormData(); const formData = new FormData();
formData.append('attachments+', file); formData.append('attachments+', file);
const record = await pb.collection(TASGRID_COLLECTION).update(taskId, formData); const record = await pb.collection(TASGRID_COLLECTION).update(taskId, formData);
// Return the URL for the newly uploaded file. PocketBase appends new files to the array.
// We need to find the specific filename that was just uploaded to get its URL.
// Since we don't know the exact generated filename, we can return the URL of the last file in the array.
if (record.attachments && record.attachments.length > 0) { if (record.attachments && record.attachments.length > 0) {
const latestFilename = record.attachments[record.attachments.length - 1]; const latestFilename = record.attachments[record.attachments.length - 1];
return pb.files.getURL(record, latestFilename); return {
url: pb.files.getURL(record, latestFilename),
filename: latestFilename
};
} }
throw new Error("File uploaded but no attachment returned"); throw new Error("File uploaded but no attachment returned");
@@ -599,7 +599,7 @@ export const uploadTaskAttachment = async (taskId: string, file: File): Promise<
} }
}; };
export const uploadNoteAttachment = async (noteId: string, file: File): Promise<string> => { export const uploadNoteAttachment = async (noteId: string, file: File): Promise<{ url: string, filename: string }> => {
try { try {
const formData = new FormData(); const formData = new FormData();
formData.append('attachments+', file); formData.append('attachments+', file);
@@ -608,7 +608,10 @@ export const uploadNoteAttachment = async (noteId: string, file: File): Promise<
if (record.attachments && record.attachments.length > 0) { if (record.attachments && record.attachments.length > 0) {
const latestFilename = record.attachments[record.attachments.length - 1]; const latestFilename = record.attachments[record.attachments.length - 1];
return pb.files.getURL(record, latestFilename); return {
url: pb.files.getURL(record, latestFilename),
filename: latestFilename
};
} }
throw new Error("File uploaded but no attachment returned"); throw new Error("File uploaded but no attachment returned");