From cc738398748917a20d011349f25ccb7cc48e1adb Mon Sep 17 00:00:00 2001 From: Flo2410 Date: Fri, 26 Jan 2024 21:57:20 +0000 Subject: [PATCH] Added search suggestions to the quick launch menu. If this feture and the search feature of the `quicklaunch` are turned on, up to *four* search suggestions will be shown as results. --- src/components/quicklaunch.jsx | 95 +++++++++++++++--------- src/components/widgets/search/search.jsx | 6 ++ src/pages/api/searchSuggestion.js | 14 ++++ 3 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 src/pages/api/searchSuggestion.js diff --git a/src/components/quicklaunch.jsx b/src/components/quicklaunch.jsx index 7fb1460a..ef45686f 100644 --- a/src/components/quicklaunch.jsx +++ b/src/components/quicklaunch.jsx @@ -16,9 +16,9 @@ export default function QuickLaunch({ }) { const { t } = useTranslation(); const { settings } = useContext(SettingsContext); - const { searchDescriptions, hideVisitURL } = settings?.quicklaunch + const { searchDescriptions, hideVisitURL, hideSearchSuggestions } = settings?.quicklaunch ? settings.quicklaunch - : { searchDescriptions: false, hideVisitURL: false }; + : { searchDescriptions: false, hideVisitURL: false, hideSearchSuggestions: false }; const searchField = useRef(); @@ -90,45 +90,66 @@ export default function QuickLaunch({ } useEffect(() => { - if (searchString.length === 0) setResults([]); - else { - let newResults = servicesAndBookmarks.filter((r) => { - const nameMatch = r.name.toLowerCase().includes(searchString); - let descriptionMatch; + (async () => { + if (searchString.length === 0) setResults([]); + else { + let newResults = servicesAndBookmarks.filter((r) => { + const nameMatch = r.name.toLowerCase().includes(searchString); + let descriptionMatch; + if (searchDescriptions) { + descriptionMatch = r.description?.toLowerCase().includes(searchString); + r.priority = nameMatch ? 2 * +nameMatch : +descriptionMatch; // eslint-disable-line no-param-reassign + } + return nameMatch || descriptionMatch; + }); + if (searchDescriptions) { - descriptionMatch = r.description?.toLowerCase().includes(searchString); - r.priority = nameMatch ? 2 * +nameMatch : +descriptionMatch; // eslint-disable-line no-param-reassign + newResults = newResults.sort((a, b) => b.priority - a.priority); } - return nameMatch || descriptionMatch; - }); - if (searchDescriptions) { - newResults = newResults.sort((a, b) => b.priority - a.priority); + if (searchProvider) { + newResults.push({ + href: searchProvider.url + encodeURIComponent(searchString), + name: `${searchProvider.name ?? t("quicklaunch.custom")} ${t("quicklaunch.search")}`, + type: "search", + }); + + if (!hideSearchSuggestions) { + const searchSuggestionResult = await fetch(`/api/searchSuggestion?query=${encodeURIComponent(searchString)}&providerName=${searchProvider.name}`); + const searchSuggestion = (await searchSuggestionResult.json())[1]; + + // Check if there is a search suggestion + if (searchSuggestion) { + // Restrict the searchSuggestion to 4 entries + if (searchSuggestion.length - 4 > 0) { + searchSuggestion.splice(-(searchSuggestion.length-4)); + } + + newResults = newResults.concat(searchSuggestion.map((suggestion)=>({ + href: searchProvider.url + encodeURIComponent(suggestion), + name: suggestion, + type: "search", + }))); + } + } + } + + if (!hideVisitURL && url) { + newResults.unshift({ + href: url.toString(), + name: `${t("quicklaunch.visit")} URL`, + type: "url", + }); + } + + setResults(newResults); + + if (newResults.length) { + setCurrentItemIndex(0); + } } - - if (searchProvider) { - newResults.push({ - href: searchProvider.url + encodeURIComponent(searchString), - name: `${searchProvider.name ?? t("quicklaunch.custom")} ${t("quicklaunch.search")} `, - type: "search", - }); - } - - if (!hideVisitURL && url) { - newResults.unshift({ - href: url.toString(), - name: `${t("quicklaunch.visit")} URL`, - type: "url", - }); - } - - setResults(newResults); - - if (newResults.length) { - setCurrentItemIndex(0); - } - } - }, [searchString, servicesAndBookmarks, searchDescriptions, hideVisitURL, searchProvider, url, t]); + })() + }, [searchString, servicesAndBookmarks, searchDescriptions, hideVisitURL, hideSearchSuggestions, searchProvider, url, t]); const [hidden, setHidden] = useState(true); useEffect(() => { diff --git a/src/components/widgets/search/search.jsx b/src/components/widgets/search/search.jsx index 0e439132..5c97a92d 100644 --- a/src/components/widgets/search/search.jsx +++ b/src/components/widgets/search/search.jsx @@ -12,31 +12,37 @@ export const searchProviders = { google: { name: "Google", url: "https://www.google.com/search?q=", + suggestionUrl: "https://www.google.com/complete/search?client=chrome&q=home", icon: SiGoogle, }, duckduckgo: { name: "DuckDuckGo", url: "https://duckduckgo.com/?q=", + suggestionUrl: "https://duckduckgo.com/ac/?type=list&q=", icon: SiDuckduckgo, }, bing: { name: "Bing", url: "https://www.bing.com/search?q=", + suggestionUrl: "https://api.bing.com/osjson.aspx?query=", icon: SiMicrosoftbing, }, baidu: { name: "Baidu", url: "https://www.baidu.com/s?wd=", + suggestionUrl: "http://suggestion.baidu.com/su?&action=opensearch&ie=utf-8&wd=", icon: SiBaidu, }, brave: { name: "Brave", url: "https://search.brave.com/search?q=", + suggestionUrl: "https://search.brave.com/api/suggest?&rich=false&q=", icon: SiBrave, }, custom: { name: "Custom", url: false, + suggestionUrl: false, icon: FiSearch, }, }; diff --git a/src/pages/api/searchSuggestion.js b/src/pages/api/searchSuggestion.js new file mode 100644 index 00000000..5480eb5f --- /dev/null +++ b/src/pages/api/searchSuggestion.js @@ -0,0 +1,14 @@ +import { searchProviders } from "components/widgets/search/search"; +import cachedFetch from "utils/proxy/cached-fetch"; + +export default async function handler(req, res) { + const { query, providerName } = req.query; + + const provider = Object.values(searchProviders).find(({ name }) => name === providerName); + + if (!provider.suggestionUrl) { + return res.json([query, []]); // Responde with the same array format but with no suggestions. + } + + return res.send(await cachedFetch(`${provider.suggestionUrl}${query}`, 5)); +}