Merge main (search rendering fixes) into integrate/all
This commit is contained in:
@@ -1894,7 +1894,9 @@ function startCacheResyncWatch() {
|
|||||||
// Pull a result set's artwork into the service worker's thumbnail cache, a few at
|
// Pull a result set's artwork into the service worker's thumbnail cache, a few at
|
||||||
// a time, so a saved search still has pictures offline.
|
// a time, so a saved search still has pictures offline.
|
||||||
function warmSearchThumbs(results) {
|
function warmSearchThumbs(results) {
|
||||||
const urls = (results || []).map((r) => r && r.thumbnail).filter(Boolean).slice(0, 100);
|
// Only the first screens: warming 100+ images competes with the results
|
||||||
|
// themselves on a slow link.
|
||||||
|
const urls = (results || []).map((r) => r && r.thumbnail).filter(Boolean).slice(0, 24);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
const next = () => { if (i >= urls.length) return; warmThumb(urls[i++]); setTimeout(next, 40); };
|
const next = () => { if (i >= urls.length) return; warmThumb(urls[i++]); setTimeout(next, 40); };
|
||||||
next(); next(); next();
|
next(); next(); next();
|
||||||
@@ -8067,6 +8069,9 @@ function wireUI() {
|
|||||||
collectSearchMetadata(q, instant.results);
|
collectSearchMetadata(q, instant.results);
|
||||||
if (SearchLibrary.isFresh(instant.ts)) { scrollListIntoViewPortrait(); return; }
|
if (SearchLibrary.isFresh(instant.ts)) { scrollListIntoViewPortrait(); return; }
|
||||||
} else {
|
} else {
|
||||||
|
// Never leave the previous search's "Updated … · N results" line over
|
||||||
|
// an empty list while this one loads.
|
||||||
|
$('searchMeta')?.classList.add('hidden');
|
||||||
render();
|
render();
|
||||||
showSearchSkeletons();
|
showSearchSkeletons();
|
||||||
}
|
}
|
||||||
@@ -8120,8 +8125,15 @@ function wireUI() {
|
|||||||
const more = await API.search(q);
|
const more = await API.search(q);
|
||||||
const full = merged(more?.results || []);
|
const full = merged(more?.results || []);
|
||||||
if (mySeq !== searchSeq || !more?.ok || full.length <= results.length) return;
|
if (mySeq !== searchSeq || !more?.ok || full.length <= results.length) return;
|
||||||
searchResults = full;
|
const shown = searchResults;
|
||||||
renderList();
|
const prefix = shown.length <= full.length && shown.every((v, i) => full[i] && full[i].id === v.id);
|
||||||
|
if (prefix && els.cards.children.length) {
|
||||||
|
// Grow the same array, so cards already drawn queue the full list too.
|
||||||
|
const from = shown.length;
|
||||||
|
shown.push(...full.slice(from));
|
||||||
|
searchResults = shown;
|
||||||
|
appendListCards(shown, from);
|
||||||
|
} else { searchResults = full; renderList(); }
|
||||||
RecentSearches.cacheResults(q, full, Date.now());
|
RecentSearches.cacheResults(q, full, Date.now());
|
||||||
showSearchMeta(q, Date.now());
|
showSearchMeta(q, Date.now());
|
||||||
} catch { /* keep the first page */ }
|
} catch { /* keep the first page */ }
|
||||||
|
|||||||
@@ -405,8 +405,38 @@ function renderList() {
|
|||||||
}
|
}
|
||||||
els.status.classList.add('hidden');
|
els.status.classList.add('hidden');
|
||||||
|
|
||||||
list.forEach((v, i) => els.cards.appendChild(renderCard(v, i, list)));
|
// Long lists (a deep search returns up to 200) are drawn in chunks: the first
|
||||||
|
// screenful at once, the rest on later frames, so a phone never freezes.
|
||||||
|
const token = (renderList._token = (renderList._token || 0) + 1);
|
||||||
|
const FIRST = 30, STEP = 40;
|
||||||
|
const draw = (from, to) => {
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
for (let i = from; i < Math.min(to, list.length); i++) frag.appendChild(renderCard(list[i], i, list));
|
||||||
|
els.cards.appendChild(frag);
|
||||||
|
};
|
||||||
|
draw(0, FIRST);
|
||||||
markPlayingCard();
|
markPlayingCard();
|
||||||
|
let at = FIRST;
|
||||||
|
const more = () => {
|
||||||
|
if (renderList._token !== token || at >= list.length) return;
|
||||||
|
draw(at, at + STEP); at += STEP; markPlayingCard();
|
||||||
|
(window.requestIdleCallback || ((f) => setTimeout(f, 30)))(more);
|
||||||
|
};
|
||||||
|
if (list.length > FIRST) (window.requestIdleCallback || ((f) => setTimeout(f, 30)))(more);
|
||||||
|
}
|
||||||
|
// Search only: the server's fuller list arrived and starts with the cards
|
||||||
|
// already shown — add the new ones below instead of redrawing everything.
|
||||||
|
function appendListCards(list, fromIndex) {
|
||||||
|
const token = renderList._token;
|
||||||
|
let at = fromIndex;
|
||||||
|
const step = () => {
|
||||||
|
if (renderList._token !== token || at >= list.length) return;
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
for (const end = Math.min(at + 40, list.length); at < end; at++) frag.appendChild(renderCard(list[at], at, list));
|
||||||
|
els.cards.appendChild(frag);
|
||||||
|
(window.requestIdleCallback || ((f) => setTimeout(f, 30)))(step);
|
||||||
|
};
|
||||||
|
step();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addBrowseCta(empty, label) {
|
function addBrowseCta(empty, label) {
|
||||||
|
|||||||
Reference in New Issue
Block a user