From d0b9355beb6f7f9e16367ed45eed69f72f207fd6 Mon Sep 17 00:00:00 2001 From: Roland Date: Wed, 7 Oct 2026 16:56:26 -0600 Subject: [PATCH] Add Domains to the Other search tab Domains match the search text by name and/or any of the Tags, like Channels. The list and its estimate share one query. Co-Authored-By: Claude Opus 5.5 --- app/src/api.js | 10 +++++ app/src/components/OtherSearchView.test.js | 42 +++++++++++++++++++- app/src/components/Search.js | 24 ++++++++++- app/src/hooks/customHooks.js | 22 +++++++++++ modules/archive/api.py | 11 ++++++ modules/archive/lib.py | 27 ++++++++++++- modules/archive/schema.py | 6 +++ modules/archive/test/test_domain_search.py | 46 ++++++++++++++++++++++ modules/repos/test/test_search.py | 6 +-- wrolpi/common.py | 11 ++++-- 10 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 modules/archive/test/test_domain_search.py diff --git a/app/src/api.js b/app/src/api.js index 6b2b9673..3d8a6488 100644 --- a/app/src/api.js +++ b/app/src/api.js @@ -2129,6 +2129,16 @@ export async function searchChannels(tagNames, searchStr) { } } +// Domains whose name matches `searchStr` and/or which have any of the tags. +export async function searchDomains(tagNames, searchStr) { + const body = {tag_names: tagNames, search_str: searchStr}; + const response = await apiPost(`${ARCHIVES_API}/domains/search`, body); + if (response.ok) { + const content = await response.json(); + return {domains: content.domains}; + } +} + export async function getOutdatedZims() { const response = await apiGet(`${API_URI}/zim/outdated`); if (response.status === 200) { diff --git a/app/src/components/OtherSearchView.test.js b/app/src/components/OtherSearchView.test.js index e2526ec3..468a6000 100644 --- a/app/src/components/OtherSearchView.test.js +++ b/app/src/components/OtherSearchView.test.js @@ -1,7 +1,7 @@ import React from 'react'; import {act, render, screen} from '../test-utils'; import {OtherSearchView} from './Search'; -import {searchChannels, searchRepos} from '../api'; +import {searchChannels, searchDomains, searchRepos} from '../api'; // What the search page's Other tab shows for each state of useSearchChannels. "No Channels" // must only appear once the server has confirmed there are none. @@ -9,6 +9,7 @@ import {searchChannels, searchRepos} from '../api'; jest.mock('../api', () => ({ ...jest.requireActual('../api'), searchChannels: jest.fn(), + searchDomains: jest.fn(), searchRepos: jest.fn(), })); @@ -18,6 +19,8 @@ beforeEach(() => { searchChannels.mockReset(); searchRepos.mockReset(); searchRepos.mockResolvedValue({repos: [], total: 0}); + searchDomains.mockReset(); + searchDomains.mockResolvedValue({domains: []}); }); test('pending: shows a loader, not "No Channels"', () => { @@ -117,3 +120,40 @@ describe('Repos', () => { expect(screen.getByText(/files