Selaa lähdekoodia

Merge pull request #899 from chain710/main

Support multi search providers
shamoon 2 vuotta sitten
vanhempi
commit
ad299e9c94
2 muutettua tiedostoa jossa 102 lisäystä ja 18 poistoa
  1. 91 12
      src/components/widgets/search/search.jsx
  2. 11 6
      src/pages/index.jsx

+ 91 - 12
src/components/widgets/search/search.jsx

@@ -1,7 +1,9 @@
-import { useState } from "react";
+import { useState, useEffect, Fragment } from "react";
 import { useTranslation } from "next-i18next";
 import { useTranslation } from "next-i18next";
 import { FiSearch } from "react-icons/fi";
 import { FiSearch } from "react-icons/fi";
 import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si";
 import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si";
+import { Listbox, Transition } from "@headlessui/react";
+import classNames from "classnames";
 
 
 export const searchProviders = {
 export const searchProviders = {
   google: {
   google: {
@@ -36,21 +38,55 @@ export const searchProviders = {
   },
   },
 };
 };
 
 
+function getAvailableProviderIds(options) {
+  if (options.provider && Array.isArray(options.provider)) {
+    return Object.keys(searchProviders).filter((value) => options.provider.includes(value));
+  }
+  if (options.provider && searchProviders[options.provider]) {
+    return [options.provider];
+  }
+  return null;
+}
+
+const localStorageKey = "search-name";
+
+export function getStoredProvider() {
+  if (typeof window !== 'undefined') {
+    const storedName = localStorage.getItem(localStorageKey);
+    if (storedName) {
+      return Object.values(searchProviders).find((el) => el.name === storedName);
+    }
+  }
+  return null;
+}
+
 export default function Search({ options }) {
 export default function Search({ options }) {
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
-  const provider = searchProviders[options.provider];
+  const availableProviderIds = getAvailableProviderIds(options);
+
   const [query, setQuery] = useState("");
   const [query, setQuery] = useState("");
+  const [selectedProvider, setSelectedProvider] = useState(searchProviders[availableProviderIds[0] ?? searchProviders.google]);
 
 
-  if (!provider) {
+  useEffect(() => {
+    const storedProvider = getStoredProvider();
+    let storedProviderKey = null;
+    storedProviderKey = Object.keys(searchProviders).find((pkey) => searchProviders[pkey] === storedProvider);
+    if (storedProvider && availableProviderIds.includes(storedProviderKey)) {
+      setSelectedProvider(storedProvider);
+    }
+  }, [availableProviderIds]);
+  
+  if (!availableProviderIds) {
     return null;
     return null;
   }
   }
 
 
   function handleSubmit(event) {
   function handleSubmit(event) {
     const q = encodeURIComponent(query);
     const q = encodeURIComponent(query);
 
 
-    if (provider.url) {
-      window.open(`${provider.url}${q}`, options.target || "_blank");
+    const url = { selectedProvider };
+    if (url) {
+      window.open(`${url}${q}`, options.target || "_blank");
     } else {
     } else {
       window.open(`${options.url}${q}`, options.target || "_blank");
       window.open(`${options.url}${q}`, options.target || "_blank");
     }
     }
@@ -60,6 +96,11 @@ export default function Search({ options }) {
     setQuery("");
     setQuery("");
   }
   }
 
 
+  const onChangeProvider = (provider) => {
+    setSelectedProvider(provider);
+    localStorage.setItem(localStorageKey, provider.name);
+  }
+
   return (
   return (
     <form className="flex-col relative h-8 my-4 min-w-fit grow first:ml-0 ml-4" onSubmit={handleSubmit}>
     <form className="flex-col relative h-8 my-4 min-w-fit grow first:ml-0 ml-4" onSubmit={handleSubmit}>
       <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-white" />
       <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-white" />
@@ -82,17 +123,55 @@ export default function Search({ options }) {
         // eslint-disable-next-line jsx-a11y/no-autofocus
         // eslint-disable-next-line jsx-a11y/no-autofocus
         autoFocus={options.focus}
         autoFocus={options.focus}
       />
       />
-      <button
-        type="submit"
-        className="
+      <Listbox as="div" value={selectedProvider} onChange={onChangeProvider} className="relative text-left" disabled={availableProviderIds?.length === 1}>
+        <div>
+          <Listbox.Button
+            className="
         absolute right-0.5 bottom-0.5 rounded-r-md px-4 py-2 border-1
         absolute right-0.5 bottom-0.5 rounded-r-md px-4 py-2 border-1
         text-white font-medium text-sm
         text-white font-medium text-sm
         bg-theme-600/40 dark:bg-white/10
         bg-theme-600/40 dark:bg-white/10
         focus:ring-theme-500 dark:focus:ring-white/50"
         focus:ring-theme-500 dark:focus:ring-white/50"
-      >
-        <provider.icon className="text-white w-3 h-3" />
-        <span className="sr-only">{t("search.search")}</span>
-      </button>
+          >
+            <selectedProvider.icon className="text-white w-3 h-3" />
+            <span className="sr-only">{t("search.search")}</span>
+          </Listbox.Button>
+        </div>
+        <Transition
+          as={Fragment}
+          enter="transition ease-out duration-100"
+          enterFrom="transform opacity-0 scale-95"
+          enterTo="transform opacity-100 scale-100"
+          leave="transition ease-in duration-75"
+          leaveFrom="transform opacity-100 scale-100"
+          leaveTo="transform opacity-0 scale-95"
+        >
+          <Listbox.Options
+            className="absolute right-0 z-10 mt-1 origin-top-right rounded-md 
+            bg-theme-100 dark:bg-theme-600 shadow-lg 
+            ring-1 ring-black ring-opacity-5 focus:outline-none"
+          >
+            <div className="flex flex-col">
+              {availableProviderIds.map((providerId) => {
+                const p = searchProviders[providerId];
+                return (
+                  <Listbox.Option key={providerId} value={p} as={Fragment}>
+                    {({ active }) => (
+                      <li
+                        className={classNames(
+                          "rounded-md cursor-pointer",
+                          active ? "bg-theme-600/10 dark:bg-white/10 dark:text-gray-900" : "dark:text-gray-100"
+                        )}
+                      >
+                        <p.icon className="h-4 w-4 mx-4 my-2" />
+                      </li>
+                    )}
+                  </Listbox.Option>
+                );
+              })}
+            </div>
+          </Listbox.Options>
+        </Transition>
+      </Listbox>
     </form>
     </form>
   );
   );
 }
 }

+ 11 - 6
src/pages/index.jsx

@@ -22,7 +22,7 @@ import { bookmarksResponse, servicesResponse, widgetsResponse } from "utils/conf
 import ErrorBoundary from "components/errorboundry";
 import ErrorBoundary from "components/errorboundry";
 import themes from "utils/styles/themes";
 import themes from "utils/styles/themes";
 import QuickLaunch from "components/quicklaunch";
 import QuickLaunch from "components/quicklaunch";
-import { searchProviders } from "components/widgets/search/search";
+import { getStoredProvider, searchProviders } from "components/widgets/search/search";
 
 
 const ThemeToggle = dynamic(() => import("components/toggles/theme"), {
 const ThemeToggle = dynamic(() => import("components/toggles/theme"), {
   ssr: false,
   ssr: false,
@@ -197,12 +197,17 @@ function Home({ initialSettings }) {
   let searchProvider = null;
   let searchProvider = null;
   const searchWidget = Object.values(widgets).find(w => w.type === "search");
   const searchWidget = Object.values(widgets).find(w => w.type === "search");
   if (searchWidget) {
   if (searchWidget) {
-    if (searchWidget.options?.provider === 'custom') {
-      searchProvider = {
-        url: searchWidget.options.url
-      }
+    if (Array.isArray(searchWidget.options?.provider)) {
+      // if search provider is a list, try to retrieve from localstorage, fall back to the first
+      searchProvider = getStoredProvider() ?? searchProviders[searchWidget.options.provider[0]];
     } else {
     } else {
-      searchProvider = searchProviders[searchWidget.options?.provider];
+      if (searchWidget.options?.provider === 'custom') {
+        searchProvider = {
+          url: searchWidget.options.url
+        }
+      } else {
+        searchProvider = searchProviders[searchWidget.options?.provider];
+      }
     }
     }
   }
   }