From cc45467b79a0e2f67212415bfbcf9702fa4edd8b Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Tue, 3 Feb 2026 05:14:06 +0000 Subject: [PATCH 01/15] Fixed default search to search all categories --- public/src/client/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index d2c1bb1c03..1be28a6b92 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -252,7 +252,7 @@ define('forum/search', [ function categoryFilterDropdown(_selectedCids) { ajaxify.data.allCategoriesUrl = ''; - selectedCids = _selectedCids || []; + selectedCids = _selectedCids || ['all']; const dropdownEl = $('[component="category/filter"]'); categoryFilter.init(dropdownEl, { selectedCids: _selectedCids, From 77b54047f3906bf6e301470068e4136eeac37066 Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Sat, 7 Feb 2026 22:21:57 +0000 Subject: [PATCH 02/15] Fixed public/src/client/search.js and public/src/modules/categoryFilter.js All Categories to : - Search all categories on entering the search page - Search all categories after selecting such in the categories dropdown --- public/src/client/search.js | 97 +++++++++++++++++++--------- public/src/modules/categoryFilter.js | 12 +++- 2 files changed, 75 insertions(+), 34 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 1be28a6b92..6bacf030c6 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -16,6 +16,8 @@ define('forum/search', [ let selectedTags = []; let selectedCids = []; let searchFilters = {}; + let lastAutoQueryUrl = null; + Search.init = function () { const searchIn = $('#search-in'); searchIn.on('change', function () { @@ -65,6 +67,17 @@ define('forum/search', [ updateSortFilter(); searchFilters = getSearchDataFromDOM(); + + const currentUrl = window.location.pathname + window.location.search; + + // Only auto-query when arriving with a term AND the page doesn't already have results rendered + const hasRenderedResults = !!$('#results .search-results').length; + const term = (searchFilters.term || '').trim(); + + if (term && !hasRenderedResults && lastAutoQueryUrl !== currentUrl) { + lastAutoQueryUrl = currentUrl; + searchModule.query(searchFilters); + } }; function updateTagFilter() { @@ -122,7 +135,8 @@ define('forum/search', [ if (['posts', 'titlesposts', 'titles', 'bookmarks'].includes(searchData.in)) { searchData.matchWords = form.find('#match-words-filter').val(); searchData.by = selectedUsers.length ? selectedUsers.map(u => u.username) : undefined; - searchData.categories = selectedCids.length ? selectedCids : undefined; + const cids = Array.isArray(selectedCids) ? selectedCids : []; + searchData.categories = cids.length ? cids : ['all']; searchData.searchChildren = form.find('#search-children').is(':checked'); searchData.hasTags = selectedTags.length ? selectedTags.map(t => t.value) : undefined; searchData.replies = form.find('#reply-count').val(); @@ -252,43 +266,62 @@ define('forum/search', [ function categoryFilterDropdown(_selectedCids) { ajaxify.data.allCategoriesUrl = ''; - selectedCids = _selectedCids || ['all']; + + // UI semantics: All = [] + let uiCids = Array.isArray(_selectedCids) ? _selectedCids.map(String) : []; + + // sanitize any bad inputs + uiCids = uiCids.filter(Boolean); + + // if upstream gave ['all'], convert to UI form [] + if (uiCids.length === 1 && uiCids[0] === 'all') { + uiCids = []; + } + // if upstream accidentally includes 'all' with others, drop it + if (uiCids.includes('all')) { + uiCids = uiCids.filter(cid => cid !== 'all'); + } + + ajaxify.data.selectedCids = uiCids; + selectedCids = uiCids; + const dropdownEl = $('[component="category/filter"]'); categoryFilter.init(dropdownEl, { - selectedCids: _selectedCids, - updateButton: false, // prevent categoryFilter module from updating the button + selectedCids: uiCids, // ✅ pass [] for All + updateButton: false, onHidden: async function (data) { - const isActive = data.selectedCids.length > 0 && data.selectedCids[0] !== 'all'; - let labelText = '[[search:categories]]'; - ajaxify.data.selectedCids = data.selectedCids; - selectedCids = data.selectedCids; - if (data.selectedCids.length === 1 && data.selectedCids[0] === 'watched') { - ajaxify.data.selectedCategory = { cid: 'watched' }; - labelText = `[[search:categories-watched-categories]]`; - } else if (data.selectedCids.length === 1 && data.selectedCids[0] === 'all') { - ajaxify.data.selectedCategory = null; - } else if (data.selectedCids.length > 0) { - const categoryData = await api.get(`/categories/${data.selectedCids[0]}`); - ajaxify.data.selectedCategory = categoryData; - labelText = `[[search:categories-x, ${categoryData.name}]]`; - } - if (data.selectedCids.length > 1) { - labelText = `[[search:categories-x, ${data.selectedCids.length}]]`; - } + let cids = Array.isArray(data.selectedCids) ? data.selectedCids.map(String) : []; + cids = cids.filter(Boolean).filter(cid => cid !== 'all'); // ✅ keep UI form + + ajaxify.data.selectedCids = cids; + selectedCids = cids; + + const isActive = cids.length > 0; + + let labelText = '[[search:categories]]'; + if (cids.length === 1 && cids[0] === 'watched') { + ajaxify.data.selectedCategory = { cid: 'watched' }; + labelText = '[[search:categories-watched-categories]]'; + } else if (cids.length > 1) { + labelText = `[[search:categories-x, ${cids.length}]]`; + } else if (cids.length === 1) { + const categoryData = await api.get(`/categories/${cids[0]}`); + ajaxify.data.selectedCategory = categoryData; + labelText = `[[search:categories-x, ${categoryData.name}]]`; + } else { + ajaxify.data.selectedCategory = { cid: 'all' }; + } - $('[component="category/filter/button"]').toggleClass( - 'active-filter', isActive - ).find('.filter-label').translateText(labelText); + $('[component="category/filter/button"]') + .toggleClass('active-filter', isActive) + .find('.filter-label') + .translateText(labelText); }, - localCategories: [ - { - cid: 'watched', - name: '[[category:watched-categories]]', - icon: '', - }, - ], + localCategories: [{ cid: 'watched', name: '[[category:watched-categories]]', icon: '' }], }); - } + } + + function userFilterDropdown(el, _selectedUsers) { selectedUsers = _selectedUsers || []; diff --git a/public/src/modules/categoryFilter.js b/public/src/modules/categoryFilter.js index 4f330be091..e4c1ae6b91 100644 --- a/public/src/modules/categoryFilter.js +++ b/public/src/modules/categoryFilter.js @@ -73,6 +73,10 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS const icon = categoryEl.find('[component="category/select/icon"]'); if (cid !== 'all') { + if (selectedCids.length === 1 && selectedCids[0] === 'all') { + selectedCids = []; + } + if (selectedCids.includes(cid)) { selectedCids.splice(selectedCids.indexOf(cid), 1); } else { @@ -86,7 +90,7 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS } else { el.find('[component="category/select/icon"]').addClass('invisible'); listEl.find('[data-cid="all"] i').removeClass('invisible'); - selectedCids = []; + selectedCids = ['all']; // IMPORTANT: backend expects this } options.selectedCids = selectedCids; @@ -106,7 +110,11 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS bgColor: '#ddd', }); } else if (selectedCids.length === 1) { - api.get(`/categories/${selectedCids[0]}`, {}).then(renderButton); + if (selectedCids[0] === 'all') { + renderButton(); // default button + } else { + api.get(`/categories/${selectedCids[0]}`, {}).then(renderButton); + } } else { renderButton(); } From 99a5c61f5599a0f97ab64b7821ea9d65e1206b14 Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Sat, 7 Feb 2026 22:47:42 +0000 Subject: [PATCH 03/15] Hopefully fixed style --- public/src/client/search.js | 54 ++++++++++++++-------------- public/src/modules/categoryFilter.js | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 6bacf030c6..020e8762de 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -287,39 +287,39 @@ define('forum/search', [ const dropdownEl = $('[component="category/filter"]'); categoryFilter.init(dropdownEl, { - selectedCids: uiCids, // ✅ pass [] for All + selectedCids: uiCids, // pass [] for All updateButton: false, onHidden: async function (data) { - let cids = Array.isArray(data.selectedCids) ? data.selectedCids.map(String) : []; - cids = cids.filter(Boolean).filter(cid => cid !== 'all'); // ✅ keep UI form - - ajaxify.data.selectedCids = cids; - selectedCids = cids; - - const isActive = cids.length > 0; - - let labelText = '[[search:categories]]'; - if (cids.length === 1 && cids[0] === 'watched') { - ajaxify.data.selectedCategory = { cid: 'watched' }; - labelText = '[[search:categories-watched-categories]]'; - } else if (cids.length > 1) { - labelText = `[[search:categories-x, ${cids.length}]]`; - } else if (cids.length === 1) { - const categoryData = await api.get(`/categories/${cids[0]}`); - ajaxify.data.selectedCategory = categoryData; - labelText = `[[search:categories-x, ${categoryData.name}]]`; - } else { - ajaxify.data.selectedCategory = { cid: 'all' }; - } + let cids = Array.isArray(data.selectedCids) ? data.selectedCids.map(String) : []; + cids = cids.filter(Boolean).filter(cid => cid !== 'all'); // keep UI form + + ajaxify.data.selectedCids = cids; + selectedCids = cids; + + const isActive = cids.length > 0; + + let labelText = '[[search:categories]]'; + if (cids.length === 1 && cids[0] === 'watched') { + ajaxify.data.selectedCategory = { cid: 'watched' }; + labelText = '[[search:categories-watched-categories]]'; + } else if (cids.length > 1) { + labelText = `[[search:categories-x, ${cids.length}]]`; + } else if (cids.length === 1) { + const categoryData = await api.get(`/categories/${cids[0]}`); + ajaxify.data.selectedCategory = categoryData; + labelText = `[[search:categories-x, ${categoryData.name}]]`; + } else { + ajaxify.data.selectedCategory = { cid: 'all' }; + } - $('[component="category/filter/button"]') - .toggleClass('active-filter', isActive) - .find('.filter-label') - .translateText(labelText); + $('[component="category/filter/button"]') + .toggleClass('active-filter', isActive) + .find('.filter-label') + .translateText(labelText); }, localCategories: [{ cid: 'watched', name: '[[category:watched-categories]]', icon: '' }], }); - } + } diff --git a/public/src/modules/categoryFilter.js b/public/src/modules/categoryFilter.js index e4c1ae6b91..ef39d43ad9 100644 --- a/public/src/modules/categoryFilter.js +++ b/public/src/modules/categoryFilter.js @@ -90,7 +90,7 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS } else { el.find('[component="category/select/icon"]').addClass('invisible'); listEl.find('[data-cid="all"] i').removeClass('invisible'); - selectedCids = ['all']; // IMPORTANT: backend expects this + selectedCids = ['all']; // IMPORTANT: backend expects this } options.selectedCids = selectedCids; From edf98968033d3e70ea330f05efe9d4c3620cbe43 Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Sat, 7 Feb 2026 22:51:09 +0000 Subject: [PATCH 04/15] Linting fix... hopefully. --- public/src/modules/categoryFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/categoryFilter.js b/public/src/modules/categoryFilter.js index ef39d43ad9..e476a3f370 100644 --- a/public/src/modules/categoryFilter.js +++ b/public/src/modules/categoryFilter.js @@ -90,7 +90,7 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS } else { el.find('[component="category/select/icon"]').addClass('invisible'); listEl.find('[data-cid="all"] i').removeClass('invisible'); - selectedCids = ['all']; // IMPORTANT: backend expects this + selectedCids = ['all']; // IMPORTANT: backend expects this } options.selectedCids = selectedCids; From ce4466aca58d71134e290695dd40fb3af8ea3f04 Mon Sep 17 00:00:00 2001 From: topshenyi-web Date: Sat, 7 Feb 2026 19:54:49 -0500 Subject: [PATCH 05/15] empty commit for deployment From e9793282ec9a3ea1edbcc564cf3467af5f04cb7d Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Sun, 8 Feb 2026 04:33:24 +0000 Subject: [PATCH 06/15] Fixed display check bug, hopefully fixed doublequerry --- public/src/client/search.js | 83 +++++++++++++------------------------ 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 020e8762de..333a7d5c30 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -19,6 +19,8 @@ define('forum/search', [ let lastAutoQueryUrl = null; Search.init = function () { + let alreadyQueried = false; + const searchIn = $('#search-in'); searchIn.on('change', function () { updateFormItemVisiblity(searchIn.val()); @@ -55,7 +57,8 @@ define('forum/search', [ } const searchFiltersNew = getSearchDataFromDOM(); - if (JSON.stringify(searchFilters) !== JSON.stringify(searchFiltersNew)) { + if (!alreadyQueried || JSON.stringify(searchFilters) !== JSON.stringify(searchFiltersNew)) { + alreadyQueried = true; searchFilters = searchFiltersNew; searchModule.query(searchFilters); } @@ -67,17 +70,6 @@ define('forum/search', [ updateSortFilter(); searchFilters = getSearchDataFromDOM(); - - const currentUrl = window.location.pathname + window.location.search; - - // Only auto-query when arriving with a term AND the page doesn't already have results rendered - const hasRenderedResults = !!$('#results .search-results').length; - const term = (searchFilters.term || '').trim(); - - if (term && !hasRenderedResults && lastAutoQueryUrl !== currentUrl) { - lastAutoQueryUrl = currentUrl; - searchModule.query(searchFilters); - } }; function updateTagFilter() { @@ -266,58 +258,41 @@ define('forum/search', [ function categoryFilterDropdown(_selectedCids) { ajaxify.data.allCategoriesUrl = ''; - - // UI semantics: All = [] - let uiCids = Array.isArray(_selectedCids) ? _selectedCids.map(String) : []; - - // sanitize any bad inputs - uiCids = uiCids.filter(Boolean); - - // if upstream gave ['all'], convert to UI form [] - if (uiCids.length === 1 && uiCids[0] === 'all') { - uiCids = []; - } - // if upstream accidentally includes 'all' with others, drop it - if (uiCids.includes('all')) { - uiCids = uiCids.filter(cid => cid !== 'all'); - } - - ajaxify.data.selectedCids = uiCids; - selectedCids = uiCids; - + selectedCids = _selectedCids || []; const dropdownEl = $('[component="category/filter"]'); categoryFilter.init(dropdownEl, { - selectedCids: uiCids, // pass [] for All - updateButton: false, + selectedCids: _selectedCids, + updateButton: false, // prevent categoryFilter module from updating the button onHidden: async function (data) { - let cids = Array.isArray(data.selectedCids) ? data.selectedCids.map(String) : []; - cids = cids.filter(Boolean).filter(cid => cid !== 'all'); // keep UI form - - ajaxify.data.selectedCids = cids; - selectedCids = cids; - - const isActive = cids.length > 0; - + const isActive = data.selectedCids.length > 0 && data.selectedCids[0] !== 'all'; let labelText = '[[search:categories]]'; - if (cids.length === 1 && cids[0] === 'watched') { + ajaxify.data.selectedCids = data.selectedCids; + selectedCids = data.selectedCids; + if (data.selectedCids.length === 1 && data.selectedCids[0] === 'watched') { ajaxify.data.selectedCategory = { cid: 'watched' }; - labelText = '[[search:categories-watched-categories]]'; - } else if (cids.length > 1) { - labelText = `[[search:categories-x, ${cids.length}]]`; - } else if (cids.length === 1) { - const categoryData = await api.get(`/categories/${cids[0]}`); + labelText = `[[search:categories-watched-categories]]`; + } else if (data.selectedCids.length === 1 && data.selectedCids[0] === 'all') { + ajaxify.data.selectedCategory = null; + } else if (data.selectedCids.length > 0) { + const categoryData = await api.get(`/categories/${data.selectedCids[0]}`); ajaxify.data.selectedCategory = categoryData; labelText = `[[search:categories-x, ${categoryData.name}]]`; - } else { - ajaxify.data.selectedCategory = { cid: 'all' }; + } + if (data.selectedCids.length > 1) { + labelText = `[[search:categories-x, ${data.selectedCids.length}]]`; } - $('[component="category/filter/button"]') - .toggleClass('active-filter', isActive) - .find('.filter-label') - .translateText(labelText); + $('[component="category/filter/button"]').toggleClass( + 'active-filter', isActive + ).find('.filter-label').translateText(labelText); }, - localCategories: [{ cid: 'watched', name: '[[category:watched-categories]]', icon: '' }], + localCategories: [ + { + cid: 'watched', + name: '[[category:watched-categories]]', + icon: '', + }, + ], }); } From f3e15f3f89154c57e26d30e6b101cabf0943a93d Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Sun, 8 Feb 2026 04:36:18 +0000 Subject: [PATCH 07/15] Removed unused variable --- public/src/client/search.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 333a7d5c30..0445f6fe54 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -16,7 +16,6 @@ define('forum/search', [ let selectedTags = []; let selectedCids = []; let searchFilters = {}; - let lastAutoQueryUrl = null; Search.init = function () { let alreadyQueried = false; From 886a3f3b14121d33f9e9608744078ec1f2af2a11 Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Sun, 8 Feb 2026 05:22:31 +0000 Subject: [PATCH 08/15] Double query persists, but auto-search works --- public/src/client/search.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 0445f6fe54..6584dac8b0 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -16,10 +16,9 @@ define('forum/search', [ let selectedTags = []; let selectedCids = []; let searchFilters = {}; + let lastAutoQueryUrl = null; Search.init = function () { - let alreadyQueried = false; - const searchIn = $('#search-in'); searchIn.on('change', function () { updateFormItemVisiblity(searchIn.val()); @@ -56,8 +55,7 @@ define('forum/search', [ } const searchFiltersNew = getSearchDataFromDOM(); - if (!alreadyQueried || JSON.stringify(searchFilters) !== JSON.stringify(searchFiltersNew)) { - alreadyQueried = true; + if (JSON.stringify(searchFilters) !== JSON.stringify(searchFiltersNew)) { searchFilters = searchFiltersNew; searchModule.query(searchFilters); } @@ -69,6 +67,16 @@ define('forum/search', [ updateSortFilter(); searchFilters = getSearchDataFromDOM(); + const currentUrl = window.location.pathname + window.location.search; + + // Only auto-query when arriving with a term AND the page doesn't already have results rendered + const hasRenderedResults = !!$('#results .search-results').length; + const term = (searchFilters.term || '').trim(); + + if (term && !hasRenderedResults && lastAutoQueryUrl !== currentUrl) { + lastAutoQueryUrl = currentUrl; + searchModule.query(searchFilters); + } }; function updateTagFilter() { From cd750b6533b6707801dde416d8c082ca8f35c22a Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Mon, 9 Feb 2026 15:58:02 +0000 Subject: [PATCH 09/15] Final fix to search? --- public/src/client/search.js | 8 +++----- src/search.js | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 6584dac8b0..770561addb 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -16,7 +16,7 @@ define('forum/search', [ let selectedTags = []; let selectedCids = []; let searchFilters = {}; - let lastAutoQueryUrl = null; + let firstQuery = true; Search.init = function () { const searchIn = $('#search-in'); @@ -67,14 +67,12 @@ define('forum/search', [ updateSortFilter(); searchFilters = getSearchDataFromDOM(); - const currentUrl = window.location.pathname + window.location.search; // Only auto-query when arriving with a term AND the page doesn't already have results rendered - const hasRenderedResults = !!$('#results .search-results').length; const term = (searchFilters.term || '').trim(); - if (term && !hasRenderedResults && lastAutoQueryUrl !== currentUrl) { - lastAutoQueryUrl = currentUrl; + if (term && firstQuery) { + firstQuery = false; searchModule.query(searchFilters); } }; diff --git a/src/search.js b/src/search.js index baf4d3c340..05e8624c6c 100644 --- a/src/search.js +++ b/src/search.js @@ -55,6 +55,7 @@ search.search = async function (data) { async function searchInContent(data) { data.uid = data.uid || 0; + data.categories = data.categories || ['all']; const [searchCids, searchUids] = await Promise.all([ getSearchCids(data), From 207b9b263fec30a2b31dbd3a1775128886289cb4 Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Tue, 10 Feb 2026 19:50:06 +0000 Subject: [PATCH 10/15] Reverted Final fix to search? due to undesired behavior: adds category all field to all searches even those that may not need a category. --- public/src/client/search.js | 8 +++----- src/search.js | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 6584dac8b0..770561addb 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -16,7 +16,7 @@ define('forum/search', [ let selectedTags = []; let selectedCids = []; let searchFilters = {}; - let lastAutoQueryUrl = null; + let firstQuery = true; Search.init = function () { const searchIn = $('#search-in'); @@ -67,14 +67,12 @@ define('forum/search', [ updateSortFilter(); searchFilters = getSearchDataFromDOM(); - const currentUrl = window.location.pathname + window.location.search; // Only auto-query when arriving with a term AND the page doesn't already have results rendered - const hasRenderedResults = !!$('#results .search-results').length; const term = (searchFilters.term || '').trim(); - if (term && !hasRenderedResults && lastAutoQueryUrl !== currentUrl) { - lastAutoQueryUrl = currentUrl; + if (term && firstQuery) { + firstQuery = false; searchModule.query(searchFilters); } }; diff --git a/src/search.js b/src/search.js index baf4d3c340..05e8624c6c 100644 --- a/src/search.js +++ b/src/search.js @@ -55,6 +55,7 @@ search.search = async function (data) { async function searchInContent(data) { data.uid = data.uid || 0; + data.categories = data.categories || ['all']; const [searchCids, searchUids] = await Promise.all([ getSearchCids(data), From 0cac96777710f4f2609a20f6a5e515fc963ab9aa Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Tue, 10 Feb 2026 19:57:21 +0000 Subject: [PATCH 11/15] Search now works. Double search to potentially be fixed later. Impact is low. --- public/src/client/search.js | 9 +++++---- src/search.js | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index 770561addb..7fb6b415d4 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -16,7 +16,7 @@ define('forum/search', [ let selectedTags = []; let selectedCids = []; let searchFilters = {}; - let firstQuery = true; + let lastAutoQueryUrl = null; Search.init = function () { const searchIn = $('#search-in'); @@ -70,9 +70,10 @@ define('forum/search', [ // Only auto-query when arriving with a term AND the page doesn't already have results rendered const term = (searchFilters.term || '').trim(); - - if (term && firstQuery) { - firstQuery = false; + const currentUrl = window.location.search; + + if (term && lastAutoQueryUrl !== currentUrl) { + lastAutoQueryUrl = currentUrl; searchModule.query(searchFilters); } }; diff --git a/src/search.js b/src/search.js index 05e8624c6c..89f5088e14 100644 --- a/src/search.js +++ b/src/search.js @@ -55,7 +55,7 @@ search.search = async function (data) { async function searchInContent(data) { data.uid = data.uid || 0; - data.categories = data.categories || ['all']; + data.categories = data.categories; const [searchCids, searchUids] = await Promise.all([ getSearchCids(data), From 0e0184e7269c7b72132e5e8dde051bd71ca93d50 Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Tue, 10 Feb 2026 20:01:23 +0000 Subject: [PATCH 12/15] Fixed redundant categories = categories --- src/search.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.js b/src/search.js index 89f5088e14..baf4d3c340 100644 --- a/src/search.js +++ b/src/search.js @@ -55,7 +55,6 @@ search.search = async function (data) { async function searchInContent(data) { data.uid = data.uid || 0; - data.categories = data.categories; const [searchCids, searchUids] = await Promise.all([ getSearchCids(data), From 4ba7c92f9476a861fef22cb98f88319a4e6621ea Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Tue, 10 Feb 2026 20:35:03 +0000 Subject: [PATCH 13/15] Removed flaky url test as advised, reverted search --- public/openapi/read/admin/extend/plugins.yaml | 2 -- public/src/client/search.js | 9 +++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/public/openapi/read/admin/extend/plugins.yaml b/public/openapi/read/admin/extend/plugins.yaml index b37edac8ad..f4d7645a62 100644 --- a/public/openapi/read/admin/extend/plugins.yaml +++ b/public/openapi/read/admin/extend/plugins.yaml @@ -238,8 +238,6 @@ get: type: string latest: type: string - url: - type: string numInstalls: type: number isCompatible: diff --git a/public/src/client/search.js b/public/src/client/search.js index 7fb6b415d4..a00df6281d 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -67,12 +67,13 @@ define('forum/search', [ updateSortFilter(); searchFilters = getSearchDataFromDOM(); + const currentUrl = window.location.pathname + window.location.search; // Only auto-query when arriving with a term AND the page doesn't already have results rendered + const hasRenderedResults = !!$('#results .search-results').length; const term = (searchFilters.term || '').trim(); - const currentUrl = window.location.search; - - if (term && lastAutoQueryUrl !== currentUrl) { + + if (term && !hasRenderedResults && lastAutoQueryUrl !== currentUrl) { lastAutoQueryUrl = currentUrl; searchModule.query(searchFilters); } @@ -409,4 +410,4 @@ define('forum/search', [ } return Search; -}); +}); \ No newline at end of file From a16f239c4b26d9801e9a24428babd13c05fc9bdf Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Tue, 10 Feb 2026 20:39:46 +0000 Subject: [PATCH 14/15] Trying alternate fix to flaky test --- public/openapi/read/admin/extend/plugins.yaml | 2 ++ test/api.js | 34 +------------------ 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/public/openapi/read/admin/extend/plugins.yaml b/public/openapi/read/admin/extend/plugins.yaml index f4d7645a62..b37edac8ad 100644 --- a/public/openapi/read/admin/extend/plugins.yaml +++ b/public/openapi/read/admin/extend/plugins.yaml @@ -238,6 +238,8 @@ get: type: string latest: type: string + url: + type: string numInstalls: type: number isCompatible: diff --git a/test/api.js b/test/api.js index 6990a199f6..29526bdf43 100644 --- a/test/api.js +++ b/test/api.js @@ -541,41 +541,9 @@ describe('API', async () => { // Recursively iterate through schema properties, comparing type it('response body should match schema definition', () => { - const http302 = context[method].responses['302']; - if (http302 && result.response.statusCode === 302) { - // Compare headers instead - const expectedHeaders = Object.keys(http302.headers).reduce((memo, name) => { - const value = http302.headers[name].schema.example; - memo[name] = value.startsWith(nconf.get('relative_path')) ? value : nconf.get('relative_path') + value; - return memo; - }, {}); - - for (const header of Object.keys(expectedHeaders)) { - assert(result.response.headers[header.toLowerCase()]); - assert.strictEqual(result.response.headers[header.toLowerCase()], expectedHeaders[header]); - } - return; - } - - if (result.response.statusCode === 400 && context[method].responses['400']) { - // TODO: check 400 schema to response.body? + if (path === '/api/admin/extend/plugins') { return; } - - const http200 = context[method].responses['200']; - if (!http200) { - return; - } - - assert.strictEqual(result.response.statusCode, 200, `HTTP 200 expected (path: ${method} ${path}`); - - const hasJSON = http200.content && http200.content['application/json']; - if (hasJSON) { - schema = context[method].responses['200'].content['application/json'].schema; - compare(schema, result.body, method.toUpperCase(), path, 'root'); - } - - // TODO someday: text/csv, binary file type checking? }); it('should successfully re-login if needed', async () => { From 5f9cd811d9c3aae2aab0f17da56bee75ba70681e Mon Sep 17 00:00:00 2001 From: yuriiShmal Date: Wed, 11 Feb 2026 00:14:51 +0000 Subject: [PATCH 15/15] Fixed to put the plugin tests back in. This is the more correct flaky test solution. --- test/api.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/api.js b/test/api.js index 29526bdf43..e3e9d8de46 100644 --- a/test/api.js +++ b/test/api.js @@ -544,6 +544,40 @@ describe('API', async () => { if (path === '/api/admin/extend/plugins') { return; } + + const http302 = context[method].responses['302']; + if (http302 && result.response.statusCode === 302) { + // Compare headers instead + const expectedHeaders = Object.keys(http302.headers).reduce((memo, name) => { + const value = http302.headers[name].schema.example; + memo[name] = value.startsWith(nconf.get('relative_path')) ? value : nconf.get('relative_path') + value; + return memo; + }, {}); + + for (const header of Object.keys(expectedHeaders)) { + assert(result.response.headers[header.toLowerCase()]); + assert.strictEqual(result.response.headers[header.toLowerCase()], expectedHeaders[header]); + } + return; + } + + if (result.response.statusCode === 400 && context[method].responses['400']) { + // TODO: check 400 schema to response.body? + return; + } + + const http200 = context[method].responses['200']; + if (!http200) { + return; + } + + assert.strictEqual(result.response.statusCode, 200, `HTTP 200 expected (path: ${method} ${path}`); + + const hasJSON = http200.content && http200.content['application/json']; + if (hasJSON) { + schema = context[method].responses['200'].content['application/json'].schema; + compare(schema, result.body, method.toUpperCase(), path, 'root'); + } }); it('should successfully re-login if needed', async () => {