cleanup (broken files)
This commit is contained in:
@@ -32,7 +32,6 @@ export const FileUpload = Extension.create({
|
||||
input.onchange = async () => {
|
||||
if (input.files?.length) {
|
||||
const file = input.files[0];
|
||||
const filename = file.name;
|
||||
const filesize = file.size.toString();
|
||||
|
||||
toast.promise(
|
||||
@@ -45,11 +44,11 @@ export const FileUpload = Extension.create({
|
||||
})(),
|
||||
{
|
||||
loading: "Uploading file...",
|
||||
success: (url) => {
|
||||
success: ({ url, filename: pbFilename }) => {
|
||||
// Insert file attchment UI card
|
||||
editor.chain()
|
||||
.focus()
|
||||
.insertFileAttachment({ src: url, filename, filesize })
|
||||
.insertFileAttachment({ src: url, filename: pbFilename, filesize })
|
||||
.run();
|
||||
|
||||
// Manually force a new paragraph at the end so the user can keep typing
|
||||
|
||||
@@ -42,11 +42,11 @@ export const ImageUpload = Extension.create({
|
||||
})(),
|
||||
{
|
||||
loading: "Processing & uploading image...",
|
||||
success: (url) => {
|
||||
success: ({ url, filename }) => {
|
||||
// Insert image
|
||||
editor.chain()
|
||||
.focus()
|
||||
.setImage({ src: url })
|
||||
.setImage({ src: url, filename })
|
||||
.run();
|
||||
|
||||
// Manually force a new paragraph at the end so the user can keep typing
|
||||
|
||||
+24
-20
@@ -7,27 +7,19 @@ export const CustomImage = Image.extend({
|
||||
selectable: true,
|
||||
draggable: true,
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
addAttributes() {
|
||||
return {
|
||||
Backspace: () => {
|
||||
const { selection } = this.editor.state;
|
||||
if (!selection.empty) {
|
||||
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;
|
||||
...this.parent?.(),
|
||||
src: {
|
||||
default: null,
|
||||
},
|
||||
Delete: () => {
|
||||
const { selection } = this.editor.state;
|
||||
if (!selection.empty) {
|
||||
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;
|
||||
filename: {
|
||||
default: null,
|
||||
parseHTML: element => element.getAttribute('data-filename'),
|
||||
renderHTML: attributes => {
|
||||
if (!attributes.filename) return {};
|
||||
return { 'data-filename': attributes.filename };
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -43,7 +35,11 @@ export const CustomImage = Image.extend({
|
||||
const img = document.createElement('img');
|
||||
Object.entries(node.attrs).forEach(([key, value]) => {
|
||||
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');
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ export const VideoUpload = Extension.create({
|
||||
})(),
|
||||
{
|
||||
loading: "Uploading video...",
|
||||
success: (url) => {
|
||||
success: ({ url, filename }) => {
|
||||
// Insert video
|
||||
editor.chain()
|
||||
.focus()
|
||||
.setVideo({ src: url })
|
||||
.setVideo({ src: url, filename })
|
||||
.run();
|
||||
|
||||
// Manually force a new paragraph at the end so the user can keep typing
|
||||
|
||||
+25
-13
@@ -27,6 +27,25 @@ export const Video = Node.create<VideoOptions>({
|
||||
src: {
|
||||
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');
|
||||
Object.entries(node.attrs).forEach(([key, value]) => {
|
||||
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
|
||||
@@ -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' {
|
||||
interface Commands<ReturnType> {
|
||||
video: {
|
||||
setVideo: (options: { src: string }) => ReturnType;
|
||||
setVideo: (options: { src: string, filename?: string }) => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+10
-7
@@ -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 {
|
||||
const formData = new FormData();
|
||||
formData.append('attachments+', file);
|
||||
|
||||
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) {
|
||||
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");
|
||||
@@ -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 {
|
||||
const formData = new FormData();
|
||||
formData.append('attachments+', file);
|
||||
@@ -608,7 +608,10 @@ export const uploadNoteAttachment = async (noteId: string, file: File): Promise<
|
||||
|
||||
if (record.attachments && record.attachments.length > 0) {
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user