app/tabs.mjs (157 lines of code) (raw):
import { _, __, cloneTemplate, updateTemplate } from "util";
import * as BugList from "buglist";
import * as BugTable from "bugtable";
import * as Bugzilla from "bugzilla";
import * as Dialog from "dialog";
import * as Global from "global";
function addTab(tab, $tabGroup) {
let $tab = cloneTemplate(_("#tab-template"));
updateTemplate($tab, tab);
$tab = $tab.firstElementChild;
$tab.dataset.tab = tab.name;
$tabGroup.append($tab);
const $content = cloneTemplate(_("#tab-content-template"));
updateTemplate($content, {
outer: `tab-${tab.name}`,
inner: `${tab.name}-content`,
});
_("#tabs-content").append($content);
document.addEventListener(`tab.${tab.name}`, () => {
const $container = _(`#tab-${tab.name}`);
for (const $buglist of __($container, ".buglist-container")) {
BugList.updateQuery($buglist.id);
}
for (const $bugtable of __($container, ".bugtable-container")) {
BugTable.refresh($bugtable.id);
}
});
return $tab;
}
function addTabGroup(tabs) {
const $tabGroup = document.createElement("div");
$tabGroup.classList.add("tab-group");
for (const tab of tabs) {
addTab(tab, $tabGroup);
}
_("#refresh-all-button").before($tabGroup);
}
function addComponentsTab(tab) {
const $tabGroup = _("#components-tab-group");
const $tab = addTab(tab, $tabGroup);
$tab.classList.add("disabled");
}
function addTabs() {
addComponentsTab({
name: "triage",
title: "Triage",
});
addComponentsTab({
name: "important",
title: "Important",
});
addComponentsTab({
name: "stalled",
title: "Stalled & Longstanding",
});
addComponentsTab({
name: "overview",
title: "Overview",
});
addTabGroup([
{
name: "reo",
title: "Regressions Triage",
},
{
name: "tracked",
title: "Tracked",
},
]);
}
function updateAuth() {
const account = Global.getAccount();
if (account) {
_("#username").textContent = account.real_name;
_("#nav #key-button").classList.add("authenticated");
} else {
_("#username").textContent = "";
_("#nav #key-button").classList.remove("authenticated");
}
}
export function initUI() {
addTabs();
updateAuth();
_("#nav").addEventListener("click", async (event) => {
const $selected = event.target.closest(".tab");
if (!$selected || !$selected.dataset.tab) return;
await switchTo($selected);
});
_("#nav #refresh-all-button").addEventListener("click", (event) => {
if (event.shiftKey) {
Global.clearComponentsCache();
window.location.reload();
} else {
document.dispatchEvent(new Event("refresh"));
}
});
_("#nav #key-button").addEventListener("click", async () => {
const oldApiKey = Bugzilla.getApiKey();
let account;
for (;;) {
const prefix = oldApiKey ? "Replace" : "Set";
let key = await Dialog.prompt(`${prefix} Bugzilla API-Key:`);
if (key === false) return;
key = key.trim();
Bugzilla.setApiKey(key.trim());
if (key === "") break;
Dialog.showSpinner("Verifying API-Key");
const res = await Bugzilla.whoami();
Dialog.hideSpinner();
if (res === undefined) {
Bugzilla.setApiKey(oldApiKey);
await Dialog.alert("Invalid Bugzilla API-Key");
} else {
account = res;
break;
}
}
Global.setAccount(account);
updateAuth();
document.dispatchEvent(new Event("refresh"));
});
}
export async function switchTo($tab) {
if ($tab.closest("#components-tab-group") && $tab.dataset.tab !== "components") {
const components = Global.selectedComponents();
if (components.length === 0) {
await Dialog.alert("No components selected.");
return;
}
if (components.length >= 50) {
await Dialog.alert(
"Too many components selected. Please select fewer than 50.",
);
return;
}
}
for (const $t of __(".tab.selected")) {
$t.classList.remove("selected");
}
$tab.classList.add("selected");
// change visible content
for (const $content of __(".content.selected")) {
$content.classList.remove("selected");
}
const selectedTab = $tab.dataset.tab;
_(`#tab-${selectedTab}`).classList.add("selected");
// update doc hash and title
if (selectedTab === "components") {
history.pushState("", "", "/");
} else {
document.location.hash = `tab.${selectedTab}`;
}
const title = $tab.textContent.trim().replace(/ \(\d+\)/, "");
document.title = `BugDash - ${title}`;
// notify tab
document.dispatchEvent(new Event(`tab.${selectedTab}`));
}