Add polls feature: Beep_Polls class, poll UI in composer, voting support

This commit is contained in:
Krystie
2026-05-19 16:31:58 -07:00
parent 237ddbfcc8
commit dfd621e350
6 changed files with 876 additions and 0 deletions
+184
View File
@@ -91,9 +91,18 @@
if (pendingVoice && data.id) {
tasks.push(uploadPendingVoice(data.id, pendingVoice, compose));
}
// Create poll if poll data exists
if (data.id) {
tasks.push(createPoll(data.id, compose).then(function() {
clearPoll(compose);
}));
}
if (tasks.length) {
Promise.all(tasks).then(uploadDone);
} else {
// No uploads, just clear poll if present
clearPoll(compose);
}
})
.catch(function () {
@@ -275,6 +284,50 @@
}
return;
}
// Poll button - toggle poll section
var pollBtn = t.closest('[data-beep-poll-btn]');
if (pollBtn) {
e.preventDefault();
var compose = pollBtn.closest('[data-beep-compose]');
if (compose) openPollSection(compose);
return;
}
// Poll add option
var pollAddOptionBtn = t.closest('[data-beep-poll-add-option]');
if (pollAddOptionBtn) {
e.preventDefault();
var compose = pollAddOptionBtn.closest('[data-beep-compose]');
if (compose) addPollOption(compose);
return;
}
// Poll remove option
var pollRemoveOptionBtn = t.closest('[data-beep-poll-remove-option]');
if (pollRemoveOptionBtn) {
e.preventDefault();
var compose = pollRemoveOptionBtn.closest('[data-beep-compose]');
if (compose) removePollOption(compose);
return;
}
// Poll remove
var pollRemoveBtn = t.closest('[data-beep-poll-remove]');
if (pollRemoveBtn) {
e.preventDefault();
var compose = pollRemoveBtn.closest('[data-beep-compose]');
if (compose) clearPoll(compose);
return;
}
// Poll vote button
var pollVoteBtn = t.closest('[data-beep-poll-vote]');
if (pollVoteBtn) {
e.preventDefault();
if (requireLogin()) handlePollVote(pollVoteBtn);
return;
}
}
function onInput(e) {
@@ -786,3 +839,134 @@
}
closeGifPicker();
}
/* ---------- Poll helpers ---------- */
function openPollSection(compose) {
var pollSection = compose.querySelector('[data-beep-compose-poll]');
if (!pollSection) return;
pollSection.hidden = false;
// Focus question input
var questionInput = pollSection.querySelector('[data-beep-poll-question]');
if (questionInput) questionInput.focus();
// Ensure we have at least 2 options
var options = pollSection.querySelectorAll('[data-beep-poll-option]');
if (options.length < 2) {
while (options.length < 2) {
addPollOption(compose);
options = pollSection.querySelectorAll('[data-beep-poll-option]');
}
}
}
function addPollOption(compose) {
var pollSection = compose.querySelector('[data-beep-compose-poll]');
if (!pollSection) return;
var optionsContainer = pollSection.querySelector('[data-beep-poll-options]');
if (!optionsContainer) return;
var optionCount = optionsContainer.querySelectorAll('[data-beep-poll-option]').length;
if (optionCount >= 6) return; // Max 6 options
var newRow = document.createElement('div');
newRow.className = 'beep-poll-option-row';
newRow.innerHTML = '<input type="text" class="beep-poll-option" data-beep-poll-option placeholder="Option ' + (optionCount + 1) + '" maxlength="100">';
optionsContainer.appendChild(newRow);
updatePollButtons(compose);
}
function removePollOption(compose) {
var pollSection = compose.querySelector('[data-beep-compose-poll]');
if (!pollSection) return;
var options = pollSection.querySelectorAll('[data-beep-poll-option]');
if (options.length <= 2) return; // Min 2 options
var lastOption = options[options.length - 1];
if (lastOption) lastOption.parentElement.remove();
updatePollButtons(compose);
}
function updatePollButtons(compose) {
var pollSection = compose.querySelector('[data-beep-compose-poll]');
if (!pollSection) return;
var options = pollSection.querySelectorAll('[data-beep-poll-option]');
var addBtn = pollSection.querySelector('[data-beep-poll-add-option]');
var removeBtn = pollSection.querySelector('[data-beep-poll-remove-option]');
if (addBtn) addBtn.hidden = options.length >= 6;
if (removeBtn) removeBtn.hidden = options.length <= 2;
}
function clearPoll(compose) {
if (!compose) return;
compose._beepPoll = null;
var pollSection = compose.querySelector('[data-beep-compose-poll]');
if (pollSection) {
pollSection.hidden = true;
var question = pollSection.querySelector('[data-beep-poll-question]');
if (question) question.value = '';
var options = pollSection.querySelectorAll('[data-beep-poll-option]');
// Reset to 2 options
if (options.length > 2) {
for (var i = options.length - 1; i >= 2; i--) {
if (options[i].parentElement) options[i].parentElement.remove();
}
}
options = pollSection.querySelectorAll('[data-beep-poll-option]');
for (var j = 0; j < options.length; j++) {
options[j].value = '';
}
}
}
function collectPollData(compose) {
if (!compose) return null;
var pollSection = compose.querySelector('[data-beep-compose-poll]');
if (!pollSection || pollSection.hidden) return null;
var questionInput = pollSection.querySelector('[data-beep-poll-question]');
var question = questionInput ? questionInput.value.trim() : '';
if (!question) return null;
var optionInputs = pollSection.querySelectorAll('[data-beep-poll-option]');
var options = [];
for (var i = 0; i < optionInputs.length; i++) {
var val = optionInputs[i].value.trim();
if (val) options.push(val);
}
if (options.length < 2) return null;
return { question: question, options: options };
}
function createPoll(beepId, compose) {
var pollData = collectPollData(compose);
if (!pollData) return Promise.resolve();
return fetch(config.restUrl + 'poll/' + beepId, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce },
credentials: 'same-origin',
body: JSON.stringify(pollData)
})
.then(handleResponse)
.catch(function(err) {
console.warn('Poll creation failed:', err);
});
}
function handlePollVote(btn) {
var beepId = btn.dataset.beepPollBeep;
var optionIndex = parseInt(btn.dataset.beepPollVote, 10);
if (!beepId || isNaN(optionIndex)) return;
btn.disabled = true;
fetch(config.restUrl + 'poll/' + beepId + '/vote', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce },
credentials: 'same-origin',
body: JSON.stringify({ option_index: optionIndex })
})
.then(handleResponse)
.then(function(data) {
// Refresh the beep post to show updated poll results
var postEl = btn.closest('[data-beep-post]');
if (postEl) {
var list = postEl.closest('[data-beep-list]');
if (list) reloadFeed(list.closest('[data-beep-feed]'), 'latest');
}
})
.catch(function() {
btn.disabled = false;
});
}