This commit is contained in:
parent
c88d1886c9
commit
7f74aaced8
194
static/app.js
194
static/app.js
@ -56,13 +56,28 @@
|
|||||||
const graphModalClose = document.getElementById("graphModalClose");
|
const graphModalClose = document.getElementById("graphModalClose");
|
||||||
const channelMap = new Map();
|
const channelMap = new Map();
|
||||||
const transcriptCache = new Map();
|
const transcriptCache = new Map();
|
||||||
|
const SETTINGS_KEY = "tlc-search-settings";
|
||||||
|
const DEFAULT_SETTINGS = {
|
||||||
|
channel: "",
|
||||||
|
year: "",
|
||||||
|
sort: "relevant",
|
||||||
|
size: "10",
|
||||||
|
exact: true,
|
||||||
|
fuzzy: true,
|
||||||
|
phrase: true,
|
||||||
|
external: false,
|
||||||
|
queryString: false,
|
||||||
|
};
|
||||||
|
let settings = loadSettings();
|
||||||
let lastFocusBeforeModal = null;
|
let lastFocusBeforeModal = null;
|
||||||
let pendingChannelSelection = "";
|
let pendingChannelSelection = "";
|
||||||
let channelsReady = false;
|
let channelsReady = false;
|
||||||
let previousToggleState = { exact: true, fuzzy: true, phrase: true };
|
let previousToggleState = {
|
||||||
let currentPage =
|
exact: settings.exact,
|
||||||
parseInt(qs.get("page") || "0", 10) ||
|
fuzzy: settings.fuzzy,
|
||||||
0;
|
phrase: settings.phrase,
|
||||||
|
};
|
||||||
|
let currentPage = 0;
|
||||||
|
|
||||||
function toggleAboutPanel(show) {
|
function toggleAboutPanel(show) {
|
||||||
if (!aboutPanel) return;
|
if (!aboutPanel) return;
|
||||||
@ -73,13 +88,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseBoolParam(name, defaultValue) {
|
|
||||||
const raw = qs.get(name);
|
|
||||||
if (raw === null) return defaultValue;
|
|
||||||
const lowered = raw.toLowerCase();
|
|
||||||
return !["0", "false", "no"].includes(lowered);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseChannelParam(params) {
|
function parseChannelParam(params) {
|
||||||
if (!params) return "";
|
if (!params) return "";
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
@ -102,6 +110,69 @@
|
|||||||
return first || "";
|
return first || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadSettings() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(SETTINGS_KEY);
|
||||||
|
if (!raw) return { ...DEFAULT_SETTINGS };
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return { ...DEFAULT_SETTINGS, ...parsed };
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to load settings", err);
|
||||||
|
return { ...DEFAULT_SETTINGS };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistSettings() {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to persist settings", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyStoredSettings() {
|
||||||
|
yearSel.value = settings.year || "";
|
||||||
|
sortSel.value = settings.sort || "relevant";
|
||||||
|
sizeSel.value = settings.size || "10";
|
||||||
|
exactToggle.checked = settings.exact;
|
||||||
|
fuzzyToggle.checked = settings.fuzzy;
|
||||||
|
phraseToggle.checked = settings.phrase;
|
||||||
|
if (externalToggle) {
|
||||||
|
externalToggle.checked = settings.external;
|
||||||
|
}
|
||||||
|
if (queryToggle) {
|
||||||
|
queryToggle.checked = settings.queryString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentTogglePreferences() {
|
||||||
|
if (queryToggle && queryToggle.checked) {
|
||||||
|
return { ...previousToggleState };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
exact: !!exactToggle.checked,
|
||||||
|
fuzzy: !!fuzzyToggle.checked,
|
||||||
|
phrase: !!phraseToggle.checked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSettingsFromControls() {
|
||||||
|
const togglePrefs = currentTogglePreferences();
|
||||||
|
const next = {
|
||||||
|
...settings,
|
||||||
|
channel: channelSelect ? channelSelect.value || "" : "",
|
||||||
|
year: yearSel.value || "",
|
||||||
|
sort: sortSel.value || "relevant",
|
||||||
|
size: sizeSel.value || "10",
|
||||||
|
external: externalToggle ? !!externalToggle.checked : false,
|
||||||
|
queryString: queryToggle ? !!queryToggle.checked : false,
|
||||||
|
...togglePrefs,
|
||||||
|
};
|
||||||
|
settings = next;
|
||||||
|
persistSettings();
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
function getSelectedChannels() {
|
function getSelectedChannels() {
|
||||||
if (!channelSelect) return [];
|
if (!channelSelect) return [];
|
||||||
const value = channelSelect.value;
|
const value = channelSelect.value;
|
||||||
@ -130,20 +201,18 @@
|
|||||||
|
|
||||||
function setFromQuery() {
|
function setFromQuery() {
|
||||||
qInput.value = qs.get("q") || "";
|
qInput.value = qs.get("q") || "";
|
||||||
yearSel.value = qs.get("year") || "";
|
const urlChannel = parseChannelParam(qs);
|
||||||
sortSel.value = qs.get("sort") || "relevant";
|
if (urlChannel) {
|
||||||
sizeSel.value = qs.get("size") || "10";
|
pendingChannelSelection = urlChannel;
|
||||||
pendingChannelSelection = parseChannelParam(qs);
|
settings.channel = urlChannel;
|
||||||
|
persistSettings();
|
||||||
|
} else {
|
||||||
|
pendingChannelSelection = settings.channel || "";
|
||||||
|
}
|
||||||
|
applyStoredSettings();
|
||||||
if (channelSelect) {
|
if (channelSelect) {
|
||||||
channelSelect.value = pendingChannelSelection || "";
|
channelSelect.value = pendingChannelSelection || "";
|
||||||
}
|
}
|
||||||
exactToggle.checked = parseBoolParam("exact", true);
|
|
||||||
fuzzyToggle.checked = parseBoolParam("fuzzy", true);
|
|
||||||
phraseToggle.checked = parseBoolParam("phrase", true);
|
|
||||||
if (externalToggle) {
|
|
||||||
externalToggle.checked = parseBoolParam("external", false);
|
|
||||||
}
|
|
||||||
queryToggle.checked = parseBoolParam("query_string", false);
|
|
||||||
applyQueryMode();
|
applyQueryMode();
|
||||||
rememberToggleState();
|
rememberToggleState();
|
||||||
}
|
}
|
||||||
@ -157,6 +226,8 @@
|
|||||||
fuzzy: fuzzyToggle.checked,
|
fuzzy: fuzzyToggle.checked,
|
||||||
phrase: phraseToggle.checked,
|
phrase: phraseToggle.checked,
|
||||||
};
|
};
|
||||||
|
settings = { ...settings, ...previousToggleState };
|
||||||
|
persistSettings();
|
||||||
}
|
}
|
||||||
exactToggle.checked = false;
|
exactToggle.checked = false;
|
||||||
fuzzyToggle.checked = false;
|
fuzzyToggle.checked = false;
|
||||||
@ -172,6 +243,8 @@
|
|||||||
fuzzyToggle.checked = previousToggleState.fuzzy;
|
fuzzyToggle.checked = previousToggleState.fuzzy;
|
||||||
phraseToggle.checked = previousToggleState.phrase;
|
phraseToggle.checked = previousToggleState.phrase;
|
||||||
}
|
}
|
||||||
|
settings.queryString = !!(queryToggle && queryToggle.checked);
|
||||||
|
persistSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function rememberToggleState() {
|
function rememberToggleState() {
|
||||||
@ -181,6 +254,8 @@
|
|||||||
fuzzy: !!fuzzyToggle.checked,
|
fuzzy: !!fuzzyToggle.checked,
|
||||||
phrase: !!phraseToggle.checked,
|
phrase: !!phraseToggle.checked,
|
||||||
};
|
};
|
||||||
|
settings = { ...settings, ...previousToggleState };
|
||||||
|
persistSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,6 +388,8 @@
|
|||||||
} else {
|
} else {
|
||||||
channelSelect.value = "";
|
channelSelect.value = "";
|
||||||
}
|
}
|
||||||
|
settings.channel = channelSelect.value || "";
|
||||||
|
persistSettings();
|
||||||
|
|
||||||
channelsReady = true;
|
channelsReady = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -322,25 +399,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUrl(q, sort, channels, year, page, size, exact, fuzzy, phrase, queryMode, includeExternal) {
|
function updateUrl(q) {
|
||||||
const next = new URL(window.location.href);
|
const next = new URL(window.location.href);
|
||||||
next.searchParams.set("q", q);
|
if (q) {
|
||||||
next.searchParams.set("sort", sort);
|
next.searchParams.set("q", q);
|
||||||
|
} else {
|
||||||
|
next.searchParams.delete("q");
|
||||||
|
}
|
||||||
|
next.searchParams.delete("page");
|
||||||
|
next.searchParams.delete("sort");
|
||||||
next.searchParams.delete("channel_id");
|
next.searchParams.delete("channel_id");
|
||||||
next.searchParams.delete("channel");
|
next.searchParams.delete("channel");
|
||||||
channels.forEach((id) => next.searchParams.append("channel_id", id));
|
next.searchParams.delete("year");
|
||||||
if (year) {
|
next.searchParams.delete("size");
|
||||||
next.searchParams.set("year", year);
|
next.searchParams.delete("exact");
|
||||||
} else {
|
next.searchParams.delete("fuzzy");
|
||||||
next.searchParams.delete("year");
|
next.searchParams.delete("phrase");
|
||||||
}
|
next.searchParams.delete("query_string");
|
||||||
next.searchParams.set("page", page);
|
next.searchParams.delete("external");
|
||||||
next.searchParams.set("size", size);
|
|
||||||
next.searchParams.set("exact", exact ? "1" : "0");
|
|
||||||
next.searchParams.set("fuzzy", fuzzy ? "1" : "0");
|
|
||||||
next.searchParams.set("phrase", phrase ? "1" : "0");
|
|
||||||
next.searchParams.set("query_string", queryMode ? "1" : "0");
|
|
||||||
next.searchParams.set("external", includeExternal ? "1" : "0");
|
|
||||||
history.pushState({}, "", next.toString());
|
history.pushState({}, "", next.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1542,24 +1618,14 @@ async function updateFrequencyChart(term, channels, year, queryMode, toggles = {
|
|||||||
fuzzy,
|
fuzzy,
|
||||||
phrase,
|
phrase,
|
||||||
};
|
};
|
||||||
|
settings = { ...settings, ...previousToggleState };
|
||||||
|
persistSettings();
|
||||||
}
|
}
|
||||||
const page = pageOverride != null ? pageOverride : currentPage;
|
const page = pageOverride != null ? pageOverride : currentPage;
|
||||||
currentPage = page;
|
currentPage = page;
|
||||||
|
|
||||||
if (pushState) {
|
if (pushState) {
|
||||||
updateUrl(
|
updateUrl(q);
|
||||||
q,
|
|
||||||
sort,
|
|
||||||
channels,
|
|
||||||
year,
|
|
||||||
page,
|
|
||||||
size,
|
|
||||||
exact,
|
|
||||||
fuzzy,
|
|
||||||
phrase,
|
|
||||||
queryMode,
|
|
||||||
includeExternal
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@ -1575,6 +1641,8 @@ async function updateFrequencyChart(term, channels, year, queryMode, toggles = {
|
|||||||
channels.forEach((id) => params.append("channel_id", id));
|
channels.forEach((id) => params.append("channel_id", id));
|
||||||
if (year) params.set("year", year);
|
if (year) params.set("year", year);
|
||||||
|
|
||||||
|
syncSettingsFromControls();
|
||||||
|
|
||||||
const res = await fetch(`/api/search?${params.toString()}`);
|
const res = await fetch(`/api/search?${params.toString()}`);
|
||||||
const payload = await res.json();
|
const payload = await res.json();
|
||||||
renderResults(payload, page);
|
renderResults(payload, page);
|
||||||
@ -1598,31 +1666,39 @@ async function updateFrequencyChart(term, channels, year, queryMode, toggles = {
|
|||||||
if (channelSelect) {
|
if (channelSelect) {
|
||||||
channelSelect.addEventListener("change", () => {
|
channelSelect.addEventListener("change", () => {
|
||||||
pendingChannelSelection = channelSelect.value || "";
|
pendingChannelSelection = channelSelect.value || "";
|
||||||
|
settings.channel = pendingChannelSelection;
|
||||||
|
persistSettings();
|
||||||
if (channelsReady) {
|
if (channelsReady) {
|
||||||
runSearch(0);
|
runSearch(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
yearSel.addEventListener("change", () => runSearch(0));
|
yearSel.addEventListener("change", () => { syncSettingsFromControls(); runSearch(0); });
|
||||||
sortSel.addEventListener("change", () => runSearch(0));
|
sortSel.addEventListener("change", () => { syncSettingsFromControls(); runSearch(0); });
|
||||||
sizeSel.addEventListener("change", () => runSearch(0));
|
sizeSel.addEventListener("change", () => { syncSettingsFromControls(); runSearch(0); });
|
||||||
exactToggle.addEventListener("change", () => { rememberToggleState(); runSearch(0); });
|
exactToggle.addEventListener("change", () => { rememberToggleState(); syncSettingsFromControls(); runSearch(0); });
|
||||||
fuzzyToggle.addEventListener("change", () => { rememberToggleState(); runSearch(0); });
|
fuzzyToggle.addEventListener("change", () => { rememberToggleState(); syncSettingsFromControls(); runSearch(0); });
|
||||||
phraseToggle.addEventListener("change", () => { rememberToggleState(); runSearch(0); });
|
phraseToggle.addEventListener("change", () => { rememberToggleState(); syncSettingsFromControls(); runSearch(0); });
|
||||||
if (externalToggle) {
|
if (externalToggle) {
|
||||||
externalToggle.addEventListener("change", () => {
|
externalToggle.addEventListener("change", () => {
|
||||||
pendingChannelSelection = "";
|
pendingChannelSelection = "";
|
||||||
|
settings.external = !!externalToggle.checked;
|
||||||
|
persistSettings();
|
||||||
loadChannels().then(() => runSearch(0));
|
loadChannels().then(() => runSearch(0));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (queryToggle) {
|
if (queryToggle) {
|
||||||
queryToggle.addEventListener("change", () => { applyQueryMode(); runSearch(0); });
|
queryToggle.addEventListener("change", () => {
|
||||||
|
applyQueryMode();
|
||||||
|
syncSettingsFromControls();
|
||||||
|
runSearch(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("popstate", () => {
|
window.addEventListener("popstate", () => {
|
||||||
qs = new URLSearchParams(window.location.search);
|
qs = new URLSearchParams(window.location.search);
|
||||||
setFromQuery();
|
setFromQuery();
|
||||||
currentPage = parseInt(qs.get("page") || "0", 10) || 0;
|
currentPage = 0;
|
||||||
runSearch(currentPage, false);
|
runSearch(currentPage, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user