Skip to content

Commit 237f90f

Browse files
committed
editor: refactor attachment component
Signed-off-by: Hamish <[email protected]>
1 parent 0013fcd commit 237f90f

File tree

1 file changed

+101
-148
lines changed

1 file changed

+101
-148
lines changed

packages/editor/src/extensions/attachment/component.tsx

Lines changed: 101 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { Icons } from "../../toolbar/icons.js";
2525
import { ReactNodeViewProps } from "../react/index.js";
2626
import { ToolbarGroup } from "../../toolbar/components/toolbar-group.js";
2727
import { DesktopOnly } from "../../components/responsive/index.js";
28+
import { ToolbarGroupDefinition } from "../../toolbar/types.js";
29+
import { toBlobURL, revokeBloburl } from "../../utils/downloader.js";
2830

2931
export function AttachmentComponent(
3032
props: ReactNodeViewProps<FileAttachment | AudioAttachment>
@@ -50,15 +52,10 @@ export function AttachmentComponent(
5052
if (data) {
5153
// Convert base64 data to blob URL for audio playback
5254
try {
53-
const byteCharacters = atob(data.split(",")[1] || data);
54-
const byteNumbers = new Array(byteCharacters.length);
55-
for (let i = 0; i < byteCharacters.length; i++) {
56-
byteNumbers[i] = byteCharacters.charCodeAt(i);
55+
const url = toBlobURL(data, "other", mime, hash);
56+
if (url) {
57+
setAudioSrc(url);
5758
}
58-
const byteArray = new Uint8Array(byteNumbers);
59-
const blob = new Blob([byteArray], { type: mime });
60-
const url = URL.createObjectURL(blob);
61-
setAudioSrc(url);
6259
} catch (error) {
6360
console.error("Failed to create audio blob:", error);
6461
}
@@ -71,102 +68,94 @@ export function AttachmentComponent(
7168
// Clean up blob URL on unmount
7269
useEffect(() => {
7370
return () => {
74-
if (audioSrc) {
75-
URL.revokeObjectURL(audioSrc);
71+
if (audioSrc && hash) {
72+
revokeBloburl(hash);
7673
}
7774
};
78-
}, [audioSrc]);
75+
}, [audioSrc, hash]);
7976

80-
if (isAudioFile && audioSrc) {
81-
return (
82-
<Box
83-
ref={elementRef}
77+
const getToolbarTools = (): ToolbarGroupDefinition => {
78+
if (isAudioFile) {
79+
return editor.isEditable
80+
? ["removeAttachment", "downloadAttachment"]
81+
: ["downloadAttachment"];
82+
}
83+
84+
return editor.isEditable
85+
? ["removeAttachment", "downloadAttachment", "previewAttachment"]
86+
: ["downloadAttachment", "previewAttachment"];
87+
};
88+
89+
const renderFileInfo = () => (
90+
<Box
91+
sx={{
92+
display: "flex",
93+
alignItems: "center",
94+
mb: isAudioFile && audioSrc ? 1 : 0
95+
}}
96+
>
97+
<Icon path={Icons.attachment} size={isAudioFile && audioSrc ? 16 : 14} />
98+
<Text
8499
as="span"
85-
contentEditable={false}
86-
variant={"body"}
87100
sx={{
88-
display: "inline-flex",
89-
flexDirection: "column",
90-
position: "relative",
91-
userSelect: "none",
92-
backgroundColor: "var(--background-secondary)",
93-
p: 2,
94-
m: 1,
95-
borderRadius: "default",
96-
border: "1px solid var(--border)",
97-
maxWidth: 350,
98-
borderColor: selected ? "accent" : "border",
99-
":hover": {
100-
bg: "hover"
101-
}
101+
ml: "small",
102+
fontSize: "body",
103+
whiteSpace: "nowrap",
104+
textOverflow: "ellipsis",
105+
overflow: "hidden",
106+
flex: isAudioFile && audioSrc ? 1 : "none"
102107
}}
103-
data-drag-handle
104-
onDragStart={() => setIsDragging(true)}
105-
onDragEnd={() => setIsDragging(false)}
106108
>
107-
<Box sx={{ display: "flex", alignItems: "center", mb: 1 }}>
108-
<Icon path={Icons.attachment} size={16} />
109-
<Text
110-
as="span"
111-
sx={{
112-
ml: "small",
113-
fontSize: "body",
114-
whiteSpace: "nowrap",
115-
textOverflow: "ellipsis",
116-
overflow: "hidden",
117-
flex: 1
118-
}}
119-
>
120-
{filename}
121-
</Text>
122-
<Text
123-
as="span"
124-
sx={{
125-
ml: 1,
126-
fontSize: "0.65rem",
127-
color: "var(--paragraph-secondary)",
128-
flexShrink: 0
129-
}}
130-
>
131-
{progress ? `${progress}%` : formatBytes(size)}
132-
</Text>
133-
</Box>
109+
{filename}
110+
</Text>
111+
<Text
112+
as="span"
113+
sx={{
114+
ml: 1,
115+
fontSize: "0.65rem",
116+
color: "var(--paragraph-secondary)",
117+
flexShrink: 0
118+
}}
119+
>
120+
{progress ? `${progress}%` : formatBytes(size)}
121+
</Text>
122+
</Box>
123+
);
134124

135-
<Box
125+
// Common toolbar component
126+
const renderToolbar = () => (
127+
<DesktopOnly>
128+
{selected && !isDragging && (
129+
<ToolbarGroup
130+
editor={editor}
131+
groupId="attachmentTools"
132+
tools={getToolbarTools()}
136133
sx={{
137-
width: "100%",
138-
"& audio": {
139-
width: "100%",
140-
height: "32px"
141-
}
134+
boxShadow: "menu",
135+
borderRadius: "default",
136+
bg: "background",
137+
position: "absolute",
138+
top: -35
142139
}}
143-
>
144-
<audio controls preload="metadata" src={audioSrc} />
145-
</Box>
140+
/>
141+
)}
142+
</DesktopOnly>
143+
);
146144

147-
<DesktopOnly>
148-
{selected && !isDragging && (
149-
<ToolbarGroup
150-
editor={editor}
151-
groupId="attachmentTools"
152-
tools={
153-
editor.isEditable
154-
? ["removeAttachment", "downloadAttachment"]
155-
: ["downloadAttachment"]
156-
}
157-
sx={{
158-
boxShadow: "menu",
159-
borderRadius: "default",
160-
bg: "background",
161-
position: "absolute",
162-
top: -35
163-
}}
164-
/>
165-
)}
166-
</DesktopOnly>
167-
</Box>
168-
);
169-
}
145+
// Audio player section for audio files
146+
const renderAudioPlayer = () => (
147+
<Box
148+
sx={{
149+
width: "100%",
150+
"& audio": {
151+
width: "100%",
152+
height: "32px"
153+
}
154+
}}
155+
>
156+
<audio controls preload="metadata" src={audioSrc} />
157+
</Box>
158+
);
170159

171160
return (
172161
<Box
@@ -177,74 +166,38 @@ export function AttachmentComponent(
177166
sx={{
178167
display: "inline-flex",
179168
position: "relative",
180-
justifyContent: "center",
181169
userSelect: "none",
182-
alignItems: "center",
183170
backgroundColor: "var(--background-secondary)",
184-
px: 1,
185171
m: 1,
186172
borderRadius: "default",
187173
border: "1px solid var(--border)",
188-
cursor: "pointer",
189-
maxWidth: 250,
190174
borderColor: selected ? "accent" : "border",
191175
":hover": {
192176
bg: "hover"
193-
}
177+
},
178+
// Audio-specific overrides
179+
...(isAudioFile && audioSrc
180+
? {
181+
flexDirection: "column",
182+
p: 2,
183+
width: "50%",
184+
}
185+
: {
186+
justifyContent: "center",
187+
alignItems: "center",
188+
px: 1,
189+
cursor: "pointer",
190+
maxWidth: 250
191+
})
194192
}}
195-
title={filename}
193+
title={!isAudioFile || !audioSrc ? filename : undefined}
196194
onDragStart={() => setIsDragging(true)}
197195
onDragEnd={() => setIsDragging(false)}
198196
data-drag-handle
199197
>
200-
<Icon path={Icons.attachment} size={14} />
201-
<Text
202-
as="span"
203-
sx={{
204-
ml: "small",
205-
fontSize: "body",
206-
whiteSpace: "nowrap",
207-
textOverflow: "ellipsis",
208-
overflow: "hidden"
209-
}}
210-
>
211-
{filename}
212-
</Text>
213-
<Text
214-
as="span"
215-
sx={{
216-
ml: 1,
217-
fontSize: "0.65rem",
218-
color: "var(--paragraph-secondary)",
219-
flexShrink: 0
220-
}}
221-
>
222-
{progress ? `${progress}%` : formatBytes(size)}
223-
</Text>
224-
<DesktopOnly>
225-
{selected && !isDragging && (
226-
<ToolbarGroup
227-
editor={editor}
228-
groupId="attachmentTools"
229-
tools={
230-
editor.isEditable
231-
? [
232-
"removeAttachment",
233-
"downloadAttachment",
234-
"previewAttachment"
235-
]
236-
: ["downloadAttachment", "previewAttachment"]
237-
}
238-
sx={{
239-
boxShadow: "menu",
240-
borderRadius: "default",
241-
bg: "background",
242-
position: "absolute",
243-
top: -35
244-
}}
245-
/>
246-
)}
247-
</DesktopOnly>
198+
{renderFileInfo()}
199+
{isAudioFile && audioSrc && renderAudioPlayer()}
200+
{renderToolbar()}
248201
</Box>
249202
);
250203
}

0 commit comments

Comments
 (0)