Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions experimenter/experimenter/nimbus_ui/static/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as bootstrap from "bootstrap";
import "htmx.org";
import "bootstrap-select";
import { setupReadonlyJsonEditors } from "./codemirror_utils.js";
import { setupDevtoolsBanner } from "./nimbus_devtools.js";

window.bootstrap = bootstrap;
const setupThemeSwitcher = () => {
Expand Down Expand Up @@ -98,6 +99,7 @@ const setupHTMXLoadingOverlay = () => {
};

$(() => {
void setupDevtoolsBanner();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

out of curiosity how is void different from not having it here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Essentially no different. This is really a signal that we're calling an async function and not awaiting it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(if you do const x = void f() for some async function f, x will be undefined instead of a Promise`.)

setupThemeSwitcher();
setupTooltips();
setupToasts();
Expand Down
137 changes: 137 additions & 0 deletions experimenter/experimenter/nimbus_ui/static/js/nimbus_devtools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/* global cookieStore */

const AVAILABLE_NIMBUS_DEVTOOLS_VERSION = [0, 4, 0];
const COOKIE_NAME = "nimbus-devtools";
const ONE_DAY = 60 * 60 * 24;

function closeBanner(banner) {
banner.classList.add("d-none");
}

async function remindLater(banner) {
await cookieStore.set({
name: COOKIE_NAME,
sameSite: "strict",
maxAge: ONE_DAY,

value: JSON.stringify({
dismiss: true,
}),
});

closeBanner(banner);
}

async function remindNextVersion(banner) {
await cookieStore.set({
name: COOKIE_NAME,
sameSite: "strict",
value: JSON.stringify({
remindIfNewer: AVAILABLE_NIMBUS_DEVTOOLS_VERSION,
}),
});

closeBanner(banner);
}

async function dismiss(banner) {
await cookieStore.set({
name: "nimbus-devtools",
sameSite: "strict",
value: JSON.stringify({
dismiss: true,
}),
});

closeBanner(banner);
}

/**
* Compare two version 3-tuples.
*
* @param {[Number, Number, Number]} a The first version to compare.
* @param {[Number, Number, Number]} b The second version to compare.
*
* @returns {-1 | 0 | 1} -1 if a < b, 0 if a == b, 1 if a > b
*/
function versionCompare(a, b) {
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
}

async function getCookie() {
const cookie = await cookieStore.get("nimbus-devtools").catch(() => null);
if (cookie?.value) {
try {
return JSON.parse(cookie.value);
} catch (e) {
console.error(`Invalid cookie '${cookie}'`, e);
await cookieStore.delete("nimbus-devtools");
}
}

return null;
}

export async function setupDevtoolsBanner() {
const cookie = await getCookie();

if (cookie?.dismiss) {
return;
}

if (
cookie?.remindIfNewer &&
Array.isArray(cookie.remindIfNewer) &&
versionCompare(AVAILABLE_NIMBUS_DEVTOOLS_VERSION, cookie.remindIfNewer) <= 0
) {
return;
}

if (
window.__NIMBUS_DEVTOOLS__ &&
versionCompare(
AVAILABLE_NIMBUS_DEVTOOLS_VERSION,
window.__NIMBUS_DEVTOOLS__.version,
) <= 0
) {
return;
}

const banner = document.getElementById("nimbus-devtools-banner");

banner.classList.remove("d-none");

if (window.__NIMBUS_DEVTOOLS__?.version) {
banner.querySelector(".old-version-placeholder").textContent =
window.__NIMBUS_DEVTOOLS__.version.join(".");
banner
.querySelector(".nimbus-devtools-needs-update")
.classList.remove("d-none");
} else {
banner
.querySelector(".nimbus-devtools-needs-install")
.classList.remove("d-none");
}

banner
.querySelectorAll(".new-version-placeholder")
.forEach(
(el) => (el.textContent = AVAILABLE_NIMBUS_DEVTOOLS_VERSION.join(".")),
);

banner
.querySelector(".remind-later-btn")
.addEventListener("click", () => remindLater(banner));

banner
.querySelector(".remind-next-version-btn")
.addEventListener("click", () => remindNextVersion(banner));

banner
.querySelector(".dismiss-btn")
.addEventListener("click", () => dismiss(banner));

banner
.querySelector(".new-version-link")
.addEventListener("click", () => closeBanner(banner));
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% load static %}

{% include 'glean/opt_out_alert.html' %}
{% include "nimbus_devtools/banner.html" %}

<nav class="navbar navbar-expand-xl">
<div class="container-fluid">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div id="nimbus-devtools-banner"
class="alert alert-success bg-transparent text-success d-none">
<div class="nimbus-devtools-needs-install d-none">
<p>nimbus-devtools is either uninstalled or out-of-date.</p>
<p>
You can download the latest version of nimbus-devtools:
<a href="https://github.com/mozilla-extensions/nimbus-devtools/releases/latest"
target="_blank"
class="new-version-link">v<span class="new-version-placeholder"></span></a>
</p>
</div>
<div class="nimbus-devtools-needs-update d-none">
<p>
Your version of nimbus-devtools (v<span class="old-version-placeholder"></span>) is out of date.
</p>
<p>
A new version is available:
<a href="https://github.com/mozilla-extensions/nimbus-devtools/releases/latest"
target="_blank"
class="new-version-link">v<span class="new-version-placeholder"></span></a>
</p>
</div>
<button class="btn btn-primary me-2 remind-later-btn">Remind me tomorrow</button>
<button class="btn btn-danger me-2 remind-next-version-btn">Don't remind me about this version</button>
<button class="btn btn-danger me-2 dismiss-btn">Dismiss</button>
</div>
Loading