Skip to content

Commit 72a3b94

Browse files
committed
Drag and drop for audio uploads
1 parent ef39a86 commit 72a3b94

File tree

4 files changed

+67
-13
lines changed

4 files changed

+67
-13
lines changed

src/audioVisualizer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export default class AudioVisualizer extends Component {
6161
}
6262

6363
render(props) {
64-
return <canvas class="audioVisualizer"
64+
return <canvas class={`audioVisualizer ${props.class}`}
6565
onclick={props.onclick}
6666
height={props.height}
6767
width={props.width}

src/messagePanel.js

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as Icons from "./icons.js"
22
import { h, createRef, Fragment, Component } from 'preact';
33
import * as CommonMark from 'commonmark'
4-
import { loadImageElement, loadVideoElement, createThumbnail, blurhashFromFile } from "./utils/media.js"
4+
import { loadImageElement, loadMediaElement, createThumbnail, blurhashFromFile } from "./utils/media.js"
55
import { spaceChild, spaceParent, mscLocation, mscParent, mscMarkupMsgKey, mscPdfText, mscMediaFragment, mscPdfHighlight, populusHighlight } from "./constants.js"
66
import { processRegex } from './processRegex.js'
77
import { UserColor } from './utils/colors.js'
@@ -182,6 +182,8 @@ class FileUploadInput extends Component {
182182

183183
theForm = createRef()
184184

185+
secondaryAudio = createRef()
186+
185187
getFile = _ => this.props.file || this.fileLoader.current.files[0]
186188

187189
validateFile = file => {
@@ -199,6 +201,16 @@ class FileUploadInput extends Component {
199201
previewUrl: URL.createObjectURL(file),
200202
mediaType: "image",
201203
})
204+
} else if (/^audio/.test(file.type)) {
205+
loadMediaElement(file, "audio").then(elt => {
206+
this.mediaElement = elt
207+
const stream = elt.mozCaptureStream?.() || elt.captureStream()
208+
this.setState({
209+
stream,
210+
previewUrl: URL.createObjectURL(file),
211+
mediaType: "audio",
212+
})
213+
})
202214
} else {
203215
this.setState({ mediaType: "default" })
204216
}
@@ -209,12 +221,26 @@ class FileUploadInput extends Component {
209221
switch (this.state.mediaType) {
210222
case "image": await this.submitImage(); break
211223
case "video": await this.submitVideo(); break
224+
case "audio": await this.submitAudio(); break
212225
case "default": await this.submitDefault(); break
213226
}
214227
}
215228
this.props.done()
216229
}
217230

