Initial app
This commit is contained in:
parent
713e10f585
commit
1428aa9a26
19
manifest.json
Normal file
19
manifest.json
Normal 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
80
popup.html
Normal 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
110
popup.js
Normal 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
73
scripts/content.js
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user