Initial app
This commit is contained in:
		
							
								
								
									
										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);
 | 
				
			||||||
		Reference in New Issue
	
	Block a user