mirror of
https://github.com/kiwix/kiwix-js-pwa.git
synced 2025-09-08 11:48:26 -04:00
Allow user to search for more results from UI
This commit is contained in:
parent
54640dc6a7
commit
cc77c59e47
@ -224,8 +224,6 @@ function onPointerUp (e) {
|
||||
|
||||
if (/UWP/.test(params.appType)) document.body.addEventListener('pointerup', onPointerUp);
|
||||
|
||||
var searchArticlesFocused = false;
|
||||
|
||||
document.getElementById('searchArticles').addEventListener('click', function () {
|
||||
var val = prefix.value;
|
||||
// Do not initiate the same search if it is already in progress
|
||||
@ -243,8 +241,6 @@ document.getElementById('searchArticles').addEventListener('click', function ()
|
||||
var headerHeight = document.getElementById('top').getBoundingClientRect().height;
|
||||
var footerHeight = document.getElementById('footer').getBoundingClientRect().height;
|
||||
scrollbox.style.height = window.innerHeight - headerHeight - footerHeight + 'px';
|
||||
// This flag is set to true in the mousedown event below
|
||||
searchArticlesFocused = false;
|
||||
});
|
||||
document.getElementById('formArticleSearch').addEventListener('submit', function () {
|
||||
document.getElementById('searchArticles').click();
|
||||
@ -342,19 +338,16 @@ prefix.addEventListener('focus', function () {
|
||||
});
|
||||
// Hide the search results if user moves out of prefix field
|
||||
prefix.addEventListener('blur', function () {
|
||||
if (!searchArticlesFocused) {
|
||||
appstate.search.status = 'cancelled';
|
||||
}
|
||||
// We need to wait one tick for the activeElement to receive focus
|
||||
setTimeout(function () {
|
||||
if (!(/^articleList|searchSyntaxLink/.test(document.activeElement.id) ||
|
||||
/^list-group/.test(document.activeElement.className))) {
|
||||
scrollbox.style.height = 0;
|
||||
document.getElementById('articleListWithHeader').style.display = 'none';
|
||||
appstate.tempPrefix = '';
|
||||
uiUtil.clearSpinner();
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(function () {
|
||||
if (!(/^articleList|searchSyntaxLink/.test(document.activeElement.id) ||
|
||||
/^list-group/.test(document.activeElement.className))) {
|
||||
scrollbox.style.height = 0;
|
||||
document.getElementById('articleListWithHeader').style.display = 'none';
|
||||
appstate.tempPrefix = '';
|
||||
uiUtil.clearSpinner();
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
|
||||
// Add keyboard shortcuts
|
||||
@ -685,7 +678,7 @@ document.getElementById('btnRescanDeviceStorage').addEventListener('click', func
|
||||
displayFileSelect();
|
||||
}
|
||||
// Check if we are in an Android app, and if so, auto-select use of OPFS if there is no set value in settingsStore for useOPFS
|
||||
if ((/Android/.test(params.appType) || /Firefox/.test(navigator.userAgent)) && !params.useOPFS && !settingsStore.getItem('useOPFS')) {
|
||||
if ((/Android/.test(params.appType) || /Firefox/.test(navigator.userAgent)) && !params.useOPFS && !settingsStore.hasItem('useOPFS')) {
|
||||
// This will only run first time app is run on Android
|
||||
setTimeout(function () {
|
||||
uiUtil.systemAlert('<p>We are switching to the Private File System (OPFS).</p>' +
|
||||
@ -701,11 +694,13 @@ document.getElementById('btnRescanDeviceStorage').addEventListener('click', func
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
} else if (!settingsStore.getItem('useOPFS')) {
|
||||
// This esnures that there is an explicit setting for useOPFS, which in turn allows us to tell if the
|
||||
} else if (!settingsStore.hasItem('useOPFS')) {
|
||||
// This ensures that there is an explicit setting for useOPFS, which in turn allows us to tell if the
|
||||
// app is running for the first time (so we don't keep prompting the user to use the OPFS)
|
||||
settingsStore.setItem('useOPFS', false, Infinity);
|
||||
}
|
||||
// Since we may have changed the storage type, we should recalculate the max search size
|
||||
uiUtil.dynamicallySetMaxSearchResults();
|
||||
});
|
||||
// Bottom bar :
|
||||
// @TODO Since bottom bar now hidden in Settings and About the returntoArticle code cannot be accessed;
|
||||
@ -1675,6 +1670,8 @@ function setOPFSUI () {
|
||||
btnDeleteOPFSEntry.style.display = 'none';
|
||||
btnExportOPFSEntry.style.display = 'none';
|
||||
}
|
||||
// Enabling or disabling the OPFS affects the maximum number of search results we should return
|
||||
uiUtil.dynamicallySetMaxSearchResults();
|
||||
}
|
||||
|
||||
// Set the OPFS UI on app launch
|
||||
@ -1940,6 +1937,7 @@ if (window.electronAPI) {
|
||||
document.getElementById('libzimSearchType').addEventListener('change', function (e) {
|
||||
params.libzimSearchType = e.target.checked ? 'searchWithSnippets' : 'search';
|
||||
settingsStore.setItem('libzimSearchType', params.libzimSearchType, Infinity);
|
||||
uiUtil.dynamicallySetMaxSearchResults();
|
||||
});
|
||||
|
||||
document.getElementById('disableDragAndDropCheck').addEventListener('change', function () {
|
||||
@ -4792,12 +4790,12 @@ function listenForSearchKeys () {
|
||||
* with a binary search inside the index file)
|
||||
* @param {String} prefix The string that must appear at the start of any title searched for
|
||||
*/
|
||||
function searchDirEntriesFromPrefix (prefix) {
|
||||
function searchDirEntriesFromPrefix (prefix, size) {
|
||||
if (appstate.selectedArchive !== null && appstate.selectedArchive.isReady()) {
|
||||
// Cancel the old search (zimArchive search object will receive this change)
|
||||
appstate.search.status = 'cancelled';
|
||||
// Initiate a new search object and point appstate.search to it (the zimAcrhive search object will continue to point to the old object)
|
||||
appstate.search = { prefix: prefix, status: 'init', type: '', size: params.maxSearchResultsSize };
|
||||
appstate.search = { prefix: prefix, status: 'init', type: '', size: size || params.maxSearchResultsSize };
|
||||
uiUtil.hideActiveContentWarning();
|
||||
if (!prefix || /^\s/.test(prefix)) {
|
||||
var sel = prefix ? prefix.replace(/^\s(.*)/, '$1') : '';
|
||||
@ -4975,23 +4973,49 @@ function populateListOfArticles (dirEntryArray, reportingSearch) {
|
||||
var message;
|
||||
if (stillSearching) {
|
||||
message = 'Searching [' + appstate.search.type + ']... found: ' + nbDirEntry + '...' +
|
||||
(reportingSearch.scanCount ? ' [scanning ' + reportingSearch.scanCount + ' titles] <a href="#">stop</a>' : '');
|
||||
} else if (nbDirEntry >= params.maxSearchResultsSize) {
|
||||
message = 'First ' + params.maxSearchResultsSize + (reportingSearch.searchUrlIndex ? ' assets' : ' articles') + ' found: refine your search.';
|
||||
(reportingSearch.scanCount ? ' [scanning ' + reportingSearch.scanCount + ' titles] <a href="#" id="stopScan">stop</a>' : '');
|
||||
} else if (nbDirEntry >= reportingSearch.size) {
|
||||
message = 'First ' + reportingSearch.size + (reportingSearch.searchUrlIndex ? ' assets' : ' articles') +
|
||||
' found: refine your search.';
|
||||
} else if (reportingSearch.status === 'error') {
|
||||
message = 'Incorrect search syntax! See <a href="#searchSyntaxError" id="searchSyntaxLink">Search syntax</a> in About!';
|
||||
} else {
|
||||
message = 'Finished. ' + (nbDirEntry || 'No') + ' articles found' +
|
||||
(appstate.search.type === 'basic' ? ': try fewer words for full search.' : '.');
|
||||
}
|
||||
if (!stillSearching && reportingSearch.scanCount) message += ' [scanned ' + reportingSearch.scanCount + ' titles]';
|
||||
if (!stillSearching && reportingSearch.scanCount) {
|
||||
message += ' [scanned ' + reportingSearch.scanCount + ' titles] ' +
|
||||
'<a href="#" id="getMoreResults">Get more</a>';
|
||||
}
|
||||
|
||||
articleListHeaderMessageDiv.innerHTML = message;
|
||||
if (stillSearching && reportingSearch.countReport) return;
|
||||
|
||||
// Add event listener for stopScan link
|
||||
var stopScanElement = document.getElementById('stopScan');
|
||||
if (stopScanElement && !stopScanElement.hasAttribute('data-listener-added')) {
|
||||
stopScanElement.addEventListener('mousedown', function (e) {
|
||||
e.preventDefault();
|
||||
appstate.search.status = 'cancelled';
|
||||
});
|
||||
stopScanElement.setAttribute('data-listener-added', 'true');
|
||||
}
|
||||
// Add event listener for getMoreResults link
|
||||
var getMoreResultsElement = document.getElementById('getMoreResults');
|
||||
if (getMoreResultsElement && !getMoreResultsElement.hasAttribute('data-listener-added')) {
|
||||
getMoreResultsElement.addEventListener('mousedown', function (e) {
|
||||
e.preventDefault();
|
||||
// Temporarily increase the search window by params.maxSearchResultsSize
|
||||
var temporarySearchSize = appstate.search.size + params.maxSearchResultsSize;
|
||||
// Rerun the search with the current prefix
|
||||
searchDirEntriesFromPrefix(appstate.search.prefix, temporarySearchSize);
|
||||
});
|
||||
getMoreResultsElement.setAttribute('data-listener-added', 'true');
|
||||
}
|
||||
|
||||
if (stillSearching && reportingSearch.countReport) return;
|
||||
var articleListDiv = document.getElementById('articleList');
|
||||
var articleListDivHtml = '';
|
||||
var listLength = dirEntryArray.length < params.maxSearchResultsSize ? dirEntryArray.length : params.maxSearchResultsSize;
|
||||
var listLength = dirEntryArray.length < reportingSearch.size ? dirEntryArray.length : reportingSearch.size;
|
||||
for (var i = 0; i < listLength; i++) {
|
||||
var dirEntry = dirEntryArray[i];
|
||||
// NB We use encodeURIComponent rather than encodeURI here because we know that any question marks in the title are not querystrings,
|
||||
|
@ -96,8 +96,8 @@ params['PWAServer'] = 'https://pwa.kiwix.org/'; // Production server
|
||||
params['storeType'] = getBestAvailableStorageAPI();
|
||||
params['appType'] = getAppType();
|
||||
params['keyPrefix'] = 'kiwixjs-'; // Prefix to use for localStorage keys
|
||||
// Maximum number of article titles to return (range is 5 - 100, default 20), but see intelligent search-size calculation below
|
||||
params['maxSearchResultsSize'] = ~~(getSetting('maxSearchResultsSize') || 20);
|
||||
// Maximum number of article titles to return (range is 5 - 100, default 15), but see intelligent search-size calculation below
|
||||
params['maxSearchResultsSize'] = ~~(getSetting('maxSearchResultsSize') || 15);
|
||||
params['relativeFontSize'] = ~~(getSetting('relativeFontSize') || 100); // Sets the initial font size for articles (as a percentage) - user can adjust using zoom buttons
|
||||
params['relativeUIFontSize'] = ~~(getSetting('relativeUIFontSize') || 100); // Sets the initial font size for UI (as a percentage) - user can adjust using slider in Config
|
||||
params['cssSource'] = getSetting('cssSource') || 'auto'; // Set default to "auto", "desktop" or "mobile"
|
||||
@ -244,25 +244,6 @@ if (getSetting('lastPageLoad') === 'failed') {
|
||||
setSetting('lastPageLoad', 'failed');
|
||||
}
|
||||
|
||||
// Use intelligent search-size calculation based on app type and environment
|
||||
// This is because fulltext search with snippets is slower than basic fulltext search, and excruciatingly slow on Android if not using the OPFS
|
||||
if (!getSetting('maxSearchResultsSize')) {
|
||||
if (params.libzimSearchType === 'search') {
|
||||
// If the user has set the search type to basic search, we can use a larger number of results
|
||||
params.maxSearchResultsSize = 30;
|
||||
}
|
||||
if (/Android/.test(params.appType)) {
|
||||
if (params.useOPFS) {
|
||||
// Android with OPFS can handle more results: 15 with snippets, 20 with basic search
|
||||
params.maxSearchResultsSize = params.libzimSearchType === 'search' ? 20 : 15;
|
||||
} else {
|
||||
// Android without OPFS needs restricted results and basic search
|
||||
params.maxSearchResultsSize = 10;
|
||||
params.libzimSearchType = getSetting('libzimSearchType') || 'search';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize checkbox, radio and other values
|
||||
document.getElementById('cssCacheModeCheck').checked = params.cssCache;
|
||||
document.getElementById('navButtonsPosCheck').checked = params.navButtonsPos === 'top';
|
||||
|
@ -26,6 +26,7 @@
|
||||
/* global webpMachine, params, appstate, Windows */
|
||||
|
||||
import util from './util.js';
|
||||
import settingsStore from './settingsStore.js';
|
||||
|
||||
/**
|
||||
* Global variables
|
||||
@ -1646,8 +1647,7 @@ function attachArticleListEventListeners (findDirEntryCallback, appstate) {
|
||||
hoverTimeout = null;
|
||||
}
|
||||
// Safety check: ensure the element still has the expected children
|
||||
if (element.children.length < 2) return;
|
||||
|
||||
// if (element.children.length < 2) return;
|
||||
// Always collapse on mouse leave
|
||||
// var header = element.children[0];
|
||||
// var content = element.children[1];
|
||||
@ -1658,6 +1658,29 @@ function attachArticleListEventListeners (findDirEntryCallback, appstate) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use intelligent search-size calculation based on app type and environment. This is because fulltext search with snippets
|
||||
* is slower than basic fulltext search, and excruciatingly slow on Android if not using the OPFS
|
||||
*/
|
||||
function dynamicallySetMaxSearchResults () {
|
||||
if (!settingsStore.hasItem('maxSearchResultsSize')) {
|
||||
if (params.libzimSearchType === 'search') {
|
||||
// If the user has set the search type to basic search, we can use a larger number of results
|
||||
params.maxSearchResultsSize = 25;
|
||||
}
|
||||
if (/Android/.test(params.appType)) {
|
||||
if (params.useOPFS) {
|
||||
// Android with OPFS can handle more results: 15 with snippets, 20 with basic search
|
||||
params.maxSearchResultsSize = params.libzimSearchType === 'search' ? 15 : 12;
|
||||
} else {
|
||||
// Android without OPFS needs restricted results and basic search
|
||||
params.maxSearchResultsSize = 10;
|
||||
params.libzimSearchType = settingsStore.getItem('libzimSearchType') || 'search';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions and classes exposed by this module
|
||||
*/
|
||||
@ -1699,5 +1722,6 @@ export default {
|
||||
handleTitleClick: handleTitleClick,
|
||||
createSnippetElements: createSnippetElements,
|
||||
toggleSnippet: toggleSnippet,
|
||||
attachArticleListEventListeners: attachArticleListEventListeners
|
||||
attachArticleListEventListeners: attachArticleListEventListeners,
|
||||
dynamicallySetMaxSearchResults: dynamicallySetMaxSearchResults
|
||||
};
|
||||
|
@ -575,7 +575,7 @@ ZIMArchive.prototype.findDirEntriesWithPrefixCaseSensitive = function (prefix, s
|
||||
*
|
||||
* @param {Object} search The appstate.search object
|
||||
* @param {Array} dirEntries The array of already found Directory Entries
|
||||
* @param {Integer} number Optional positive number of search results requested (otherwise params.maxSearchResults will be used)
|
||||
* @param {Integer} number Override number of results requested in search object (used to get remaining results)
|
||||
* @returns {Promise<callbackDirEntry>} The augmented array of Directory Entries with titles that correspond to search
|
||||
*/
|
||||
ZIMArchive.prototype.findDirEntriesFromFullTextSearch = function (search, dirEntries, number) {
|
||||
@ -583,7 +583,7 @@ ZIMArchive.prototype.findDirEntriesFromFullTextSearch = function (search, dirEnt
|
||||
var that = this;
|
||||
// We give ourselves an overhead in caclulating the results needed, because full-text search will return some results already found
|
||||
// var resultsNeeded = Math.floor(params.maxSearchResultsSize - dirEntries.length / 2);
|
||||
var resultsNeeded = number || params.maxSearchResultsSize;
|
||||
var resultsNeeded = number || search.size;
|
||||
var searchType = params.libzimSearchType || 'search';
|
||||
return this.callLibzimWorker({ action: searchType, text: search.prefix, numResults: resultsNeeded }).then(function (returned) {
|
||||
if (returned) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user