Move common filters to the bar and keep the drawer for advanced ones

This commit is contained in:
Fisto
2026-09-17 08:22:32 +02:00
parent f5f3196382
commit 42b4bfd231
5 changed files with 134 additions and 129 deletions
+115 -117
View File
@@ -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();
}