Initial app

This commit is contained in:
sto 2025-04-12 14:40:20 +02:00
parent 713e10f585
commit 1428aa9a26
5 changed files with 282 additions and 0 deletions

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

19
manifest.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "Puzzle leaderboard tracker",
"description": "Base Level Extension",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"permissions": [
"storage"
],
"content_scripts": [
{
"js": ["scripts/content.js"],
"matches": ["https://meet.google.com/*"]
}
]
}

80
popup.html Normal file
View File

@ -0,0 +1,80 @@
<html>
<style>
html {
height: 0;
}
body {
margin: 10px;
width: 400px;
}
[bottom] {
height: 10px;
}
h1 {
align-items: center;
display: flex;
justify-content: center;
margin-bottom: 5px;
}
[container] {
display: flex;
flex-direction: column;
}
#time {
display: flex;
font-size: 28px;
justify-content: center;
}
button {
border: solid 1px grey;
border-radius: 5px;
height: 30px;
margin-top: 5px;
}
button:hover {
cursor: pointer;
opacity: 0.8;
}
#start-button {
background-color: lightgreen;
}
#end-button {
background-color: lightskyblue;
}
#reset-button {
background-color: lightcoral;
}
[messages] {
display: none;
margin-top: 10px;
max-height: 300px;
overflow: auto;
}
span[time] {
color: red;
margin-right: 5px;
}
span[name] {
color: black;
font-weight: 600;
margin-right: 5px;
}
span[text] {
color: black;
}
</style>
<script src="popup.js"></script>
<body>
<h1>Puzzle contest</h1>
<div container>
<div id="time">0:00:00</div>
<button id="start-button">Start a new contest</button>
<button id="end-button" style="display: none;">End the contest</button>
<button id="reset-button" style="display: none;">Reset all saved data</button>
<button id="messages-button">show messages</button>
<div messages id="messages">
</div>
</div>
<div bottom></div>
</body>
</html>

110
popup.js Normal file
View File

@ -0,0 +1,110 @@
let startTime = 0;
let endTime = 0;
let messagesCount = 0;
let showMessages = false;
async function init() {
const data = await chrome.storage.local.get(['startTime', 'endTime']);
if (data?.endTime > 0) {
startTime = data.startTime;
endTime = data.endTime;
document.getElementById('reset-button').style.display = 'block';
} else if (data?.startTime > 0) {
startTime = data.startTime;
document.getElementById('start-button').style.display = 'none';
document.getElementById('end-button').style.display = 'block';
}
const startButton = document.getElementById('start-button');
startButton.onclick = startContest;
const endButton = document.getElementById('end-button');
endButton.onclick = endContest;
const resetButton = document.getElementById('reset-button');
resetButton.onclick = resetData;
const messagesButton = document.getElementById('messages-button');
messagesButton.onclick = switchMessages;
update();
}
async function fetchMessages() {
const data = await chrome.storage.local.get('messages');
if (data.messages && data.messages.length > messagesCount) {
const messagesEl = document.getElementById('messages');
let html = messagesEl.innerHTML;
for (let i = messagesCount; i < data.messages.length; i++) {
const {displayTime, completionTime, name, text} = data.messages[i];
html += `<div message><span time>${displayTime}</span><span name>${name}</span><span text>${text}</span></div>`
}
messagesEl.innerHTML = html;
messagesCount = data.messages.length;
}
}
function sendMessage(message, callback) {
chrome.tabs.query({active: true, currentWindow: true}, tabs => {
tabs.forEach(tab => chrome.tabs.sendMessage(tab.id, message, callback));
});
}
function startContest() {
startTime = Date.now();
endTime = 0;
chrome.storage.local.set({ startTime: startTime, endTime: 0, messages: [] });
document.getElementById('start-button').style.display = 'none';
document.getElementById('end-button').style.display = 'block';
document.getElementById('reset-button').style.display = 'none';
document.getElementById('messages').innerHTML = '';
if (showMessages) switchMessages();
}
function endContest() {
endTime = Date.now();
chrome.storage.local.set({ endTime: endTime });
document.getElementById('start-button').style.display = 'block';
document.getElementById('end-button').style.display = 'none';
document.getElementById('reset-button').style.display = 'block';
}
function resetData() {
chrome.storage.local.set({ startTime: 0, endTime: 0, messages: [] });
startTime = 0;
endTime = 0;
document.getElementById('time').innerHTML = '0:00:00';
document.getElementById('reset-button').style.display = 'none';
document.getElementById('messages').innerHTML = '';
if (showMessages) switchMessages();
}
function switchMessages() {
const messagesEl = document.getElementById('messages');
const messagesButtonEl = document.getElementById('messages-button');
if (showMessages) {
messagesEl.style.display = 'none';
messagesButtonEl.innerHTML = 'show messages';
} else {
messagesEl.style.display = 'block';
messagesButtonEl.innerHTML = 'hide messages';
}
showMessages = !showMessages;
}
function dis(n) {
if (n < 10) return "0" + n;
return n.toString();
}
function update() {
// Update the display time in the popup.
let displayTime = '0:00:00';
if (startTime != 0) {
let rawTime = Math.floor((Date.now() - startTime) / 1000);
if (endTime > 0) rawTime = Math.floor((endTime - startTime) / 1000);
displayTime = `${Math.floor(rawTime / 3600)}:${dis(Math.floor((rawTime % 3600) / 60))}:${dis(rawTime % 60)}`;
}
document.getElementById('time').innerHTML = displayTime;
setTimeout(update, 1000);
// Fetch and save new messages.
fetchMessages();
}
setTimeout(init, 20);

