diff --git a/icons/mgc/search_2_cute_re.svg b/icons/mgc/search_2_cute_re.svg new file mode 100644 index 000000000..7c5e90731 --- /dev/null +++ b/icons/mgc/search_2_cute_re.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/atoms/app.ts b/src/renderer/src/atoms/app.ts index 57bab10d0..97894dca9 100644 --- a/src/renderer/src/atoms/app.ts +++ b/src/renderer/src/atoms/app.ts @@ -1,6 +1,10 @@ import { createAtomHooks } from "@renderer/lib/jotai" import { atom } from "jotai" -export const [, ,useAppIsReady, , , setAppIsReady] = createAtomHooks( +export const [, , useAppIsReady, , , setAppIsReady] = createAtomHooks( + atom(false), +) + +export const [, , useAppSearchOpen, , , setAppSearchOpen] = createAtomHooks( atom(false), ) diff --git a/src/renderer/src/modules/feed-column/index.tsx b/src/renderer/src/modules/feed-column/index.tsx index e0c3db9bb..48c29f66a 100644 --- a/src/renderer/src/modules/feed-column/index.tsx +++ b/src/renderer/src/modules/feed-column/index.tsx @@ -1,4 +1,6 @@ +import { setAppSearchOpen } from "@renderer/atoms/app" import { getReadonlyRoute } from "@renderer/atoms/route" +import { useGeneralSettingKey } from "@renderer/atoms/settings/general" import { useSidebarActiveView } from "@renderer/atoms/sidebar" import { Logo } from "@renderer/components/icons/logo" import { ActionButton } from "@renderer/components/ui/button" @@ -141,9 +143,11 @@ export function FeedColumn({ children }: PropsWithChildren) { )}
+ + @@ -196,7 +200,6 @@ export function FeedColumn({ children }: PropsWithChildren) {
{children} - ) } @@ -230,3 +233,17 @@ const SwipeWrapper: Component<{ ) } + +const SearchActionButton = () => { + const canSearch = useGeneralSettingKey("dataPersist") + if (!canSearch) return null + return ( + setAppSearchOpen(true)} + > + + + ) +} diff --git a/src/renderer/src/modules/search/cmdk.module.css b/src/renderer/src/modules/search/cmdk.module.css new file mode 100644 index 000000000..517d1e935 --- /dev/null +++ b/src/renderer/src/modules/search/cmdk.module.css @@ -0,0 +1,12 @@ +.status-bar { + @apply scale-y-75 z-10 relative h-px w-full shrink-0 transform; + + &.loading::before { + @apply scale-y-75 z-10 h-px absolute bottom-0 w-full left-0 top-0 transform; + @apply bg-repeat; + + content: ""; + background: linear-gradient(90deg, transparent, #bbb, transparent); + animation: move 2s steps(60) infinite; + } +} diff --git a/src/renderer/src/modules/search/cmdk.tsx b/src/renderer/src/modules/search/cmdk.tsx index 199d6b187..98e6130f4 100644 --- a/src/renderer/src/modules/search/cmdk.tsx +++ b/src/renderer/src/modules/search/cmdk.tsx @@ -1,3 +1,4 @@ +import { setAppSearchOpen, useAppSearchOpen } from "@renderer/atoms/app" import { EmptyIcon } from "@renderer/components/icons/empty" import { Logo } from "@renderer/components/icons/logo" import { SiteIcon } from "@renderer/components/site-icon" @@ -22,16 +23,15 @@ import { Command } from "cmdk" import type { FC } from "react" import * as React from "react" import { memo, useMemo } from "react" -import { useHotkeys } from "react-hotkeys-hook" + +import styles from "./cmdk.module.css" const SearchCmdKContext = React.createContext | null>( null, ) export const SearchCmdK: React.FC = () => { - const [open, setOpen] = React.useState(false) - useHotkeys("meta+k,ctrl+k", () => { - setOpen((o) => !o) - }) + const open = useAppSearchOpen() + const searchInstance = useMemo(() => searchActions.createLocalDbSearch(), []) const entries = useSearchStore((s) => s.entries) @@ -51,7 +51,7 @@ export const SearchCmdK: React.FC = () => { React.useCallback((e) => { const $input = inputRef.current if (e.key === "Escape") { - setOpen(false) + setAppSearchOpen(false) return } @@ -61,6 +61,20 @@ export const SearchCmdK: React.FC = () => { $input?.focus() } }, []) + const [isPending, startTransition] = React.useTransition() + const handleSearch = React.useCallback( + async (value: string) => { + const { search } = await searchInstance + startTransition(() => { + search(value) + const $scrollView = scrollViewRef.current + if ($scrollView) { + $scrollView.scrollTop = 0 + } + }) + }, + [searchInstance], + ) return ( { shouldFilter={false} open={open} onKeyDown={handleKeyDownToFocusInput} - onOpenChange={setOpen} + onOpenChange={setAppSearchOpen} className={cn( "h-[600px] max-h-[80vh] w-[800px] max-w-[100vw] rounded-none md:h-screen md:max-h-[60vh] md:max-w-[80vw]", "flex min-h-[50vh] flex-col bg-zinc-50/85 shadow-2xl backdrop-blur-md dark:bg-neutral-900/80 md:rounded-xl", @@ -79,15 +93,11 @@ export const SearchCmdK: React.FC = () => { { - const { search } = await searchInstance - search(value) - const $scrollView = scrollViewRef.current - if ($scrollView) { - $scrollView.scrollTop = 0 - } - }} + placeholder={searchActions.getCurrentKeyword() || "Search..."} + onValueChange={handleSearch} + /> +
{ id={feed.item.id!} index={entries.length + index} icon={feed.item.siteUrl} - subtitle={useFeedUnreadStore.getState().data[feed.item.id!]?.toString()} + subtitle={useFeedUnreadStore + .getState() + .data[feed.item.id!]?.toString()} /> ))} @@ -221,7 +233,7 @@ const SearchOptions = () => { const searchInstance = React.useContext(SearchCmdKContext) const hasKeyword = useSearchStore((s) => !!s.keyword) return ( -
+
Search Type