From 377c69e8a742d8ff27bda8e946af8a13487c843e Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 1 Jan 2024 11:31:17 -0700 Subject: [PATCH] Adding search icon button to navbar. --- app/src/DashboardPage.js | 13 +++- app/src/api.js | 21 ++++-- app/src/components/Nav.js | 2 + app/src/components/Search.js | 127 ++++++++++++++++++++++++++++------ app/src/components/Zim.js | 9 ++- modules/zim/api.py | 34 +++++++++ modules/zim/schema.py | 6 ++ modules/zim/test/test_api.py | 12 ++++ wrolpi/files/api.py | 14 ++++ wrolpi/files/schema.py | 6 ++ wrolpi/files/test/test_api.py | 12 ++++ wrolpi/root_api.py | 37 ---------- wrolpi/test/test_root_api.py | 15 ---- 13 files changed, 222 insertions(+), 86 deletions(-) diff --git a/app/src/DashboardPage.js b/app/src/DashboardPage.js index 04ff4f24..a3ac7352 100644 --- a/app/src/DashboardPage.js +++ b/app/src/DashboardPage.js @@ -154,13 +154,22 @@ export function Getters() { } export function DashboardPage() { - const navigate = useNavigate(); const {searchStr, setSearchStr, activeTags} = useSearch(); - const {suggestionsResults, handleResultSelect, resultRenderer, loading} = useContext(SearchSuggestionsContext); + const { + suggestionsResults, + handleResultSelect, + resultRenderer, + loading, + setSearchStr: setSuggestionSearchStr + } = useContext(SearchSuggestionsContext); const {status} = useContext(StatusContext); + React.useEffect(() => { + setSuggestionSearchStr(searchStr); + }, [searchStr]) + let title = 'Dashboard'; if (searchStr) { title = `Search: ${searchStr} - Dashboard`; diff --git a/app/src/api.js b/app/src/api.js index 3b4d9628..142b4c2c 100644 --- a/app/src/api.js +++ b/app/src/api.js @@ -1022,17 +1022,24 @@ export async function searchSuggestions(search_str) { } } -export async function searchEstimate(search_str, tagNames) { +export async function searchEstimateFiles(search_str, tagNames) { const body = {search_str: search_str, tag_names: tagNames}; - const response = await apiPost(`${API_URI}/search_estimates`, body); + const response = await apiPost(`${API_URI}/files/search_estimates`, body); if (response.ok) { const content = await response.json(); - - const fileGroups = content.file_groups; - const zimsEstimates = content.zims_estimates; return { - fileGroups, - zimsEstimates, + fileGroups: content.file_groups, + } + } +} + +export async function searchEstimateZims(search_str, tagNames) { + const body = {search_str: search_str, tag_names: tagNames}; + const response = await apiPost(`${API_URI}/zim/search_estimates`, body); + if (response.ok) { + const content = await response.json(); + return { + zimsEstimates: content.zims_estimates, } } } diff --git a/app/src/components/Nav.js b/app/src/components/Nav.js index 18bfaaab..2a0d409e 100644 --- a/app/src/components/Nav.js +++ b/app/src/components/Nav.js @@ -6,6 +6,7 @@ import Icon from "semantic-ui-react/dist/commonjs/elements/Icon"; import {CPUTemperatureIcon, DarkModeToggle, HotspotStatusIcon, NAME, SystemLoadIcon} from "./Common"; import {ShareButton} from "./Share"; import {useWROLMode} from "../hooks/customHooks"; +import {SearchIconButton} from "./Search"; const links = [ {text: 'Videos', to: '/videos', key: 'videos'}, @@ -76,6 +77,7 @@ export function NavBar() { + ; return <> diff --git a/app/src/components/Search.js b/app/src/components/Search.js index 4b33e81e..ebe24404 100644 --- a/app/src/components/Search.js +++ b/app/src/components/Search.js @@ -1,13 +1,15 @@ -import React, {useState} from "react"; +import React, {useContext, useState} from "react"; import {createSearchParams, Route, Routes, useNavigate} from "react-router-dom"; -import {FilesSearchView} from "./Files"; +import {FileSearchFilterButton, FilesSearchView} from "./Files"; import {useLatestRequest, usePages, useQuery} from "../hooks/customHooks"; import {ZimSearchView} from "./Zim"; -import {searchEstimate, searchSuggestions} from "../api"; -import {fuzzyMatch, normalizeEstimate, TabLinks} from "./Common"; +import {searchEstimateFiles, searchEstimateZims, searchSuggestions} from "../api"; +import {fuzzyMatch, normalizeEstimate, SearchResultsInput, TabLinks} from "./Common"; import _ from "lodash"; import {TagsContext} from "../Tags"; import {Header as SHeader, Label} from "semantic-ui-react"; +import {Button, Modal, ModalContent} from "./Theme"; +import Grid from "semantic-ui-react/dist/commonjs/collections/Grid"; export const SearchSuggestionsContext = React.createContext({ suggestionsResults: {}, @@ -16,6 +18,8 @@ export const SearchSuggestionsContext = React.createContext({ handleResultSelect: null, resultRenderer: null, loading: false, + searchStr: '', + setSearchStr: null, }); @@ -113,10 +117,10 @@ export function useSuggestions(searchStr) { } -export function useSearchSuggestions() { +export function useSearchSuggestions(defaultSearchStr) { const navigate = useNavigate(); + const [searchStr, setSearchStr] = React.useState(defaultSearchStr || ''); const {SingleTag, fuzzyMatchTagsByName} = React.useContext(TagsContext); - const {searchStr} = useSearch(); const {suggestions, loading} = useSuggestions(searchStr); // The results that will be displayed by . @@ -226,43 +230,73 @@ export function useSearchSuggestions() { return {title} }; - return {suggestions, suggestionsResults, suggestionsSums, searchStr, handleResultSelect, resultRenderer, loading} + return { + suggestions, + suggestionsResults, + suggestionsSums, + searchStr, + setSearchStr, + handleResultSelect, + resultRenderer, + loading + } } export function useSearchEstimate(searchStr, tagNames) { - const {data, sendRequest, loading} = useLatestRequest(250); + const {data: filesData, sendRequest: sendFilesRequest, loading: loadingFiles} = useLatestRequest(250); + const {data: zimsData, sendRequest: sendZimsRequest, loading: loadingZims} = useLatestRequest(250); // The estimates from the response. - const [estimates, setEstimates] = React.useState({}); + const [estimates, setEstimates] = React.useState({ + fileGroups: [], + zims: [], + }); // The estimates summarized. - const [estimatesSums, setEstimatesSums] = React.useState({}); + const [estimatesSums, setEstimatesSums] = React.useState({ + fileGroups: 0, + zims: 0, + }); React.useEffect(() => { // Handle results from `sendRequest`. - if (_.isEmpty(data)) { + if (_.isEmpty(filesData)) { return; } - setEstimates(data); - setEstimatesSums({ - fileGroups: data.fileGroups, - zims: data.zimsEstimates.reduce((i, j) => i + j['estimate'], 0), - }); - }, [JSON.stringify(data)]); + setEstimates({...estimates, fileGroups: filesData.fileGroups}); + setEstimatesSums({...estimatesSums, fileGroups: filesData.fileGroups}); + }, [JSON.stringify(filesData)]); React.useEffect(() => { - // Use the `useLatestRequest` to handle user typing. - sendRequest(async () => await searchEstimate(searchStr, tagNames)); - }, [searchStr, JSON.stringify(tagNames)], sendRequest); + sendFilesRequest(async () => await searchEstimateFiles(searchStr, tagNames)); + }, [searchStr, JSON.stringify(tagNames)], sendFilesRequest); - return {estimates, estimatesSums, loading} + React.useEffect(() => { + // Handle results from `sendRequest`. + if (_.isEmpty(zimsData)) { + return; + } + + setEstimates({...estimates, zimsEstimates: zimsData.zimsEstimates}); + setEstimatesSums({...estimatesSums, zims: zimsData.zimsEstimates.reduce((i, j) => i + j['estimate'], 0)}); + }, [JSON.stringify(zimsData)]); + + React.useEffect(() => { + sendZimsRequest(async () => await searchEstimateZims(searchStr, tagNames)); + }, [searchStr, JSON.stringify(tagNames)], sendZimsRequest); + + return { + estimates, + estimatesSums, + loading: loadingFiles || loadingZims, + } } export function SearchView() { const {searchStr, activeTags} = useSearch(); - const {estimates, estimatesSums} = useSearchEstimate(searchStr, activeTags); + const {estimates, estimatesSums, loading} = useSearchEstimate(searchStr, activeTags); let filesTabName = Files ; let zimsTabName = Zims; @@ -280,7 +314,54 @@ export function SearchView() { }/> - }/> + }/> } + +export function SearchIconButton() { + // A single button which displays a modal for search suggestions. + const { + suggestionsResults, + handleResultSelect, + resultRenderer, + loading, + searchStr, + setSearchStr, + } = useContext(SearchSuggestionsContext); + const [open, setOpen] = React.useState(false); + + const localHandleResultSelect = (i) => { + // Close modal when user selects a result. + setOpen(false); + handleResultSelect(i); + } + + return +