Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an options page with past runs and some controls #116

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
320093e
Add initial version of the chrome extension
saranshdhingra Apr 10, 2022
f2f68ed
Added minor changes to the extension service worker
saranshdhingra May 6, 2022
11a1e2b
Added EOF line in the extension files
saranshdhingra May 6, 2022
180d843
Merge branch 'main' into add-extension
saranshdhingra May 6, 2022
821fad8
Removed unused files, sync to 0.1.2
saranshdhingra May 6, 2022
4c496a8
Run eslint on service-worker.js
saranshdhingra Jun 3, 2022
bbffa91
Merge branch 'main' into add-extension
saranshdhingra Jun 3, 2022
da268dc
Save run data to chrome.storage
saranshdhingra Jun 3, 2022
10270d1
Added an Options page
saranshdhingra Jun 3, 2022
b56dd3b
Added material UI
saranshdhingra Jun 3, 2022
0a24440
Options page now shows the current status
saranshdhingra Jun 6, 2022
baf1d66
Bumped manifest version
saranshdhingra Jun 10, 2022
524eb68
Merged main into current branch
saranshdhingra Jun 10, 2022
950dadb
The current ping status is now synced to the options page in realtime
saranshdhingra Jun 10, 2022
79c1b77
Service worker now syncs the latest ping results to only the options tab
saranshdhingra Jun 10, 2022
6da8e0e
Added a circular progress bar in the current status
saranshdhingra Jun 17, 2022
523a76f
Added run and stop buttons in the options page
saranshdhingra Jun 17, 2022
10e939b
Running concurrent tests has no effect
saranshdhingra Jun 17, 2022
f1ae347
Merge branch 'main' into ext-save-run-results
saranshdhingra Jun 28, 2022
030d789
Merge branch 'main' into ext-save-run-results
saranshdhingra Jul 26, 2023
f03c4bd
Added missing license headers in a CSS file
saranshdhingra Jul 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext/css/material-components-web.min.css.map

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions ext/css/material-web-14.0.0.css

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions ext/css/options.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/material-components/material-components-web/blob/master/LICENSE
*/
:root {
saranshdhingra marked this conversation as resolved.
Show resolved Hide resolved
--mdc-theme-primary: rgb(26, 115, 232);
--mdc-theme-secondary: rgba(0, 0, 0, 0.04);
--icon-margin-horizontal: 5px;
}

body {
font-family: Roboto, sans-serif;
color: #485656;
margin: 0;
}

a,
a:visited {
color: inherit;
text-decoration: none;
}

.d-block {
display: block;
}

.d-flex {
display: flex;
}

.d-none {
display: none;
}

.flex-align-center {
align-items: center;
}

.flex-justify-center {
justify-content: center;
}

.mdc-top-app-bar__row {
width: 90%;
margin-left: auto;
margin-right: auto;
}

.mdc-top-app-bar__row .mdc-top-app-bar__section {
padding-left: 0;
padding-right: 0;
}

.logo-container {
display: inline-flex;
align-items: center;
padding: 0;
}

.logo-container .logo {
margin-right: 10px;
}

.app-bar-link {
display: inline-flex;
align-items: center;
margin: 0 10px;
}

.app-bar-link .material-icons {
margin-right: var(--icon-margin-horizontal);
}

.tabDetails .tabSingle {
display: none;
flex-direction: column;
align-items: center;
}

.tabDetails .tabSingle.tabActive {
display: flex;
}

#statusContainer .btns {
margin-top: 1rem;
}

#statusContainer.statusRunning .stopBtn,
#statusContainer:not(.statusRunning) .runBtn {
display: block;
}

#statusContainer:not(.statusRunning) .stopBtn,
#statusContainer.statusRunning .runBtn {
display: none;
}

#statusContainer .statusRow {
display: flex;
flex-direction: column;
align-items: center;
}

#statusContainer .statusRow .value {
text-transform: capitalize;
font-size: 1rem;
}

#progress {
width: 200px;
height: 200px;
position: relative;
}

#statusContainer:not(.statusRunning) .completedVal,
#statusContainer:not(.statusRunning) .totalVal {
display: none;
}

#progress .completedVal {
font-size: 2rem;
border-bottom: 2px solid var(--mdc-theme-primary);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -100%);
}

#progress .totalVal {
font-size: 2rem;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, 0);
}

#progress .mdc-circular-progress {
width: 100%;
height: 100%;
display: none;
}

#statusContainer.statusRunning .mdc-circular-progress {
display: block;
}
1 change: 1 addition & 0 deletions ext/js/material-web-14.0.0.js

Large diffs are not rendered by default.

201 changes: 201 additions & 0 deletions ext/js/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
window.onload = async function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a license header

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer addEventListener when registering an event listener.

initComponents();
showCurrentRuns();
updateCurrentRunningStatus(await getCurrentRunningStatus());
};

let currentProgress;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment for a global variable.


/**
* Message received from other parts of the extension
*/
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === "sync_ping_status") {
updateCurrentRunningStatus(request.currentStatus);
}
});

