editor: various fixes and improvements related to audio widget

This commit is contained in:
Abdullah Atta
2026-02-24 09:36:47 +05:00
parent add1b8a68a
commit ab900ae5da
4 changed files with 63 additions and 35 deletions

View File

@@ -39,8 +39,6 @@ declare module "@tiptap/core" {
export const AudioNode = Node.create<AudioOptions>({
name: "audio",
content: "",
marks: "",
draggable: true,
priority: 51,
@@ -71,7 +69,7 @@ export const AudioNode = Node.create<AudioOptions>({
parseHTML() {
return [
{
tag: "audio[data-hash][data-mime^='audio/']"
tag: "audio"
}
];
},

View File

@@ -28,39 +28,27 @@ import { DesktopOnly } from "../../components/responsive/index.js";
import { toBlobURL, revokeBloburl } from "../../utils/downloader.js";
import { formatBytes } from "@notesnook/common";
const SAMPLE_AUDIO = toBlobURL(
"data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
"other",
"audio/wav",
"sample-audio"
);
export function AudioComponent(props: ReactNodeViewProps<AudioAttachment>) {
const { editor, node, selected } = props;
const { filename, size, progress, mime, hash } = node.attrs;
const elementRef = useRef<HTMLDivElement>();
const [isDragging, setIsDragging] = useState(false);
const [audioSrc, setAudioSrc] = useState<string>();
useEffect(() => {
if (editor.storage?.getAttachmentData && hash) {
editor.storage
.getAttachmentData({
type: "file",
hash
})
.then((data: string | undefined) => {
if (data) {
const url = toBlobURL(data, "other", mime, hash);
if (url) {
setAudioSrc(url);
}
}
})
.catch(console.error);
}
}, [editor.storage, hash, mime]);
const [error, setError] = useState<string>();
useEffect(() => {
return () => {
if (audioSrc && hash) {
if (hash) {
revokeBloburl(hash);
}
};
}, [audioSrc, hash]);
}, [hash]);
return (
<Box
@@ -86,12 +74,13 @@ export function AudioComponent(props: ReactNodeViewProps<AudioAttachment>) {
onDragStart={() => setIsDragging(true)}
onDragEnd={() => setIsDragging(false)}
data-drag-handle
data-drag-image
>
<Box
sx={{
display: "flex",
alignItems: "center",
mb: audioSrc ? 2 : 0
mb: 2
}}
>
<Icon path={Icons.attachment} size={16} />
@@ -120,7 +109,9 @@ export function AudioComponent(props: ReactNodeViewProps<AudioAttachment>) {
{progress ? `${progress}%` : formatBytes(size, 1)}
</Text>
</Box>
{audioSrc && (
{error ? (
<Text variant="error">{error}</Text>
) : (
<Box
sx={{
width: "100%",
@@ -130,7 +121,39 @@ export function AudioComponent(props: ReactNodeViewProps<AudioAttachment>) {
}
}}
>
<audio controls preload="metadata" src={audioSrc} />
<audio
onMouseDown={(e) => e.stopPropagation()}
onDragStart={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
controls
controlsList="nodownload nofullscreen"
src={SAMPLE_AUDIO}
onPlay={(e) => {
const target = e.currentTarget;
if (
editor.storage?.getAttachmentData &&
hash &&
target.src === SAMPLE_AUDIO
) {
e.preventDefault();
editor.storage
.getAttachmentData({
type: "file",
hash
})
.then((data: string | undefined) => {
if (!data) return;
const url = toBlobURL(data, "other", mime, hash);
if (!url) return;
target.src = url;
return target.play();
})
.catch((e) => setError((e as Error).message));
}
}}
>
<Text as="p">Your browser does not support the audio element.</Text>
</audio>
</Box>
)}
<DesktopOnly>

View File

@@ -23,6 +23,7 @@ import { MoreTools } from "../components/more-tools.js";
import { useToolbarLocation } from "../stores/toolbar-store.js";
import { findSelectedNode } from "../../utils/prosemirror.js";
import { Attachment } from "../../extensions/attachment/index.js";
import { Editor } from "../../types.js";
export function AttachmentSettings(props: ToolProps) {
const { editor } = props;
@@ -52,9 +53,7 @@ export function DownloadAttachment(props: ToolProps) {
title={props.title}
toggled={false}
onClick={() => {
const attachmentNode =
findSelectedNode(editor, "attachment") ||
findSelectedNode(editor, "image");
const attachmentNode = findAttachmentNode(editor);
const attachment = (attachmentNode?.attrs || {}) as Attachment;
editor.storage.downloadAttachment?.(attachment);
@@ -65,8 +64,7 @@ export function DownloadAttachment(props: ToolProps) {
export function PreviewAttachment(props: ToolProps) {
const { editor } = props;
const attachmentNode =
findSelectedNode(editor, "attachment") || findSelectedNode(editor, "image");
const attachmentNode = findAttachmentNode(editor);
const attachment = (attachmentNode?.attrs || {}) as Attachment;
if (!editor.isActive("image") && !canPreviewAttachment(attachment))
@@ -117,3 +115,11 @@ function canPreviewAttachment(attachment: Attachment) {
return previewableFileExtensions.indexOf(extension) > -1;
}
function findAttachmentNode(editor: Editor) {
return (
findSelectedNode(editor, "attachment") ||
findSelectedNode(editor, "image") ||
findSelectedNode(editor, "audio")
);
}

View File

@@ -131,8 +131,9 @@
}
.ProseMirror > div.codeblock-view-content-wrap,
.ProseMirror > div.taskList-view-content-wrap,
.ProseMirror > div.math-block.math-node {
.ProseMirror > div.math-block.math-node,
.ProseMirror > div.audio-view-content-wrap,
.ProseMirror > div.taskList-view-content-wrap {
margin-top: 1em;
margin-bottom: 1em;
}