Move common filters to the bar and keep the drawer for advanced ones
This commit is contained in:
+115
-117
@@ -622,22 +622,6 @@
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function renderToolbar() {
|
||||
toolbar.replaceChildren();
|
||||
if (state.view !== "media") {
|
||||
toolbar.append(el("span", null, "Slopbot is the dedicated bot core. Setup: docs/slopbot.md."));
|
||||
return;
|
||||
}
|
||||
|
||||
toolbar.append(button("Advanced search", toggleFilterDrawer));
|
||||
toolbar.append(metaMenu());
|
||||
toolbar.append(el("span", "vk-media-spacer"));
|
||||
toolbar.append(button("History", openHistoryModal));
|
||||
toolbar.append(button("New scrape…", openScrapeModal));
|
||||
}
|
||||
|
||||
// ---- Advanced search drawer ----
|
||||
|
||||
function defaultFilters() {
|
||||
return {
|
||||
source: "",
|
||||
@@ -652,112 +636,151 @@
|
||||
};
|
||||
}
|
||||
|
||||
function toggleFilterDrawer() {
|
||||
filterDrawer.hidden = !filterDrawer.hidden;
|
||||
if (!filterDrawer.hidden) {
|
||||
state.draft = {
|
||||
kind: state.kind,
|
||||
...state.filters,
|
||||
authorIds: [...state.filters.authorIds]
|
||||
};
|
||||
renderFilterDrawer();
|
||||
function ensureDraft() {
|
||||
if (!state.draft) {
|
||||
state.draft = { kind: state.kind, ...state.filters, authorIds: [...state.filters.authorIds] };
|
||||
}
|
||||
}
|
||||
|
||||
function renderFilterDrawer() {
|
||||
filterDrawer.replaceChildren();
|
||||
const draft = state.draft;
|
||||
|
||||
const section = (label) => {
|
||||
const wrap = el("div", "vk-media-filter-section");
|
||||
wrap.append(el("div", "vk-media-status", label));
|
||||
return wrap;
|
||||
function applyDraft() {
|
||||
state.kind = state.draft.kind;
|
||||
state.filters = {
|
||||
source: state.draft.source,
|
||||
channelId: state.draft.channelId,
|
||||
authorIds: [...state.draft.authorIds],
|
||||
origin: state.draft.origin,
|
||||
cachedOnly: state.draft.cachedOnly,
|
||||
aspect: state.draft.aspect,
|
||||
resolution: state.draft.resolution,
|
||||
after: state.draft.after,
|
||||
before: state.draft.before
|
||||
};
|
||||
filterDrawer.hidden = true;
|
||||
if (state.loaded) {
|
||||
void loadMedia(true);
|
||||
} else {
|
||||
void loadStats();
|
||||
}
|
||||
}
|
||||
|
||||
/** Compact multi-select for the posters, shown in the bar. */
|
||||
function peopleMenu() {
|
||||
const wrap = el("span", "vk-media-meta");
|
||||
const panel = el("div", "vk-media-meta-menu vk-media-people-menu");
|
||||
panel.hidden = true;
|
||||
|
||||
const toggle = button(
|
||||
`People${state.draft.authorIds.length ? ` (${state.draft.authorIds.length})` : ""}`,
|
||||
() => {
|
||||
panel.hidden = !panel.hidden;
|
||||
}
|
||||
);
|
||||
|
||||
const list = el("div", "vk-media-people");
|
||||
for (const author of state.authors ?? []) {
|
||||
if (!author.id) continue;
|
||||
const row = el("label", "vk-media-check");
|
||||
const box = document.createElement("input");
|
||||
box.type = "checkbox";
|
||||
box.checked = state.draft.authorIds.includes(author.id);
|
||||
box.addEventListener("change", () => {
|
||||
state.draft.authorIds = box.checked
|
||||
? [...new Set([...state.draft.authorIds, author.id])]
|
||||
: state.draft.authorIds.filter(id => id !== author.id);
|
||||
panel.hidden = true;
|
||||
renderToolbar();
|
||||
});
|
||||
row.append(box, document.createTextNode(` ${author.name} (${author.count})`));
|
||||
list.append(row);
|
||||
}
|
||||
if (!list.childElementCount) list.append(el("span", "vk-media-status", "No posters indexed yet."));
|
||||
|
||||
panel.append(list);
|
||||
wrap.append(toggle, panel);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function renderToolbar() {
|
||||
toolbar.replaceChildren();
|
||||
if (state.view !== "media") {
|
||||
toolbar.append(el("span", null, "Slopbot is the dedicated bot core. Setup: docs/slopbot.md."));
|
||||
return;
|
||||
}
|
||||
|
||||
ensureDraft();
|
||||
|
||||
// Filetype
|
||||
const kinds = section("Filetype");
|
||||
const chips = el("div", "vk-media-toolbar");
|
||||
for (const kind of KINDS) {
|
||||
const chip = el(
|
||||
"button",
|
||||
`vk-chip ${draft.kind === kind ? "vk-chip-on" : "vk-chip-off"}`,
|
||||
`vk-chip ${state.draft.kind === kind ? "vk-chip-on" : "vk-chip-off"}`,
|
||||
KIND_LABELS[kind]
|
||||
);
|
||||
chip.type = "button";
|
||||
chip.addEventListener("click", () => {
|
||||
draft.kind = kind;
|
||||
renderFilterDrawer();
|
||||
state.draft.kind = kind;
|
||||
renderToolbar();
|
||||
});
|
||||
chips.append(chip);
|
||||
toolbar.append(chip);
|
||||
}
|
||||
kinds.append(chips);
|
||||
filterDrawer.append(kinds);
|
||||
|
||||
// Source kind
|
||||
const sourceSection = section("Source kind");
|
||||
sourceSection.append(
|
||||
toolbar.append(
|
||||
filterSelect(
|
||||
[
|
||||
["", "Any"],
|
||||
["", "Any source"],
|
||||
["attachment", "Attachments"],
|
||||
["sticker", "Stickers"],
|
||||
["embed", "Embeds"]
|
||||
],
|
||||
draft.source,
|
||||
state.draft.source,
|
||||
value => {
|
||||
draft.source = value;
|
||||
state.draft.source = value;
|
||||
}
|
||||
)
|
||||
);
|
||||
filterDrawer.append(sourceSection);
|
||||
|
||||
// Server or DM
|
||||
const originSection = section("Source");
|
||||
originSection.append(
|
||||
toolbar.append(
|
||||
filterSelect(
|
||||
[
|
||||
["", "Anywhere"],
|
||||
["guild", "Server channels"],
|
||||
["dm", "DMs and group chats"]
|
||||
],
|
||||
draft.origin,
|
||||
state.draft.origin,
|
||||
value => {
|
||||
draft.origin = value;
|
||||
state.draft.origin = value;
|
||||
}
|
||||
)
|
||||
);
|
||||
filterDrawer.append(originSection);
|
||||
|
||||
// People (multi-select)
|
||||
const peopleSection = section("People");
|
||||
const people = el("div", "vk-media-people");
|
||||
for (const author of state.authors ?? []) {
|
||||
if (!author.id) continue;
|
||||
const row = el("label", "vk-media-check");
|
||||
const box = document.createElement("input");
|
||||
box.type = "checkbox";
|
||||
box.checked = draft.authorIds.includes(author.id);
|
||||
box.addEventListener("change", () => {
|
||||
draft.authorIds = box.checked
|
||||
? [...new Set([...draft.authorIds, author.id])]
|
||||
: draft.authorIds.filter(id => id !== author.id);
|
||||
});
|
||||
row.append(box, document.createTextNode(` ${author.name} (${author.count})`));
|
||||
people.append(row);
|
||||
}
|
||||
if (!people.childElementCount) people.append(el("span", "vk-media-status", "No posters indexed yet."));
|
||||
peopleSection.append(people);
|
||||
filterDrawer.append(peopleSection);
|
||||
toolbar.append(peopleMenu());
|
||||
toolbar.append(button("Advanced search", toggleFilterDrawer));
|
||||
toolbar.append(metaMenu());
|
||||
toolbar.append(el("span", "vk-media-spacer"));
|
||||
toolbar.append(button("Apply filters", applyDraft));
|
||||
toolbar.append(button("Reset", resetMediaView));
|
||||
toolbar.append(button("History", openHistoryModal));
|
||||
toolbar.append(button("New scrape…", openScrapeModal));
|
||||
}
|
||||
|
||||
// Advanced
|
||||
const advanced = document.createElement("details");
|
||||
advanced.className = "vk-media-advanced";
|
||||
const summary = document.createElement("summary");
|
||||
summary.textContent = "Advanced";
|
||||
advanced.append(summary);
|
||||
function toggleFilterDrawer() {
|
||||
filterDrawer.hidden = !filterDrawer.hidden;
|
||||
if (!filterDrawer.hidden) renderFilterDrawer();
|
||||
}
|
||||
|
||||
const advancedBody = el("div", "vk-media-filter-section");
|
||||
advancedBody.append(
|
||||
/** The drawer holds the less common filters; the basic ones live in the bar. */
|
||||
function renderFilterDrawer() {
|
||||
ensureDraft();
|
||||
filterDrawer.replaceChildren();
|
||||
const draft = state.draft;
|
||||
|
||||
const section = label => {
|
||||
const wrap = el("div", "vk-media-filter-section");
|
||||
wrap.append(el("div", "vk-media-status", label));
|
||||
return wrap;
|
||||
};
|
||||
|
||||
const filters = section("Filters");
|
||||
filters.append(
|
||||
filterSelect(
|
||||
[
|
||||
["", "Any ratio"],
|
||||
@@ -810,9 +833,9 @@
|
||||
channelSelect.addEventListener("change", () => {
|
||||
draft.channelId = channelSelect.value;
|
||||
});
|
||||
advancedBody.append(channelSelect);
|
||||
filters.append(channelSelect);
|
||||
|
||||
advancedBody.append(
|
||||
filters.append(
|
||||
el("span", "vk-media-status", "From"),
|
||||
dateInput(draft.after, value => {
|
||||
draft.after = value;
|
||||
@@ -831,12 +854,10 @@
|
||||
draft.cachedOnly = cachedBox.checked;
|
||||
});
|
||||
cachedRow.append(cachedBox, document.createTextNode(" Show only cached previews"));
|
||||
advancedBody.append(cachedRow);
|
||||
filters.append(cachedRow);
|
||||
filterDrawer.append(filters);
|
||||
|
||||
advanced.append(advancedBody);
|
||||
filterDrawer.append(advanced);
|
||||
|
||||
// Zoom
|
||||
// Zoom applies live; it is not part of the filter draft.
|
||||
const zoomSection = section("Card size");
|
||||
const zoom = document.createElement("input");
|
||||
zoom.type = "range";
|
||||
@@ -852,34 +873,9 @@
|
||||
zoomSection.append(zoom);
|
||||
filterDrawer.append(zoomSection);
|
||||
|
||||
// Actions
|
||||
const actions = el("div", "vk-media-toolbar");
|
||||
actions.append(
|
||||
button("Apply filters", () => {
|
||||
state.kind = draft.kind;
|
||||
state.filters = {
|
||||
source: draft.source,
|
||||
channelId: draft.channelId,
|
||||
authorIds: [...draft.authorIds],
|
||||
origin: draft.origin,
|
||||
cachedOnly: draft.cachedOnly,
|
||||
aspect: draft.aspect,
|
||||
resolution: draft.resolution,
|
||||
after: draft.after,
|
||||
before: draft.before
|
||||
};
|
||||
filterDrawer.hidden = true;
|
||||
if (state.loaded) {
|
||||
void loadMedia(true);
|
||||
} else {
|
||||
void loadStats();
|
||||
}
|
||||
}),
|
||||
button("Reset", () => {
|
||||
resetMediaView();
|
||||
})
|
||||
filterDrawer.append(
|
||||
el("span", "vk-media-status", "Press Apply filters in the bar to run the search.")
|
||||
);
|
||||
filterDrawer.append(actions);
|
||||
}
|
||||
|
||||
/** Card width from the zoom slider: 2/3 current, current, up to a full row. */
|
||||
@@ -918,9 +914,11 @@
|
||||
state.prefetched = null;
|
||||
state.items = [];
|
||||
state.offset = 0;
|
||||
state.draft = null;
|
||||
filterDrawer.hidden = true;
|
||||
applyZoom();
|
||||
renderGrid();
|
||||
renderToolbar();
|
||||
void loadStats();
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,12 @@ try {
|
||||
step("broken images", broken);
|
||||
check("no broken images", broken === 0, broken);
|
||||
|
||||
await page.getByRole("button", { name: /^People/ }).click();
|
||||
step("people checkboxes", await page.locator(".vk-media-people input").count());
|
||||
await page.getByRole("button", { name: /^People/ }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Advanced search" }).click();
|
||||
await page.waitForSelector(".vk-media-drawer:not([hidden])", { timeout: 5000 });
|
||||
step("people checkboxes", await page.locator(".vk-media-people input").count());
|
||||
|
||||
await page.locator(".vk-media-advanced summary").click();
|
||||
await page.locator(".vk-media-check", { hasText: "Show only cached previews" }).locator("input").check();
|
||||
await page.getByRole("button", { name: "Apply filters" }).click();
|
||||
await page.waitForTimeout(2500);
|
||||
|
||||
Reference in New Issue
Block a user