/**
* Loads and shows the latest runs in the options page
*/
async function showCurrentRuns() {
const runs = await getCurrentRuns();
const container = document.getElementById("runsListContainer");

runs.forEach(async (runStartTime) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer for (... of runs)

const row = document.createElement("tr");
row.classList.add("mdc-data-table__header-row");

const cell1 = document.createElement("td");
cell1.setAttribute("data-run-id", runStartTime);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using more descriptive names for cell1 and cell2. Maybe startTimeCell and latencyCell or something like that?

cell1.innerText = getFormattedTime(new Date(runStartTime));
cell1.classList.add("mdc-data-table__cell");

const cell2 = document.createElement("td");
const fastestRun = await getFastestPingInRun(runStartTime);
cell2.innerText = `${fastestRun.region} (${fastestRun.ping} ms)`;
cell2.setAttribute("data-run-id", runStartTime);
cell2.classList.add("mdc-data-table__cell");

row.appendChild(cell1);
row.appendChild(cell2);

container.appendChild(row);
});
}

/**
* Returns the information for the fastest region for a saved run recognized by a runId
* @param {int} runId
* @return {Object}
*/
async function getFastestPingInRun(runId) {
const data = await getRunData(runId);
let fastestRegion = null;

// loop therough the results of the run to find the fastest region
for (const region of Object.keys(data.results)) {
if (
fastestRegion === null ||
data.results[fastestRegion] > data.results[region]
) {
fastestRegion = region;
}
}

return {
region: fastestRegion,
ping: data.results[fastestRegion],
runId: runId,
};
}

/**
* Fetches the current stored runs from chrome.storage.local
* @return {Promise}
*/
async function getCurrentRuns() {
return new Promise((resolve, reject) => {
chrome.storage.local.get("runs", (result) => {
// return an empty array by default
resolve(result["runs"] ?? []);
});
});
}

/**
* Fetches the details of a single historical run from chrome.storage.local
* @param {object} runId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really an object? It seems to take a number.

*/
async function getRunData(runId) {
runId = "run-" + runId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this structure a bit confusing because the passed runId is a start time but also a run id, but you're reassigning the value with the "run-" prefix. Which format is the canonical run ID?

return new Promise((resolve, reject) => {
chrome.storage.local.get(runId, (result) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get returns a Promise.

// return an empty array by default
resolve(result[runId] ?? false);
});
});
}

/**
* Handler for when a tab is clicked
* @param {int} selectedIndex
*/
function focusTab(selectedIndex) {
const tabs = document
.querySelector(".tabDetails")
.querySelectorAll(".tabSingle");
[...tabs].forEach((tab, index) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NodeList has forEach since Chrome 51
https://developer.mozilla.org/en-US/docs/Web/API/NodeList

if (index === selectedIndex) {
tab.classList.add("tabActive");
} else {
tab.classList.remove("tabActive");
}
});
}

/**
* Helper to initialize all the material components
*/
function initComponents() {
new mdc.tabBar.MDCTabBar(document.querySelector(".mdc-tab-bar"));

currentProgress = new mdc.circularProgress.MDCCircularProgress(
document.getElementById("currentProgress")
);

currentProgress.progress = 0.8;

// Handle tab click
document
.querySelector(".mdc-tab-bar")
.addEventListener("MDCTabBar:activated", function (ev) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use () => instead of function

const tabIndex = ev?.detail?.index ?? 0;

focusTab(tabIndex);
});
}

/**
* Helper to return formatted time given a Date object
* @param {Date} dt
* @return {string}
*/
function getFormattedTime(dt) {
return (
new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(dt) +
", " +
new Intl.DateTimeFormat("en-US", { timeStyle: "medium" }).format(dt)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you specify the en-US locale instead of the user default?

);
}

/**
* Updates the current status for the ping test in the UI.
* @param {Object} currentStatus
*/
async function updateCurrentRunningStatus(currentStatus) {
const container = document.getElementById("statusContainer");

container.querySelector(".statusRow").querySelector(".value").innerText =
currentStatus.status;

if (currentStatus.status === "running") {
container.classList.add("statusRunning");
container.querySelector(".completedVal").innerText =
currentStatus.completed;
container.querySelector(".totalVal").innerText = currentStatus.total;

const ratio = currentStatus.completed / currentStatus.total;
currentProgress.progress = ratio;
} else {
container.classList.remove("statusRunning");
currentProgress.progress = 0;
}
}

/**
* Fetches the current status of the ping test
*/
async function getCurrentRunningStatus() {
return new Promise((resolve) => {
chrome.runtime.sendMessage(
{ action: "fetch_current_status" },
function (response) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, avoid function expressions.

resolve(response);
}
);
});
}

/**
* Event handlers for button clicks
*/
document.querySelector("button.runBtn").addEventListener("click", function (e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok to touch DOM here instead of inside of Load or DOMContentLoaded?

chrome.runtime.sendMessage({ action: "run_test" }, () => {});
});

document
.querySelector("button.stopBtn")
.addEventListener("click", function (e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, use arrow function.

chrome.runtime.sendMessage({ action: "stop_test" }, () => {});
});
Loading