Add media upload JS + CSS: image preview in composer, AJAX upload on submit, Twitter-style gallery grid, video embed styling

This commit is contained in:
Krystie
2026-05-18 11:51:21 -07:00
parent 6c9b06ccb2
commit 07ac9f112d
2 changed files with 290 additions and 0 deletions
+101
View File
@@ -16,6 +16,7 @@
var compose = feed.querySelector('[data-beep-compose]');
if (compose) initCompose(compose, feed);
initSortTabs(feed);
initMedia(compose, feed);
feed.addEventListener('click', onClick);
feed.addEventListener('input', onInput);
feed.addEventListener('keydown', onKeydown);
@@ -63,6 +64,15 @@
}
input.value = '';
refresh();
// Upload pending images to this new beep
var pending = compose._beepPendingImages;
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');
});
}
})
.catch(function () {
window.alert('Could not post. Please try again.');
@@ -325,6 +335,97 @@
return Array.from(s).length;
}
/* ---------- Media upload (images) ---------- */
function initMedia(compose, feed) {
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 pendingImages = [];
input.addEventListener('change', function (e) {
var files = Array.prototype.slice.call(e.target.files);
if (!files.length) return;
var room = 4 - pendingImages.length;
if (room <= 0) { input.value = ''; return; }
files = files.slice(0, room);
files.forEach(function (file) {
if (file.size > 10 * 1024 * 1024) {
window.alert('Image must be under 10 MB: ' + file.name);
return;
}
var reader = new FileReader();
reader.onload = function (ev) {
var url = ev.target.result;
var id = 'pending-' + Date.now() + '-' + Math.random().toString(36).substr(2, 6);
pendingImages.push({ file: file, previewUrl: url, id: id });
renderPreviews();
};
reader.readAsDataURL(file);
});
input.value = '';
});
function renderPreviews() {
if (!pendingImages.length) {
preview.innerHTML = '';
preview.hidden = true;
if (counter) counter.hidden = true;
return;
}
preview.hidden = false;
if (counter) {
counter.textContent = pendingImages.length + '/4';
counter.hidden = false;
}
preview.innerHTML = pendingImages.map(function (img) {
return '<div class="beep-media-preview-item" data-preview-id="' + img.id + '">' +
'<img src="' + img.previewUrl + '">' +
'<button class="beep-media-remove" type="button" title="Remove" data-remove="' + img.id + '">&times;</button>' +
'</div>';
}).join('');
}
preview.addEventListener('click', function (e) {
var btn = e.target.closest('[data-remove]');
if (!btn) return;
var id = btn.dataset.remove;
pendingImages = pendingImages.filter(function (img) { return img.id !== id; });
renderPreviews();
});
compose._beepPendingImages = pendingImages;
}
function uploadPendingImages(postId, pendingImages, compose) {
if (!pendingImages || !pendingImages.length) return Promise.resolve();
var promises = pendingImages.map(function (img) {
var formData = new FormData();
formData.append('file', img.file);
return fetch(config.restUrl + 'media/' + postId, {
method: 'POST',
headers: { 'X-WP-Nonce': config.nonce },
credentials: 'same-origin',
body: formData
})
.then(handleResponse)
.catch(function (err) {
console.warn('Image upload failed:', img.file.name, err);
});
});
return Promise.all(promises).then(function () {
pendingImages.length = 0;
if (compose) {
var preview = compose.querySelector('[data-beep-media-preview]');
if (preview) { preview.innerHTML = ''; preview.hidden = true; }
var counter = compose.querySelector('[data-beep-media-count]');
if (counter) counter.hidden = true;
}
});
}
function handleResponse(res) {
if (!res.ok) return res.json().then(function (j) { throw j; }, function () { throw res; });
return res.json();