mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
* feat: impl Coco server related APIs * chore: remove unused method * fix: invoke Rust interfaces in tauri::run() * chore: add invoke * feat: add add_coco_server * fix: trim the tailing forward slash * feat: interface get_user_profiles * chore: add * fix: store the servers in add interface * chore: ass * fix: skip non-publich servers with no token * feat: add * feat: get datasources and connectors * fix: invoke interfaces in tauri::run() * chore: add SidebarRef * refactor: refactoring coco-app * refactor: refactoring coco app * refactor: refactoring project layout * refactor: refactoring server management * chore: cleanup code * chore: display error when connect failed * refactor: refactoring refresh server's info * refactor: refactoring how to connect the coco serverg * chore: rename to cloud * refactor: refactoring remove coco server * fix: refresh current selected server * fix: reset server selection * chore: update login status * feat: add error message tips * fix: fix login and logout * refactor: refactoring http client * fix: fix the datasources * chore: minor fix * refactor: refactoring code * fix: fix search api * chore: optimize part of icons * chore: fix build * refactor: search list icon * refactor: search list icon * chore: lib * feat: add plugin-os --------- Co-authored-by: rain <15911122312@163.com> Co-authored-by: medcl <m@medcl.net>
133 lines
3.9 KiB
TypeScript
133 lines
3.9 KiB
TypeScript
import { useEffect, useState, useCallback, useRef } from "react";
|
|
import { isTauri } from "@tauri-apps/api/core";
|
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
|
import { LogicalSize } from "@tauri-apps/api/dpi";
|
|
|
|
import DropdownList from "./DropdownList";
|
|
import { Footer } from "./Footer";
|
|
import { SearchResults } from "../Search/SearchResults";
|
|
import { tauriFetch } from "../../api/tauriFetchClient";
|
|
import { useAppStore } from '@/stores/appStore';
|
|
interface SearchProps {
|
|
changeInput: (val: string) => void;
|
|
isTransitioned: boolean;
|
|
isChatMode: boolean;
|
|
input: string;
|
|
}
|
|
|
|
function Search({ isTransitioned, isChatMode, input }: SearchProps) {
|
|
const initializeListeners = useAppStore(state => state.initializeListeners);
|
|
useEffect(() => {
|
|
initializeListeners();
|
|
}, []);
|
|
|
|
const appStore = useAppStore();
|
|
|
|
const [suggests, setSuggests] = useState<any[]>([]);
|
|
const [isSearchComplete, setIsSearchComplete] = useState(false);
|
|
const [selectedItem, setSelectedItem] = useState<any>();
|
|
|
|
const mainWindowRef = useRef<HTMLDivElement>(null);
|
|
useEffect(() => {
|
|
if (!isTauri()) return;
|
|
const element = mainWindowRef.current;
|
|
if (!element) return;
|
|
|
|
const resizeObserver = new ResizeObserver(async (entries) => {
|
|
for (let entry of entries) {
|
|
let newHeight = entry.contentRect.height;
|
|
console.log("Height updated:", newHeight);
|
|
newHeight = newHeight + 90 + (newHeight === 0 ? 0 : 46);
|
|
await getCurrentWebviewWindow()?.setSize(
|
|
new LogicalSize(680, newHeight)
|
|
);
|
|
}
|
|
});
|
|
|
|
resizeObserver.observe(element);
|
|
|
|
return () => {
|
|
resizeObserver.disconnect();
|
|
};
|
|
}, [suggests]);
|
|
|
|
const getSuggest = async () => {
|
|
if (!input) return
|
|
try {
|
|
const response = await tauriFetch({
|
|
url: `/query/_search?query=${input}`,
|
|
method: "GET",
|
|
baseURL: appStore.endpoint_http,
|
|
});
|
|
console.log("_suggest", input, response);
|
|
const data = response.data?.hits?.hits || [];
|
|
setSuggests(data);
|
|
//
|
|
// const list = [];
|
|
// for (let i = 0; i < input.length; i++) {
|
|
// list.push({
|
|
// _source: { url: `https://www.google.com/search?q=${i}` },
|
|
// });
|
|
// }
|
|
// setSuggests(list);
|
|
//
|
|
setIsSearchComplete(true);
|
|
} catch (error) {
|
|
console.error("Failed to fetch user data:", error);
|
|
}
|
|
};
|
|
|
|
function debounce(fn: Function, delay: number) {
|
|
let timer: NodeJS.Timeout;
|
|
return (...args: any[]) => {
|
|
clearTimeout(timer);
|
|
timer = setTimeout(() => fn(...args), delay);
|
|
};
|
|
}
|
|
|
|
const debouncedSearch = useCallback(debounce(getSuggest, 300), [input]);
|
|
|
|
useEffect(() => {
|
|
!isChatMode && debouncedSearch();
|
|
if (!input) setSuggests([]);
|
|
}, [input]);
|
|
|
|
if (isChatMode || suggests.length === 0) return null;
|
|
|
|
return (
|
|
<div
|
|
className={`rounded-xl overflow-hidden bg-search_bg_light dark:bg-search_bg_dark bg-cover border border-[#E6E6E6] dark:border-[#272626] absolute w-full transition-opacity ${
|
|
isTransitioned ? "opacity-0 pointer-events-none" : "opacity-100"
|
|
} top-[96px]`}
|
|
style={{
|
|
backgroundPosition: "-1px 0",
|
|
backgroundSize: "101% 100%",
|
|
}}
|
|
>
|
|
{!isChatMode ? (
|
|
<div
|
|
ref={mainWindowRef}
|
|
className={`max-h-[498px] pb-10 w-full relative`}
|
|
>
|
|
{/* Search Results Panel */}
|
|
{suggests.length > 0 && !selectedItem ? (
|
|
<DropdownList
|
|
suggests={suggests}
|
|
isSearchComplete={isSearchComplete}
|
|
selected={(item) => setSelectedItem(item)}
|
|
/>
|
|
) : null}
|
|
|
|
{selectedItem ? <SearchResults input={input} isChatMode={isChatMode} /> : null}
|
|
|
|
{suggests.length > 0 || selectedItem ? (
|
|
<Footer isChat={false} name={selectedItem?.source} />
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default Search;
|