196 lines
5.3 KiB
JavaScript
196 lines
5.3 KiB
JavaScript
|
|
// Popup script for Jackbox Chat Tracker
|
||
|
|
console.log('Jackbox Chat Tracker: Popup loaded');
|
||
|
|
|
||
|
|
let isTracking = false;
|
||
|
|
let votes = [];
|
||
|
|
|
||
|
|
// DOM elements
|
||
|
|
const startBtn = document.getElementById('startTracking');
|
||
|
|
const stopBtn = document.getElementById('stopTracking');
|
||
|
|
const statusIndicator = document.getElementById('statusIndicator');
|
||
|
|
const statusText = document.getElementById('statusText');
|
||
|
|
const votesDisplay = document.getElementById('votesDisplay');
|
||
|
|
const voteCount = document.getElementById('voteCount');
|
||
|
|
const resetVotesBtn = document.getElementById('resetVotes');
|
||
|
|
const exportVotesBtn = document.getElementById('exportVotes');
|
||
|
|
|
||
|
|
// Initialize
|
||
|
|
loadState();
|
||
|
|
|
||
|
|
// Poll for updates every 2 seconds to keep sidebar in sync
|
||
|
|
setInterval(() => {
|
||
|
|
chrome.storage.local.get(['isTracking', 'votes'], (result) => {
|
||
|
|
const wasTracking = isTracking;
|
||
|
|
isTracking = result.isTracking || false;
|
||
|
|
votes = result.votes || [];
|
||
|
|
|
||
|
|
if (wasTracking !== isTracking) {
|
||
|
|
updateTrackingUI();
|
||
|
|
}
|
||
|
|
updateDisplay();
|
||
|
|
});
|
||
|
|
}, 2000);
|
||
|
|
|
||
|
|
// Event listeners
|
||
|
|
startBtn.addEventListener('click', startTracking);
|
||
|
|
stopBtn.addEventListener('click', stopTracking);
|
||
|
|
resetVotesBtn.addEventListener('click', resetVotes);
|
||
|
|
exportVotesBtn.addEventListener('click', exportVotes);
|
||
|
|
|
||
|
|
// Listen for updates from content script
|
||
|
|
chrome.runtime.onMessage.addListener((message) => {
|
||
|
|
if (message.action === 'votesUpdated') {
|
||
|
|
votes = message.votes;
|
||
|
|
updateDisplay();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
function loadState() {
|
||
|
|
chrome.storage.local.get(['isTracking', 'votes'], (result) => {
|
||
|
|
isTracking = result.isTracking || false;
|
||
|
|
votes = result.votes || [];
|
||
|
|
|
||
|
|
updateDisplay();
|
||
|
|
updateTrackingUI();
|
||
|
|
|
||
|
|
// Request current votes from content script
|
||
|
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||
|
|
if (tabs[0]) {
|
||
|
|
chrome.tabs.sendMessage(tabs[0].id, { action: 'getVotes' }, (response) => {
|
||
|
|
if (response && response.votes) {
|
||
|
|
votes = response.votes;
|
||
|
|
updateDisplay();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function startTracking() {
|
||
|
|
isTracking = true;
|
||
|
|
chrome.storage.local.set({ isTracking });
|
||
|
|
|
||
|
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||
|
|
if (tabs[0]) {
|
||
|
|
chrome.tabs.sendMessage(tabs[0].id, { action: 'startTracking' }, (response) => {
|
||
|
|
if (response && response.success) {
|
||
|
|
console.log('Tracking started');
|
||
|
|
updateTrackingUI();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function stopTracking() {
|
||
|
|
isTracking = false;
|
||
|
|
chrome.storage.local.set({ isTracking });
|
||
|
|
|
||
|
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||
|
|
if (tabs[0]) {
|
||
|
|
chrome.tabs.sendMessage(tabs[0].id, { action: 'stopTracking' }, (response) => {
|
||
|
|
if (response && response.success) {
|
||
|
|
console.log('Tracking stopped');
|
||
|
|
updateTrackingUI();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function resetVotes() {
|
||
|
|
if (!confirm('Reset all recorded votes? This cannot be undone.')) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
votes = [];
|
||
|
|
chrome.storage.local.set({ votes });
|
||
|
|
|
||
|
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||
|
|
if (tabs[0]) {
|
||
|
|
chrome.tabs.sendMessage(tabs[0].id, { action: 'resetVotes' }, (response) => {
|
||
|
|
if (response && response.success) {
|
||
|
|
console.log('Votes reset');
|
||
|
|
updateDisplay();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function exportVotes() {
|
||
|
|
// Export votes in the new format
|
||
|
|
const exportData = votes.map(vote => ({
|
||
|
|
username: vote.username,
|
||
|
|
message: vote.message,
|
||
|
|
timestamp: vote.timestamp
|
||
|
|
}));
|
||
|
|
|
||
|
|
const dataStr = JSON.stringify(exportData, null, 2);
|
||
|
|
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
||
|
|
const url = URL.createObjectURL(dataBlob);
|
||
|
|
|
||
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||
|
|
const filename = `jackbox-votes-${timestamp}.json`;
|
||
|
|
|
||
|
|
chrome.downloads.download({
|
||
|
|
url: url,
|
||
|
|
filename: filename,
|
||
|
|
saveAs: true
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateTrackingUI() {
|
||
|
|
if (isTracking) {
|
||
|
|
statusIndicator.classList.add('active');
|
||
|
|
statusText.textContent = 'Tracking Active';
|
||
|
|
startBtn.disabled = true;
|
||
|
|
stopBtn.disabled = false;
|
||
|
|
} else {
|
||
|
|
statusIndicator.classList.remove('active');
|
||
|
|
statusText.textContent = 'Not Tracking';
|
||
|
|
startBtn.disabled = false;
|
||
|
|
stopBtn.disabled = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateDisplay() {
|
||
|
|
voteCount.textContent = votes.length;
|
||
|
|
|
||
|
|
if (votes.length === 0) {
|
||
|
|
votesDisplay.innerHTML = '<p class="empty-state">No votes recorded yet. Start tracking to begin!</p>';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Display votes in reverse chronological order (most recent first)
|
||
|
|
const sortedVotes = [...votes].reverse();
|
||
|
|
|
||
|
|
let html = '<div class="vote-list">';
|
||
|
|
sortedVotes.forEach((vote, index) => {
|
||
|
|
const localTime = new Date(vote.timestamp).toLocaleString();
|
||
|
|
const isPositive = vote.message.includes('++');
|
||
|
|
const voteClass = isPositive ? 'vote-positive' : 'vote-negative';
|
||
|
|
|
||
|
|
html += `
|
||
|
|
<div class="vote-item ${voteClass}">
|
||
|
|
<div class="vote-header">
|
||
|
|
<strong>${escapeHtml(vote.username)}</strong>
|
||
|
|
<span class="vote-time">${localTime}</span>
|
||
|
|
</div>
|
||
|
|
<div class="vote-message">${escapeHtml(vote.message)}</div>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
});
|
||
|
|
html += '</div>';
|
||
|
|
|
||
|
|
votesDisplay.innerHTML = html;
|
||
|
|
}
|
||
|
|
|
||
|
|
function escapeHtml(text) {
|
||
|
|
const div = document.createElement('div');
|
||
|
|
div.textContent = text;
|
||
|
|
return div.innerHTML;
|
||
|
|
}
|
||
|
|
|