Add voice message support: audio upload (MP3/OGG/WAV/WebM/M4A), inline audio player in posts, voice note button + preview in composer
This commit is contained in:
+88
-6
@@ -65,13 +65,23 @@
|
||||
input.value = '';
|
||||
refresh();
|
||||
|
||||
// Upload pending images to this new beep
|
||||
// Upload pending images and voice note to this new beep
|
||||
var pending = compose._beepPendingImages;
|
||||
var pendingVoice = compose._beepPendingVoice;
|
||||
var uploadDone = function () {
|
||||
reloadFeed(feed, 'recent');
|
||||
};
|
||||
|
||||
var tasks = [];
|
||||
if (pending && pending.length && data.id) {
|
||||
uploadPendingImages(data.id, pending, compose).then(function () {
|
||||
// Refresh the beep in the DOM to show uploaded images
|
||||
reloadFeed(feed, 'recent');
|
||||
});
|
||||
tasks.push(uploadPendingImages(data.id, pending, compose));
|
||||
}
|
||||
if (pendingVoice && data.id) {
|
||||
tasks.push(uploadPendingVoice(data.id, pendingVoice, compose));
|
||||
}
|
||||
|
||||
if (tasks.length) {
|
||||
Promise.all(tasks).then(uploadDone);
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
@@ -341,9 +351,57 @@
|
||||
var input = compose.querySelector('[data-beep-media-input]');
|
||||
var preview = compose.querySelector('[data-beep-media-preview]');
|
||||
var counter = compose.querySelector('[data-beep-media-count]');
|
||||
if (!input) return;
|
||||
var voiceInput = compose.querySelector('[data-beep-voice-input]');
|
||||
var voicePreview = compose.querySelector('[data-beep-voice-preview]');
|
||||
if (!input && !voiceInput) return;
|
||||
|
||||
var pendingImages = [];
|
||||
var pendingVoice = null; // { file, id }
|
||||
|
||||
// Voice note input
|
||||
if (voiceInput) {
|
||||
voiceInput.addEventListener('change', function (e) {
|
||||
var files = e.target.files;
|
||||
if (!files || !files.length) return;
|
||||
var file = files[0];
|
||||
if (file.size > 20 * 1024 * 1024) {
|
||||
window.alert('Voice note must be under 20 MB.');
|
||||
voiceInput.value = '';
|
||||
return;
|
||||
}
|
||||
var accepted = ['audio/mpeg','audio/ogg','audio/wav','audio/webm','audio/mp4','audio/x-m4a'];
|
||||
if (accepted.indexOf(file.type) === -1) {
|
||||
window.alert('Only MP3, OGG, WAV, WebM, and M4A audio are allowed.');
|
||||
voiceInput.value = '';
|
||||
return;
|
||||
}
|
||||
if (pendingVoice) {
|
||||
window.alert('Only one voice note per beep.');
|
||||
voiceInput.value = '';
|
||||
return;
|
||||
}
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (ev) {
|
||||
var id = 'voice-' + Date.now();
|
||||
pendingVoice = { file: file, id: id };
|
||||
voicePreview.innerHTML = '<div class="beep-voice-preview-card"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1-9c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1V5zm6 6c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08C16.39 17.43 19 14.53 19 11h-2z"/></svg><span>Voice note</span><button class="beep-voice-remove" data-beep-voice-remove type="button" title="Remove">×</button></div>';
|
||||
voicePreview.hidden = false;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
voiceInput.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// Voice remove button
|
||||
if (voicePreview) {
|
||||
voicePreview.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('[data-beep-voice-remove]');
|
||||
if (!btn) return;
|
||||
pendingVoice = null;
|
||||
voicePreview.innerHTML = '';
|
||||
voicePreview.hidden = true;
|
||||
});
|
||||
}
|
||||
|
||||
input.addEventListener('change', function (e) {
|
||||
var files = Array.prototype.slice.call(e.target.files);
|
||||
@@ -397,6 +455,7 @@
|
||||
});
|
||||
|
||||
compose._beepPendingImages = pendingImages;
|
||||
compose._beepPendingVoice = pendingVoice;
|
||||
}
|
||||
|
||||
function uploadPendingImages(postId, pendingImages, compose) {
|
||||
@@ -426,6 +485,29 @@
|
||||
});
|
||||
}
|
||||
|
||||
function uploadPendingVoice(postId, pendingVoice, compose) {
|
||||
if (!pendingVoice) return Promise.resolve();
|
||||
var formData = new FormData();
|
||||
formData.append('file', pendingVoice.file);
|
||||
return fetch(config.restUrl + 'voice/' + postId, {
|
||||
method: 'POST',
|
||||
headers: { 'X-WP-Nonce': config.nonce },
|
||||
credentials: 'same-origin',
|
||||
body: formData
|
||||
})
|
||||
.then(handleResponse)
|
||||
.then(function () {
|
||||
pendingVoice = null;
|
||||
if (compose) {
|
||||
var vp = compose.querySelector('[data-beep-voice-preview]');
|
||||
if (vp) { vp.innerHTML = ''; vp.hidden = true; }
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.warn('Voice upload failed:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function handleResponse(res) {
|
||||
if (!res.ok) return res.json().then(function (j) { throw j; }, function () { throw res; });
|
||||
return res.json();
|
||||
|
||||
Reference in New Issue
Block a user