231+
handleMediaClick = _ => {
232+
console.log(this.mediaElement.current)
233+
if (this.mediaElement.paused) {
234+
this.mediaElement.currentTime = 0
235+
this.mediaElement.play()
236+
this.secondaryAudio.current?.play()
237+
}
238+
else {
239+
this.mediaElement.pause()
240+
this.secondaryAudio.current?.pause()
241+
}
242+
}
243+
218244
submitDefault = async _ => {
219245
const theFile = this.getFile()
220246
const mxc = await Client.client.uploadContent(theFile, { progressHandler: this.progressHandler }).catch(e => console.log(e))
@@ -234,8 +260,8 @@ class FileUploadInput extends Component {
234260

235261
submitVideo = async _ => {
236262
const theVideo = this.getFile()
237-
window.theVideo = theVideo
238-
const videoElt = await loadVideoElement(theVideo)
263+
// window.theVideo = theVideo
264+
const videoElt = await loadMediaElement(theVideo, "video")
239265
const thumbContent = await createThumbnail(videoElt, videoElt.videoWidth, videoElt.videoHeight, "image/jpeg")
240266
const thumbMxc = await Client.client.uploadContent(thumbContent.thumbnail, {
241267
name: `${theVideo.name}_800x600`,
@@ -265,6 +291,25 @@ class FileUploadInput extends Component {
265291
this.props.handlePending(theContent, eventI)
266292
}
267293

294+
submitAudio = async _ => {
295+
const theAudio = this.getFile()
296+
console.log("upload audio")
297+
const audioMxc = await Client.client.uploadContent(theAudio, { progressHandler: this.progressHandler })
298+
const duration = Math.round(this.mediaElement.duration * 1000)
299+
const theContent = {
300+
body: theAudio.name,
301+
info: {
302+
mimetype: theAudio.type,
303+
size: theAudio.size
304+
},
305+
msgtype: "m.audio",
306+
url: audioMxc,
307+
}
308+
if (duration < Infinity) theContent.duration = duration
309+
const eventI = await Client.client.sendMessage(this.props.roomId, theContent)
310+
this.props.handlePending(theContent, eventI)
311+
}
312+
268313
submitImage = async _ => {
269314
const theImage = this.getFile()
270315
const {width, height, img} = await loadImageElement(theImage)
@@ -304,6 +349,13 @@ class FileUploadInput extends Component {
304349
? <video ref={this.mediaPreview} class="video-message-preview media-message-thumbnail" controls src={state.previewUrl} />
305350
: state.mediaType === "image"
306351
? <img ref={this.mediaPreview} class="image-message-preview media-message-thumbnail" src={this.state.previewUrl} />
352+
: state.mediaType === "audio" && state.stream
353+
? <Fragment>
354+
{//workaround for firefox bug: https://bugzilla-dev.allizom.org/show_bug.cgi?id=1178751
355+
this.mediaElement.mozCaptureStream ? <audio ref={this.secondaryAudio} srcObject={state.stream} /> : null
356+
}
357+
<AudioVisualizer class="audio-message-preview media-message-thumbnail" height="100" onclick={this.handleMediaClick} stream={state.stream} />
358+
</Fragment>
307359
: state.mediaType === "default"
308360
? <div id="file-uploader-preview">
309361
<span>{Icons.file}</span>

src/styles/message.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,14 +390,16 @@ html[data-scrollbars="hidden"] .message-body pre::-webkit-scrollbar { display: n
390390

391391
.media-message-thumbnail {
392392
border-radius:20px;
393-
max-width:calc(100% - 20px)
393+
max-width:calc(100% - 20px);
394+
overflow:hidden;
394395
}
395396

396397
img.media-message-thumbnail {
397398
cursor: pointer;
398399
}
399400

400401
.image-message-preview,
402+
.audio-message-preview,
401403
.video-message-preview {
402404
margin:10px;
403405
width: calc(100% - 30px);

src/utils/media.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,22 +100,22 @@ function readFileAsArrayBuffer(file) {
100100
})
101101
}
102102

103-
export function loadVideoElement(videoFile) {
103+
export function loadMediaElement(mediaFile, tag) {
104104
return new Promise((resolve, reject) => {
105105
// Load the file into an html element
106-
const video = document.createElement("video")
107-
const theUrl = URL.createObjectURL(videoFile)
108-
video.src = theUrl
106+
const element = document.createElement(tag)
107+
const theUrl = URL.createObjectURL(mediaFile)
108+
element.src = theUrl
109109
// Once ready, returns its size
110-
// Wait until we have enough data to thumbnail the first frame.
111-
video.onloadeddata = _ => {
110+
// Wait until we have enough data to thumbnail the first frame, or get metadata
111+
element.onloadeddata = _ => {
112112
URL.revokeObjectURL(theUrl)
113-
resolve(video)
113+
resolve(element)
114114
}
115115
// XXX: this was previously using FileReader and a data URL, but an
116116
// apparent bug in firefox 98 seems to prevent FileReader-generated data
117117
// urls from being used with video elements
118-
video.onerror = e => reject(e)
118+
element.onerror = e => reject(e)
119119
})
120120
}
121121

0 commit comments

Comments
 (0)