mirror of
https://github.com/docmost/docmost.git
synced 2026-05-17 23:14:07 +08:00
7981ef462e
* use local resizable * feat: aduio * support audio imports * feat: use confluence real file names * cleanup * error handling * hide notice * add audio * fix pulse * Fix import and export * unify pulse * hide in readonly mode * keywords * keyword * translations * better sort * feat: PDF embed * cleanup * remove audio menu * open active * hide focus on readonly mode * increase iframe default dimension
140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
import { MediaUploadOptions, UploadFn } from "../media-utils";
|
|
import { IAttachment } from "../types";
|
|
import { generateNodeId } from "../utils";
|
|
import { Node } from "@tiptap/pm/model";
|
|
import { Command } from "@tiptap/core";
|
|
|
|
const findAudioNodeByPlaceholderId = (
|
|
doc: Node,
|
|
placeholderId: string,
|
|
): { node: Node; pos: number } | null => {
|
|
let result: { node: Node; pos: number } | null = null;
|
|
|
|
doc.descendants((node, pos) => {
|
|
if (result) return false;
|
|
|
|
if (
|
|
node.type.name === "audio" &&
|
|
node.attrs.placeholder?.id === placeholderId
|
|
) {
|
|
result = { node, pos };
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return result;
|
|
};
|
|
|
|
const handleAudioUpload =
|
|
({ validateFn, onUpload }: MediaUploadOptions): UploadFn =>
|
|
async (file, editor, pos, pageId) => {
|
|
const validated = validateFn?.(file);
|
|
// @ts-ignore
|
|
if (!validated) return;
|
|
|
|
const objectUrl = URL.createObjectURL(file);
|
|
const placeholderId = generateNodeId();
|
|
|
|
let placeholderInserted = false;
|
|
|
|
editor.storage.shared.audioPreviews =
|
|
editor.storage.shared.audioPreviews || {};
|
|
editor.storage.shared.audioPreviews[placeholderId] = objectUrl;
|
|
|
|
const insertPlaceholder = (): Command => {
|
|
return ({ tr, state }) => {
|
|
const initialPlaceholderNode = state.schema.nodes.audio?.create({
|
|
placeholder: {
|
|
id: placeholderId,
|
|
name: file.name,
|
|
},
|
|
});
|
|
|
|
if (!initialPlaceholderNode) return false;
|
|
|
|
const { parent } = tr.doc.resolve(pos);
|
|
const isEmptyTextBlock = parent.isTextblock && !parent.childCount;
|
|
|
|
if (isEmptyTextBlock) {
|
|
tr.replaceRangeWith(pos - 1, pos + 1, initialPlaceholderNode);
|
|
} else {
|
|
tr.insert(pos, initialPlaceholderNode);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
const replacePlaceholderWithAudio = (attachment: IAttachment): Command => {
|
|
return ({ tr }) => {
|
|
const { pos: currentPos = null } =
|
|
findAudioNodeByPlaceholderId(tr.doc, placeholderId) || {};
|
|
|
|
if (currentPos === null || !attachment) return;
|
|
|
|
tr.setNodeMarkup(currentPos, undefined, {
|
|
src: `/api/files/${attachment.id}/${attachment.fileName}`,
|
|
attachmentId: attachment.id,
|
|
size: attachment.fileSize,
|
|
});
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
const removePlaceholder = (): Command => {
|
|
return ({ tr }) => {
|
|
const { pos: currentPos = null } =
|
|
findAudioNodeByPlaceholderId(tr.doc, placeholderId) || {};
|
|
|
|
if (currentPos === null) return false;
|
|
|
|
tr.delete(currentPos, currentPos + 2);
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
const insertPlaceholderTimeout = setTimeout(() => {
|
|
editor.commands.command(insertPlaceholder());
|
|
placeholderInserted = true;
|
|
}, 250);
|
|
|
|
const disposePreviewFile = () => {
|
|
URL.revokeObjectURL(objectUrl);
|
|
|
|
if (editor.storage.shared.audioPreviews) {
|
|
delete editor.storage.shared.audioPreviews[placeholderId];
|
|
}
|
|
};
|
|
|
|
try {
|
|
const attachment: IAttachment = await onUpload(file, pageId);
|
|
|
|
clearTimeout(insertPlaceholderTimeout);
|
|
|
|
if (placeholderInserted) {
|
|
setTimeout(() => {
|
|
editor.commands.command(replacePlaceholderWithAudio(attachment));
|
|
disposePreviewFile();
|
|
}, 100);
|
|
} else {
|
|
editor
|
|
.chain()
|
|
.command(insertPlaceholder())
|
|
.command(replacePlaceholderWithAudio(attachment))
|
|
.run();
|
|
disposePreviewFile();
|
|
}
|
|
} catch (error) {
|
|
clearTimeout(insertPlaceholderTimeout);
|
|
|
|
editor.commands.command(removePlaceholder());
|
|
disposePreviewFile();
|
|
}
|
|
};
|
|
|
|
export { handleAudioUpload };
|