cleanup (broken files)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user