From 46297d244e3e0d83bf2dabf2582482c278287f6f Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Fri, 17 May 2024 21:18:20 -0300 Subject: client: search progress bar --- .../dependencies/generateSearchSidebarBox.js | 9 + src/static/css/site.css | 25 ++- src/static/js/client.js | 208 ++++++++++++++++++++- src/strings-default.yaml | 4 + 4 files changed, 231 insertions(+), 15 deletions(-) diff --git a/src/content/dependencies/generateSearchSidebarBox.js b/src/content/dependencies/generateSearchSidebarBox.js index 22c5c773..cc215783 100644 --- a/src/content/dependencies/generateSearchSidebarBox.js +++ b/src/content/dependencies/generateSearchSidebarBox.js @@ -20,6 +20,15 @@ export default { }, {type: 'search'}), + html.tag('template', {class: 'wiki-search-preparing-string'}, + language.$('misc.search.preparing')), + + html.tag('template', {class: 'wiki-search-loading-data-string'}, + language.$('misc.search.loadingData')), + + html.tag('template', {class: 'wiki-search-searching-string'}, + language.$('misc.search.searching')), + html.tag('template', {class: 'wiki-search-no-results-string'}, language.$('misc.search.noResults')), diff --git a/src/static/css/site.css b/src/static/css/site.css index d094738a..e94484e5 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -510,16 +510,25 @@ summary .group-name { .wiki-search-sidebar-box hr { border-color: var(--primary-color); border-style: none none dotted none; + margin-top: 3px; + margin-bottom: 3px; } -.wiki-search-sidebar-box hr:nth-of-type(1) { - margin-top: 3px; - margin-bottom: 6px; +.wiki-search-progress-container { + padding: 2px 6px 4px 6px; + display: flex; + flex-direction: row; } -.wiki-search-sidebar-box hr:nth-of-type(2) { - margin-top: 6px; - margin-bottom: 0; +.wiki-search-progress-label { + font-size: 0.9em; + font-style: oblique; + cursor: default; + margin-right: 1ch; +} + +.wiki-search-progress-bar { + flex-grow: 1; } .wiki-search-results-container { @@ -945,6 +954,10 @@ li:not(:first-child:last-child) .tooltip, color: var(--page-primary-color); } +progress { + accent-color: var(--primary-color); +} + .content-columns { columns: 2; } diff --git a/src/static/js/client.js b/src/static/js/client.js index e8de0472..5f9143e9 100644 --- a/src/static/js/client.js +++ b/src/static/js/client.js @@ -3452,11 +3452,13 @@ const wikiSearchInfo = initInfo('wikiSearchInfo', { }, event: { + whenWorkerAlive: [], + whenWorkerReady: [], + whenWorkerFailsToInitialize: [], + whenDownloadBegins: [], whenDownloadsBegin: [], - whenDownloadProgresses: [], - whenDownloadEnds: [], }, }); @@ -3511,21 +3513,24 @@ function handleSearchWorkerMessage(message) { } function handleSearchWorkerStatusMessage(message) { - const {state} = wikiSearchInfo; + const {state, event} = wikiSearchInfo; switch (message.data.status) { case 'alive': console.debug(`Search worker is alive, but not yet ready.`); + dispatchInternalEvent(event, 'whenWorkerAlive'); break; case 'ready': console.debug(`Search worker has loaded corpuses and is ready.`); state.workerReadyPromiseResolvers.resolve(state.worker); + dispatchInternalEvent(event, 'whenWorkerReady'); break; case 'setup-error': console.debug(`Search worker failed to initialize.`); state.workerReadyPromiseResolvers.reject('setup-error'); + dispatchInternalEvent(event, 'whenWorkerFailsToInitialize'); break; default: @@ -3665,6 +3670,11 @@ const sidebarSearchInfo = initInfo('sidebarSearchInfo', { searchBox: null, searchInput: null, + progressRule: null, + progressContainer: null, + progressLabel: null, + progressBar: null, + resultsRule: null, resultsContainer: null, results: null, @@ -3673,6 +3683,10 @@ const sidebarSearchInfo = initInfo('sidebarSearchInfo', { endSearchLine: null, endSearchLink: null, + preparingString: null, + loadingDataString: null, + searchingString: null, + noResultsString: null, currentResultString: null, endSearchString: null, @@ -3682,7 +3696,12 @@ const sidebarSearchInfo = initInfo('sidebarSearchInfo', { groupResultKindString: null, state: { + workerStatus: null, + searchStage: null, + stoppedTypingTimeout: null, + + indexDownloadStatuses: Object.create(null), }, session: { @@ -3711,6 +3730,15 @@ function getSidebarSearchReferences() { const findString = classPart => info.searchBox.querySelector(`.wiki-search-${classPart}-string`); + info.preparingString = + findString('preparing'); + + info.loadingDataString = + findString('loading-data'); + + info.searchingString = + findString('searching'); + info.noResultsString = findString('no-results'); @@ -3735,9 +3763,28 @@ function addSidebarSearchInternalListeners() { if (!info.searchBox) return; - wikiSearchInfo.event.whenDownloadsBegin.push(updateSidebarSearchStatus); - wikiSearchInfo.event.whenDownloadProgresses.push(updateSidebarSearchStatus); - wikiSearchInfo.event.whenDownloadEnds.push(updateSidebarSearchStatus); + wikiSearchInfo.event.whenWorkerAlive.push( + trackSidebarSearchWorkerAlive, + updateSidebarSearchStatus); + + wikiSearchInfo.event.whenWorkerReady.push( + trackSidebarSearchWorkerReady, + updateSidebarSearchStatus); + + wikiSearchInfo.event.whenWorkerFailsToInitialize.push( + trackSidebarSearchWorkerFailsToInitialize, + updateSidebarSearchStatus); + + wikiSearchInfo.event.whenDownloadsBegin.push( + trackSidebarSearchDownloadsBegin, + updateSidebarSearchStatus); + + wikiSearchInfo.event.whenDownloadProgresses.push( + updateSidebarSearchStatus); + + wikiSearchInfo.event.whenDownloadEnds.push( + trackSidebarSearchDownloadEnds, + updateSidebarSearchStatus); } function mutateSidebarSearchContent() { @@ -3745,6 +3792,39 @@ function mutateSidebarSearchContent() { if (!info.searchBox) return; + // Progress section + + info.progressRule = + document.createElement('hr'); + + info.progressContainer = + document.createElement('div'); + + info.progressContainer.classList.add('wiki-search-progress-container'); + + cssProp(info.progressRule, 'display', 'none'); + cssProp(info.progressContainer, 'display', 'none'); + + info.progressLabel = + document.createElement('label'); + + info.progressLabel.classList.add('wiki-search-progress-label'); + info.progressLabel.htmlFor = 'wiki-search-progress-bar'; + + info.progressBar = + document.createElement('progress'); + + info.progressBar.classList.add('wiki-search-progress-bar'); + info.progressBar.id = 'wiki-search-progress-bar'; + + info.progressContainer.appendChild(info.progressLabel); + info.progressContainer.appendChild(info.progressBar); + + info.searchBox.appendChild(info.progressRule); + info.searchBox.appendChild(info.progressContainer); + + // Results section + info.resultsRule = document.createElement('hr'); @@ -3766,11 +3846,11 @@ function mutateSidebarSearchContent() { info.searchBox.appendChild(info.resultsRule); info.searchBox.appendChild(info.resultsContainer); + // End search section + info.endSearchRule = document.createElement('hr'); - info.searchBox.appendChild(info.endSearchRule); - info.endSearchLine = document.createElement('p'); @@ -3844,6 +3924,50 @@ function initializeSidebarSearchState() { } } +function trackSidebarSearchWorkerAlive() { + const {state} = sidebarSearchInfo; + + state.workerStatus = 'alive'; +} + +function trackSidebarSearchWorkerReady() { + const {state} = sidebarSearchInfo; + + state.workerStatus = 'ready'; + state.searchStage = 'searching'; +} + +function trackSidebarSearchWorkerFailsToInitialize() { + const {state} = sidebarSearchInfo; + + state.workerStatus = 'failed'; +} + +function trackSidebarSearchDownloadsBegin(event) { + const {state} = sidebarSearchInfo; + + if (event.context === 'search-indexes') { + for (const key of event.keys) { + state.indexDownloadStatuses[key] = 'active'; + } + } +} + +function trackSidebarSearchDownloadEnds(event) { + const {state} = sidebarSearchInfo; + + if (event.context === 'search-indexes') { + state.indexDownloadStatuses[event.key] = 'complete'; + + const statuses = Object.values(state.indexDownloadStatuses); + if (statuses.every(status => status === 'complete')) { + for (const key of Object.keys(state.indexDownloadStatuses)) { + delete state.indexDownloadStatuses[key]; + } + } + } +} + async function activateSidebarSearch(query) { const {session, settings, state} = sidebarSearchInfo; @@ -3852,8 +3976,17 @@ async function activateSidebarSearch(query) { state.stoppedTypingTimeout = null; } + state.searchStage = + (state.workerStatus === 'ready' + ? 'searching' + : 'preparing'); + updateSidebarSearchStatus(); + const results = await searchAll(query, {enrich: true}); + state.searchStage = 'complete'; + updateSidebarSearchStatus(); + session.activeQuery = query; const stringifiedResults = JSON.stringify(results); @@ -3877,6 +4010,8 @@ function clearSidebarSearch() { info.searchInput.value = ''; + state.searchStage = null; + session.activeQuery = ''; session.activeQueryResults = ''; @@ -3890,7 +4025,62 @@ function updateSidebarSearchStatus() { const searchIndexDownloads = getSearchWorkerDownloadContext('search-indexes'); - console.log('Display:', searchIndexDownloads); + const downloadProgressValues = + Object.values(searchIndexDownloads ?? {}); + + if (downloadProgressValues.some(v => v < 1.00)) { + const total = Object.keys(state.indexDownloadStatuses).length; + const sum = accumulateSum(downloadProgressValues); + showSidebarSearchProgress( + sum / total, + templateContent(info.loadingDataString)); + + return; + } + + if (state.searchStage === 'preparing') { + showSidebarSearchProgress( + null, + templateContent(info.preparingString)); + + return; + } + + if (state.searchStage === 'searching') { + showSidebarSearchProgress( + null, + templateContent(info.searchingString)); + + return; + } + + hideSidebarSearchProgress(); +} + +function showSidebarSearchProgress(progress, label) { + const info = sidebarSearchInfo; + + cssProp(info.progressRule, 'display', null); + cssProp(info.progressContainer, 'display', null); + + if (progress === null) { + info.progressBar.removeAttribute('value'); + } else { + info.progressBar.value = progress; + } + + while (info.progressLabel.firstChild) { + info.progressLabel.firstChild.remove(); + } + + info.progressLabel.appendChild(label); +} + +function hideSidebarSearchProgress() { + const info = sidebarSearchInfo; + + cssProp(info.progressRule, 'display', 'none'); + cssProp(info.progressContainer, 'display', 'none'); } function showSidebarSearchResults(results) { diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 23f67ce6..aaa74dca 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -622,6 +622,10 @@ misc: search: placeholder: "Search for anything" + preparing: "Preparing..." + loadingData: "Loading data..." + searching: "Searching..." + noResults: >- No results for this query, sorry! Check spelling and use complete words. -- cgit 1.3.0-6-gf8a5