include USWResults in normal results

This commit is contained in:
Gusted 2021-04-06 15:35:35 +02:00
parent 63ebd1e0aa
commit b34bc513d4
No known key found for this signature in database
GPG Key ID: FD821B732837125F
4 changed files with 64 additions and 190 deletions

View File

@ -1275,10 +1275,6 @@
"message": "Also search global styles", "message": "Also search global styles",
"description": "Checkbox label in the popup's inline style search, shown when the text to search is entered" "description": "Checkbox label in the popup's inline style search, shown when the text to search is entered"
}, },
"searchUserStylesWorld": {
"message": "Search in Userstyles world",
"description": "Checkbox label in the popup's inline style search, shown when the text to search is entered"
},
"searchNumberOfResults": { "searchNumberOfResults": {
"message": "Number of matches", "message": "Number of matches",
"description": "Tooltip for the number of found search results in the editor shown on Ctrl-F" "description": "Tooltip for the number of found search results in the editor shown on Ctrl-F"

View File

@ -37,7 +37,6 @@
'popup.autoResort': false, // auto resort styles after toggling 'popup.autoResort': false, // auto resort styles after toggling
'popup.borders': false, // add white borders on the sides 'popup.borders': false, // add white borders on the sides
'popup.findStylesInline': true, // use the inline style search 'popup.findStylesInline': true, // use the inline style search
'popup.searchUserStylesWorld': false, // use userstyles world instead of uso-archive for inline searching.
'manage.onlyEnabled': false, // display only enabled styles 'manage.onlyEnabled': false, // display only enabled styles
'manage.onlyLocal': false, // display only styles created locally 'manage.onlyLocal': false, // display only styles created locally

View File

@ -252,13 +252,6 @@
</span> </span>
<span i18n-text="searchGlobalStyles"></span> <span i18n-text="searchGlobalStyles"></span>
</label> </label>
<label>
<span class="checkbox-container">
<input id="search-usw" type="checkbox" checked>
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
</span>
<span i18n-text="searchUserStylesWorld"></span>
</label>
</div> </div>
<div id="search-results-list"></div> <div id="search-results-list"></div>
<div class="search-results-nav" data-type="bottom"></div> <div class="search-results-nav" data-type="bottom"></div>

View File

@ -34,28 +34,14 @@
* @prop {string} an - authorName * @prop {string} an - authorName
* @prop {string} sn - screenshotName * @prop {string} sn - screenshotName
* @prop {boolean} sa - screenshotArchived * @prop {boolean} sa - screenshotArchived
* @prop {bolean} uw - is USw style
*/ */
/** @type IndexEntry[] */ /** @type IndexEntry[] */
let results; let results;
/** @type IndexEntry[] */ /** @type IndexEntry[] */
let index; let index;
/**
* @typedef USWIndexEntry
* @prop {Number} id - id
* @prop {string} name - name
* @prop {string} username - authorName
* @prop {string} description - description
* @prop {string} preview - screenshot
*/
/**
* @type {Object.<string, USWIndexEntry[]>}
*/
const USWIndex = {};
/** @type USWIndexEntry[] */
let USWResults = [];
let category = ''; let category = '';
let searchGlobals = $('#search-globals').checked; let searchGlobals = $('#search-globals').checked;
let searchUserStylesWorld = $('#search-usw').checked;
/** @type string[] */ /** @type string[] */
let query = []; let query = [];
/** @type 'n' | 'u' | 't' | 'w' | 'r' */ /** @type 'n' | 'u' | 't' | 'w' | 'r' */
@ -97,36 +83,24 @@
searchGlobals = this.checked; searchGlobals = this.checked;
ready = ready.then(start); ready = ready.then(start);
}; };
$('#search-usw').checked = prefs.get('popup.searchUserStylesWorld');
$('#search-usw').onchange = function () {
searchUserStylesWorld = this.checked;
prefs.set('popup.searchUserStylesWorld', searchUserStylesWorld);
ready = ready.then(start);
};
$('#search-query').oninput = function () { $('#search-query').oninput = function () {
debounce(ev => { query = [];
query = []; const text = this.value.trim().toLocaleLowerCase();
const text = ev.value.trim().toLocaleLowerCase(); const thisYear = new Date().getFullYear();
const thisYear = new Date().getFullYear(); for (let re = /"(.+?)"|(\S+)/g, m; (m = re.exec(text));) {
for (let re = /"(.+?)"|(\S+)/g, m; (m = re.exec(text));) { const n = Number(m[2]);
const n = Number(m[2]); query.push(n >= 2000 && n <= thisYear ? n : m[1] || m[2]);
query.push(n >= 2000 && n <= thisYear ? n : m[1] || m[2]); }
} if (category === STYLUS_CATEGORY && !query.includes('stylus')) {
if (category === STYLUS_CATEGORY && !query.includes('stylus')) { query.push('stylus');
query.push('stylus'); }
} ready = ready.then(start);
ready = ready.then(start);
}, 750, this);
}; };
$('#search-order').value = order; $('#search-order').value = order;
$('#search-order').onchange = function () { $('#search-order').onchange = function () {
order = this.value; order = this.value;
if (searchUserStylesWorld) { results.sort(comparator);
USWResults.sort(comparator);
} else {
results.sort(comparator);
}
render(); render();
}; };
dom.list = $('#search-results-list'); dom.list = $('#search-results-list');
@ -160,35 +134,24 @@
window.on('styleDeleted', ({detail: {style: {id}}}) => { window.on('styleDeleted', ({detail: {style: {id}}}) => {
restoreScrollPosition(); restoreScrollPosition();
if (searchUserStylesWorld) { const result = results.find(r => r.installedStyleId === id);
const result = USWResults.find(r => r.id === id); if (result) {
if (result) { clearTimeout(result.pingbackTimer);
clearTimeout(result.pingbackTimer); renderActionButtons(result.i, -1);
renderActionButtons(result.id, -1);
}
} else {
const result = results.find(r => r.installedStyleId === id);
if (result) {
clearTimeout(result.pingbackTimer);
renderActionButtons(result.i, -1);
}
} }
}); });
window.on('styleAdded', async ({detail: {style}}) => { window.on('styleAdded', async ({detail: {style}}) => {
restoreScrollPosition(); restoreScrollPosition();
if (searchUserStylesWorld) { let usoOrUSwId = calcUsoId(style) ||
const uswId = calcUSwId(style) || calcUSwId(style);
calcUSwId(await API.styles.get(style.id)); if (usoOrUSwId) {
if (uswId && USWResults.find(r => r.id === uswId)) { const styleAPI = await API.styles.get(style.id);
renderActionButtons(uswId, style.id); usoOrUSwId = calcUsoId(styleAPI) ||
} calcUSwId(styleAPI);
} else { }
const usoId = calcUsoId(style) || if (usoOrUSwId && results.find(r => r.id === usoOrUSwId)) {
calcUsoId(await API.styles.get(style.id)); renderActionButtons(usoOrUSwId, style.id);
if (usoId && results.find(r => r.i === usoId)) {
renderActionButtons(usoId, style.id);
}
} }
}); });
} }
@ -226,26 +189,18 @@
show(dom.list); show(dom.list);
hide(dom.error); hide(dom.error);
try { try {
let resultsMap = []; results = [];
for (let retry = 0; !resultsMap.length && retry <= 2; retry++) { for (let retry = 0; !results.length && retry <= 2; retry++) {
resultsMap = await search({retry, searchUserStylesWorld}); results = await search({retry});
} }
if (resultsMap.length) { if (results.length) {
const installedStyles = await API.styles.getAll(); const installedStyles = await API.styles.getAll();
if (searchUserStylesWorld) { const allUsoOrUSwIds = new Set(installedStyles.map(calcUsoId || calcUSwId));
const allUSwIds = new Set(installedStyles.map(calcUSwId)); results = results.filter(r => !allUsoOrUSwIds.has(r.i));
USWResults = resultsMap.filter(r => !allUSwIds.has(r.id));
} else {
const allUsoIds = new Set(installedStyles.map(calcUsoId));
results = resultsMap.filter(r => !allUsoIds.has(r.i));
}
} else {
USWResults = [];
results = [];
} }
render(); render();
((searchUserStylesWorld ? USWResults : results).length ? show : hide)(dom.list); (results.length ? show : hide)(dom.list);
if (!(searchUserStylesWorld ? USWResults : results).length) { if (!results.length) {
error(t('searchResultNoneFound')); error(t('searchResultNoneFound'));
} }
} catch (reason) { } catch (reason) {
@ -254,8 +209,7 @@
} }
function render() { function render() {
const correctResults = searchUserStylesWorld ? USWResults : results; totalPages = Math.ceil(results.length / PAGE_LENGTH);
totalPages = Math.ceil(correctResults.length / PAGE_LENGTH);
displayedPage = Math.min(displayedPage, totalPages) || 1; displayedPage = Math.min(displayedPage, totalPages) || 1;
let start = (displayedPage - 1) * PAGE_LENGTH; let start = (displayedPage - 1) * PAGE_LENGTH;
const end = displayedPage * PAGE_LENGTH; const end = displayedPage * PAGE_LENGTH;
@ -264,17 +218,15 @@
// keep rendered elements with ids in the range of interest // keep rendered elements with ids in the range of interest
while ( while (
plantAt < PAGE_LENGTH && plantAt < PAGE_LENGTH &&
slot && slot.id === 'search-result-' + (correctResults[start] || {}).i slot && slot.id === 'search-result-' + (results[start] || {}).i
) { ) {
slot = slot.nextElementSibling; slot = slot.nextElementSibling;
plantAt++; plantAt++;
start++; start++;
} }
// add new elements // add new elements
while (start < Math.min(end, correctResults.length)) { while (start < Math.min(end, results.length)) {
const entry = searchUserStylesWorld const entry = createSearchResultNode(results[start++]);
? createSearchUSWResultNode(correctResults[start++])
: createSearchResultNode(correctResults[start++]);
if (slot) { if (slot) {
dom.list.replaceChild(entry, slot); dom.list.replaceChild(entry, slot);
slot = entry.nextElementSibling; slot = entry.nextElementSibling;
@ -284,13 +236,13 @@
plantAt++; plantAt++;
} }
// remove extraneous elements // remove extraneous elements
const pageLen = end > correctResults.length && const pageLen = end > results.length &&
correctResults.length % PAGE_LENGTH || results.length % PAGE_LENGTH ||
Math.min(correctResults.length, PAGE_LENGTH); Math.min(results.length, PAGE_LENGTH);
while (dom.list.children.length > pageLen) { while (dom.list.children.length > pageLen) {
dom.list.lastElementChild.remove(); dom.list.lastElementChild.remove();
} }
if (correctResults.length && 'empty' in dom.container.dataset) { if (results.length && 'empty' in dom.container.dataset) {
delete dom.container.dataset.empty; delete dom.container.dataset.empty;
} }
if (scrollToFirstResult && (!FIREFOX || FIREFOX >= 55)) { if (scrollToFirstResult && (!FIREFOX || FIREFOX >= 55)) {
@ -329,6 +281,7 @@
an: author, an: author,
sa: shotArchived, sa: shotArchived,
sn: shotName, sn: shotName,
uw: isUSwStyle,
} = entry._result = result; } = entry._result = result;
entry.id = RESULT_ID_PREFIX + id; entry.id = RESULT_ID_PREFIX + id;
// title // title
@ -339,9 +292,10 @@
$('.search-result-title span', entry).textContent = $('.search-result-title span', entry).textContent =
t.breakWord(name.length < 300 ? name : name.slice(0, 300) + '...'); t.breakWord(name.length < 300 ? name : name.slice(0, 300) + '...');
// screenshot // screenshot
const auto = URLS.uso + `auto_style_screenshots/${id}${USO_AUTO_PIC_SUFFIX}`; const auto = !isUSwStyle ? URLS.uso + `auto_style_screenshots/${id}${USO_AUTO_PIC_SUFFIX}` : '';
Object.assign($('.search-result-screenshot', entry), { Object.assign($('.search-result-screenshot', entry), {
src: shotName && !shotName.endsWith(USO_AUTO_PIC_SUFFIX) src: isUSwStyle ? shotName
: shotName && !shotName.endsWith(USO_AUTO_PIC_SUFFIX)
? `${shotArchived ? URLS.usoArchiveRaw : URLS.uso + 'style_'}screenshots/${shotName}` ? `${shotArchived ? URLS.usoArchiveRaw : URLS.uso + 'style_'}screenshots/${shotName}`
: auto, : auto,
_src: auto, _src: auto,
@ -373,42 +327,6 @@
return entry; return entry;
} }
/**
* @param {USWIndexEntry} result
* @returns {Node}
*/
function createSearchUSWResultNode(result) {
const entry = t.template.searchResult.cloneNode(true);
const {
id,
name,
preview,
username,
} = entry._result = result;
entry.id = RESULT_ID_PREFIX + id;
// title
Object.assign($('.search-result-title', entry), {
onclick: Events.openURLandHide,
href: `${URLS.usw}style/${id}`,
});
$('.search-result-title span', entry).textContent =
t.breakWord(name.length < 300 ? name : name.slice(0, 300) + '...');
// screenshot
Object.assign($('.search-result-screenshot', entry), {
src: preview,
onerror: fixScreenshot,
});
// author
Object.assign($('[data-type="author"] a', entry), {
textContent: username,
title: username,
href: `${URLS.usw}user/${encodeURIComponent(username).replace(/%20/g, '+')}`,
onclick: Events.openURLandHide,
});
renderActionButtons(entry);
return entry;
}
function formatNumber(num) { function formatNumber(num) {
return ( return (
num > 1e9 ? (num / 1e9).toFixed(1) + 'B' : num > 1e9 ? (num / 1e9).toFixed(1) + 'B' :
@ -460,11 +378,11 @@
} }
} }
Object.assign($('.search-result-screenshot', entry), { Object.assign($('.search-result-screenshot', entry), {
onclick: isInstalled ? uninstall : searchUserStylesWorld ? uswInstall : install, onclick: isInstalled ? uninstall : install,
title: isInstalled ? '' : t('installButton'), title: isInstalled ? '' : t('installButton'),
}); });
$('.search-result-uninstall', entry).onclick = uninstall; $('.search-result-uninstall', entry).onclick = uninstall;
$('.search-result-install', entry).onclick = searchUserStylesWorld ? uswInstall : install; $('.search-result-install', entry).onclick = install;
} }
function renderFullInfo(entry, style) { function renderFullInfo(entry, style) {
@ -489,21 +407,24 @@
async function install() { async function install() {
const entry = this.closest('.search-result'); const entry = this.closest('.search-result');
const result = /** @type IndexEntry */ entry._result; const result = /** @type IndexEntry */ entry._result;
const {i: id} = result; const {i: id, uw: isUSwStyle} = result;
const installButton = $('.search-result-install', entry); const installButton = $('.search-result-install', entry);
showSpinner(entry); showSpinner(entry);
saveScrollPosition(entry); saveScrollPosition(entry);
installButton.disabled = true; installButton.disabled = true;
entry.style.setProperty('pointer-events', 'none', 'important'); entry.style.setProperty('pointer-events', 'none', 'important');
// FIXME: move this to background page and create an API like installUSOStyle // FIXME: move this to background page and create an API like installUSOStyle
result.pingbackTimer = setTimeout(download, PINGBACK_DELAY, if (!isUSwStyle) {
`${URLS.uso}styles/install/${id}?source=stylish-ch`); result.pingbackTimer = setTimeout(download, PINGBACK_DELAY,
`${URLS.uso}styles/install/${id}?source=stylish-ch`);
}
const updateUrl = isUSwStyle ? URLS.makeUSwCodeUrl(id) : URLS.makeUsoArchiveCodeUrl(id);
const updateUrl = URLS.makeUsoArchiveCodeUrl(id);
try { try {
const sourceCode = await download(updateUrl); const sourceCode = await download(updateUrl);
const style = await API.usercss.install({sourceCode, updateUrl}); const style = await API.usercss.install({sourceCode, updateUrl, initialUrl: isUSwStyle ? updateUrl : null});
renderFullInfo(entry, style); renderFullInfo(entry, style);
} catch (reason) { } catch (reason) {
error(`Error while downloading usoID:${id}\nReason: ${reason}`); error(`Error while downloading usoID:${id}\nReason: ${reason}`);
@ -513,30 +434,6 @@
entry.style.pointerEvents = ''; entry.style.pointerEvents = '';
} }
async function uswInstall() {
const entry = this.closest('.search-result');
const result = /** @type IndexEntry */ entry._result;
const {id} = result;
const installButton = $('.search-result-install', entry);
showSpinner(entry);
saveScrollPosition(entry);
installButton.disabled = true;
entry.style.setProperty('pointer-events', 'none', 'important');
const updateUrl = URLS.makeUSwArchiveCodeUrl(id);
try {
const sourceCode = await download(updateUrl);
const style = await API.usercss.install({sourceCode, updateUrl, initialUrl: updateUrl});
renderFullInfo(entry, style);
} catch (reason) {
error(`Error while downloading uswID:${id}\nReason: ${reason}`);
}
$remove('.lds-spinner', entry);
installButton.disabled = false;
entry.style.pointerEvents = '';
}
function uninstall() { function uninstall() {
const entry = this.closest('.search-result'); const entry = this.closest('.search-result');
saveScrollPosition(entry); saveScrollPosition(entry);
@ -586,29 +483,16 @@
const timer = setTimeout(showSpinner, BUSY_DELAY, dom.list); const timer = setTimeout(showSpinner, BUSY_DELAY, dom.list);
index = (await download(INDEX_URL, {responseType: 'json'})) index = (await download(INDEX_URL, {responseType: 'json'}))
.filter(res => res.f === 'uso'); .filter(res => res.f === 'uso');
index = [...index, ...(await download(URLS.uswIndex + 'uso-format', {responseType: 'json'}))];
clearTimeout(timer); clearTimeout(timer);
$remove(':scope > .lds-spinner', dom.list); $remove(':scope > .lds-spinner', dom.list);
return index; return index;
} }
async function searchUSW() { async function search({retry} = {}) {
const timer = setTimeout(showSpinner, BUSY_DELAY, dom.list);
const USWQuery = query.join(' ');
if (!USWQuery) {
return;
}
USWIndex[USWQuery] = (await download(URLS.uswSearch + USWQuery, {responseType: 'json'}));
clearTimeout(timer);
$remove(':scope > .lds-spinner', dom.list);
return USWIndex[USWQuery];
}
async function search({retry, searchUserStylesWorld} = {}) {
return retry && !calcCategory({retry}) return retry && !calcCategory({retry})
? [] ? []
: searchUserStylesWorld : (index || await fetchIndex()).filter(isResultMatching).sort(comparator);
? (USWIndex[query.join(' ')] || await searchUSW() || []).sort(comparator)
: (index || await fetchIndex()).filter(isResultMatching).sort(comparator);
} }
function isResultMatching(res) { function isResultMatching(res) {
@ -651,7 +535,9 @@
} }
function calcUSwId({installationUrl}) { function calcUSwId({installationUrl}) {
return installationUrl ? installationUrl.match(/\d+|$/)[0] : 0; return installationUrl ? Number(installationUrl.startsWith(URLS.usw)
&& installationUrl.match(/\d+|$/)[0])
: 0;
} }
function calcHaystack(res) { function calcHaystack(res) {