Persist the last search session
Browse files- static/app.js +50 -0
static/app.js
CHANGED
|
@@ -89,6 +89,53 @@ const STATE = {
|
|
| 89 |
_pageCache: {},
|
| 90 |
_deferredAppendWhileDragging: false,
|
| 91 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
let searchAbortController = null;
|
| 93 |
let searchPrefetchAbortController = null;
|
| 94 |
let searchPrefetchKey = null;
|
|
@@ -1108,6 +1155,7 @@ const ROUTER = {
|
|
| 1108 |
renderFilters(routeId);
|
| 1109 |
}
|
| 1110 |
routeInitialized = true;
|
|
|
|
| 1111 |
},
|
| 1112 |
updateUI() {
|
| 1113 |
if (STATE.mode === "global") {
|
|
@@ -1180,6 +1228,7 @@ function syncStateToURL(replace = true) {
|
|
| 1180 |
} else {
|
| 1181 |
history.pushState(null, "", url);
|
| 1182 |
}
|
|
|
|
| 1183 |
}
|
| 1184 |
|
| 1185 |
function syncURLToState() {
|
|
@@ -3071,6 +3120,7 @@ function setupResultDelegation() {
|
|
| 3071 |
|
| 3072 |
async function init() {
|
| 3073 |
cacheDOM();
|
|
|
|
| 3074 |
setupReaderIntentWarming();
|
| 3075 |
STATE.isDark = localStorage.getItem("theme") !== "light";
|
| 3076 |
applyTheme();
|
|
|
|
| 89 |
_pageCache: {},
|
| 90 |
_deferredAppendWhileDragging: false,
|
| 91 |
};
|
| 92 |
+
|
| 93 |
+
const SEARCH_SESSION_DB = "voiceofml-search-state";
|
| 94 |
+
function openSearchSessionDB() {
|
| 95 |
+
return new Promise((resolve, reject) => {
|
| 96 |
+
if (!window.indexedDB) return reject(new Error("IndexedDB unavailable"));
|
| 97 |
+
const request = indexedDB.open(SEARCH_SESSION_DB, 1);
|
| 98 |
+
request.onupgradeneeded = () => {
|
| 99 |
+
if (!request.result.objectStoreNames.contains("session")) request.result.createObjectStore("session");
|
| 100 |
+
};
|
| 101 |
+
request.onsuccess = () => resolve(request.result);
|
| 102 |
+
request.onerror = () => reject(request.error);
|
| 103 |
+
});
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
async function persistSearchSession() {
|
| 107 |
+
if (location.pathname.startsWith("/static/reader.html")) return;
|
| 108 |
+
try {
|
| 109 |
+
const db = await openSearchSessionDB();
|
| 110 |
+
const transaction = db.transaction("session", "readwrite");
|
| 111 |
+
transaction.objectStore("session").put({ version: 1, url: location.href, updatedAt: Date.now() }, "last-search");
|
| 112 |
+
await new Promise((resolve, reject) => {
|
| 113 |
+
transaction.oncomplete = resolve;
|
| 114 |
+
transaction.onerror = () => reject(transaction.error);
|
| 115 |
+
transaction.onabort = () => reject(transaction.error);
|
| 116 |
+
});
|
| 117 |
+
db.close();
|
| 118 |
+
} catch (_) {}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
async function restoreSearchSession() {
|
| 122 |
+
if (location.pathname !== "/" || location.search || location.hash) return false;
|
| 123 |
+
try {
|
| 124 |
+
const db = await openSearchSessionDB();
|
| 125 |
+
const transaction = db.transaction("session", "readonly");
|
| 126 |
+
const request = transaction.objectStore("session").get("last-search");
|
| 127 |
+
const saved = await new Promise((resolve, reject) => {
|
| 128 |
+
request.onsuccess = () => resolve(request.result);
|
| 129 |
+
request.onerror = () => reject(request.error);
|
| 130 |
+
});
|
| 131 |
+
db.close();
|
| 132 |
+
if (!saved || saved.version !== 1 || typeof saved.url !== "string") return false;
|
| 133 |
+
const target = new URL(saved.url);
|
| 134 |
+
if (target.origin !== location.origin || target.pathname.split("/").filter(Boolean).length > 1 || target.pathname === "/static/reader.html") return false;
|
| 135 |
+
history.replaceState(null, "", target.href);
|
| 136 |
+
return true;
|
| 137 |
+
} catch (_) { return false; }
|
| 138 |
+
}
|
| 139 |
let searchAbortController = null;
|
| 140 |
let searchPrefetchAbortController = null;
|
| 141 |
let searchPrefetchKey = null;
|
|
|
|
| 1155 |
renderFilters(routeId);
|
| 1156 |
}
|
| 1157 |
routeInitialized = true;
|
| 1158 |
+
persistSearchSession();
|
| 1159 |
},
|
| 1160 |
updateUI() {
|
| 1161 |
if (STATE.mode === "global") {
|
|
|
|
| 1228 |
} else {
|
| 1229 |
history.pushState(null, "", url);
|
| 1230 |
}
|
| 1231 |
+
persistSearchSession();
|
| 1232 |
}
|
| 1233 |
|
| 1234 |
function syncURLToState() {
|
|
|
|
| 3120 |
|
| 3121 |
async function init() {
|
| 3122 |
cacheDOM();
|
| 3123 |
+
await restoreSearchSession();
|
| 3124 |
setupReaderIntentWarming();
|
| 3125 |
STATE.isDark = localStorage.getItem("theme") !== "light";
|
| 3126 |
applyTheme();
|