Skip to content

Commit

Permalink
feat: local search action
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Jul 15, 2024
1 parent e9539cf commit 8f32ba1
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 21 deletions.
1 change: 1 addition & 0 deletions icons/mgc/search_2_cute_re.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion src/renderer/src/atoms/app.ts
Original file line number Diff line number Diff line change
@@ -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),
)
21 changes: 19 additions & 2 deletions src/renderer/src/modules/feed-column/index.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -141,9 +143,11 @@ export function FeedColumn({ children }: PropsWithChildren) {
</div>
)}
<div
className="relative flex items-center gap-2"
className="relative flex items-center gap-1"
onClick={stopPropagation}
>
<SearchActionButton />

<Link to="/discover" tabIndex={-1}>
<ActionButton shortcut="Meta+T" tooltip="Add">
<i className="i-mgc-add-cute-re size-5 text-theme-vibrancyFg" />
Expand Down Expand Up @@ -196,7 +200,6 @@ export function FeedColumn({ children }: PropsWithChildren) {
</div>

{children}

</Vibrancy>
)
}
Expand Down Expand Up @@ -230,3 +233,17 @@ const SwipeWrapper: Component<{
</m.div>
)
}

const SearchActionButton = () => {
const canSearch = useGeneralSettingKey("dataPersist")
if (!canSearch) return null
return (
<ActionButton
shortcut="Meta+K"
tooltip="Search"
onClick={() => setAppSearchOpen(true)}
>
<i className="i-mgc-search-2-cute-re size-5 text-theme-vibrancyFg" />
</ActionButton>
)
}
12 changes: 12 additions & 0 deletions src/renderer/src/modules/search/cmdk.module.css
Original file line number Diff line number Diff line change
@@ -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;
}
}
48 changes: 30 additions & 18 deletions src/renderer/src/modules/search/cmdk.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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<Promise<SearchInstance> | 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)
Expand All @@ -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
}

Expand All @@ -61,14 +61,28 @@ 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 (
<SearchCmdKContext.Provider value={searchInstance}>
<Command.Dialog
ref={dialogRef}
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",
Expand All @@ -79,15 +93,11 @@ export const SearchCmdK: React.FC = () => {
<Command.Input
className="w-full shrink-0 border-b border-zinc-200 bg-transparent p-4 px-5 text-lg leading-4 dark:border-neutral-700"
ref={inputRef}
placeholder={searchActions.getCurrentKeyword()}
onValueChange={async (value) => {
const { search } = await searchInstance
search(value)
const $scrollView = scrollViewRef.current
if ($scrollView) {
$scrollView.scrollTop = 0
}
}}
placeholder={searchActions.getCurrentKeyword() || "Search..."}
onValueChange={handleSearch}
/>
<div
className={cn(styles["status-bar"], isPending && styles["loading"])}
/>

<ScrollArea.ScrollArea
Expand Down Expand Up @@ -144,7 +154,9 @@ export const SearchCmdK: React.FC = () => {
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()}
/>
))}
</Command.Group>
Expand Down Expand Up @@ -221,7 +233,7 @@ const SearchOptions = () => {
const searchInstance = React.useContext(SearchCmdKContext)
const hasKeyword = useSearchStore((s) => !!s.keyword)
return (
<div className="absolute bottom-2 left-4 flex items-center gap-2 text-sm">
<div className="absolute bottom-2 left-4 flex items-center gap-2 text-sm text-theme-foreground/80">
<span className="shrink-0">Search Type</span>

<Select
Expand Down

0 comments on commit 8f32ba1

Please sign in to comment.