73
scripts/content.js Normal file
View File

@ -0,0 +1,73 @@
const seenMessages = new Set();
const seenDataAnnounceMessages = new Set();
let awaitingTimes = [];
let messages = [];
let startTime = 0;
async function init() {
const data = await chrome.storage.local.get(['startTime', 'messages']);
if (data?.startTime > 0) {
startTime = data.startTime;
}
if (data.messages) {
messages = data.messages;
}
checkNewMessages();
update();
}
function dis(n) {
if (n < 10) return "0" + n;
return n.toString();
}
function displayTime(rawTime) {
if (rawTime < 3600) return `${dis(Math.floor((rawTime % 3600) / 60))}:${dis(rawTime % 60)}`;
return `${Math.floor(rawTime / 3600)}:${dis(Math.floor((rawTime % 3600) / 60))}:${dis(rawTime % 60)}`;
}
function checkNewMessages() {
// Check announce messages and log their appearing times.
const announceMessageElements = document.querySelectorAll("div[data-announce-message]");
for (let e of announceMessageElements) {
if (seenDataAnnounceMessages.has(e)) continue;
seenDataAnnounceMessages.add(e);
const completionTime = Math.floor((Date.now() - startTime) / 1000);
awaitingTimes.push(completionTime);
}
// Check in-call messages.
const messageElements = document.querySelectorAll("div[data-message-id] div[jscontroller]");
for (let e of messageElements) {
if (seenMessages.has(e)) continue;
seenMessages.add(e);
let completionTime = Math.floor((Date.now() - startTime) / 1000);
// If awaiting times are available, use them first (i.e. the messages section just got opened).
if (awaitingTimes.length) completionTime = awaitingTimes.shift();
const messageNode = e.parentNode.parentNode.parentNode.parentNode;
const rootNode = messageNode.parentNode.parentNode;
const name = rootNode.firstChild.firstChild.innerHTML;
const dTime = displayTime(completionTime);
messages.push({completionTime: completionTime, name: name, text: e.innerHTML, displayTime: dTime});
e.innerHTML = `<span style="color: red; margin-right: 5px;">${dTime}</span>${e.innerHTML}`
}
chrome.storage.local.set({ messages: messages });
// If awaiting messages are still present, but the messages section is opened with no new messages,
// delete all awaiting messages (that should never happen though, there was an error somewhere).
if (messageElements.length && awaitingTimes.length) awaitingTimes = [];
setTimeout(checkNewMessages, 100);
}
async function update() {
const data = await chrome.storage.local.get(['startTime', 'endTime']);
if (data?.startTime >= 0 && data.startTime != startTime) {
startTime = data.startTime;
messages = [];
}
setTimeout(update, 2000);
}
setTimeout(init, 20);