diff --git a/src/renderer/src/atoms/app.ts b/src/renderer/src/atoms/app.ts index 6a5807919..175847da2 100644 --- a/src/renderer/src/atoms/app.ts +++ b/src/renderer/src/atoms/app.ts @@ -1,7 +1,5 @@ import { createAtomHooks } from "@renderer/lib/jotai" -import { getStorageNS } from "@renderer/lib/ns" import { atom } from "jotai" -import { atomWithStorage } from "jotai/utils" export const [, , useAppIsReady, , appIsReady, setAppIsReady] = createAtomHooks( atom(false), @@ -10,10 +8,3 @@ export const [, , useAppIsReady, , appIsReady, setAppIsReady] = createAtomHooks( export const [, , useAppSearchOpen, , , setAppSearchOpen] = createAtomHooks( atom(false), ) - -export const [, , useFeedColumnShow, , , setFeedColumnShow] = createAtomHooks( - atomWithStorage( - getStorageNS("feed-column-show"), - true, - ), -) diff --git a/src/renderer/src/atoms/sidebar.ts b/src/renderer/src/atoms/sidebar.ts index d65421b36..dad16327c 100644 --- a/src/renderer/src/atoms/sidebar.ts +++ b/src/renderer/src/atoms/sidebar.ts @@ -47,3 +47,6 @@ viewAtom.onMount = () => { setSidebarActiveView(view) } } +export const [, , useFeedColumnShow, , , setFeedColumnShow] = createAtomHooks( + atom(true), +) diff --git a/src/renderer/src/lib/parse-html.ts b/src/renderer/src/lib/parse-html.ts index ed6cd07f7..a250d93b2 100644 --- a/src/renderer/src/lib/parse-html.ts +++ b/src/renderer/src/lib/parse-html.ts @@ -56,16 +56,17 @@ export const parseHtml = ( const hastTree = pipeline.runSync(tree, file) - let imgCount = 0 + const images = [] as string[] visit(tree, "element", (node) => { - if (node.tagName === "img") { - imgCount++ + if (node.tagName === "img" && node.properties.src) { + images.push(node.properties.src as string) } }) return { - imgCount, + hastTree, + images, toContent: () => toJsxRuntime(hastTree, { Fragment, diff --git a/src/renderer/src/modules/feed-column/header.tsx b/src/renderer/src/modules/feed-column/header.tsx new file mode 100644 index 000000000..a9627801e --- /dev/null +++ b/src/renderer/src/modules/feed-column/header.tsx @@ -0,0 +1,154 @@ +import { setAppSearchOpen } from "@renderer/atoms/app" +import { useGeneralSettingKey } from "@renderer/atoms/settings/general" +import { + setFeedColumnShow, + useFeedColumnShow, + useSidebarActiveView, +} from "@renderer/atoms/sidebar" +import { Logo } from "@renderer/components/icons/logo" +import { ActionButton } from "@renderer/components/ui/button" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@renderer/components/ui/popover" +import { ProfileButton } from "@renderer/components/user-button" +import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry" +import { stopPropagation } from "@renderer/lib/dom" +import { cn } from "@renderer/lib/utils" +import { m } from "framer-motion" +import type { FC, PropsWithChildren } from "react" +import { useCallback, useRef, useState } from "react" +import { Link } from "react-router-dom" +import { toast } from "sonner" + +const useBackHome = (active: number) => { + const navigate = useNavigateEntry() + + return useCallback( + (overvideActive?: number) => { + navigate({ + feedId: null, + entryId: null, + view: overvideActive ?? active, + }) + }, + [active, navigate], + ) +} + +export const FeedColumnHeader = () => { + const [active] = useSidebarActiveView() + + const navigateBackHome = useBackHome(active) + const normalStyle = + !window.electron || window.electron.process.platform !== "darwin" + return ( +
+ {normalStyle && ( + +
{ + e.stopPropagation() + navigateBackHome() + }} + > + + + {APP_NAME} +
+
+ )} +
+ + + + + + + + + +
+
+ ) +} + +const LayoutActionButton = () => { + const feedColumnShow = useFeedColumnShow() + + return ( + + + } + onClick={() => setFeedColumnShow(!feedColumnShow)} + /> + + ) +} + +const LogoContextMenu: FC = ({ children }) => { + const [open, setOpen] = useState(false) + const logoRef = useRef(null) + + return ( + + { + setOpen(true) + }} + > + {children} + + + + + + ) +} + +const SearchActionButton = () => { + const canSearch = useGeneralSettingKey("dataPersist") + if (!canSearch) return null + return ( + setAppSearchOpen(true)} + > + + + ) +} diff --git a/src/renderer/src/modules/feed-column/index.tsx b/src/renderer/src/modules/feed-column/index.tsx index 32601e2b5..220fa588c 100644 --- a/src/renderer/src/modules/feed-column/index.tsx +++ b/src/renderer/src/modules/feed-column/index.tsx @@ -1,16 +1,7 @@ -import { setAppSearchOpen } from "@renderer/atoms/app" import { getReadonlyRoute } from "@renderer/atoms/route" -import { useGeneralSettingKey } from "@renderer/atoms/settings/general" import { useUISettingKey } from "@renderer/atoms/settings/ui" import { useSidebarActiveView } from "@renderer/atoms/sidebar" -import { Logo } from "@renderer/components/icons/logo" import { ActionButton } from "@renderer/components/ui/button" -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@renderer/components/ui/popover" -import { ProfileButton } from "@renderer/components/user-button" import { HotKeyScopeMap, views } from "@renderer/constants" import { shortcuts } from "@renderer/constants/shortcuts" import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry" @@ -30,18 +21,12 @@ import type { MotionValue } from "framer-motion" import { m, useSpring } from "framer-motion" import { atom, useAtomValue } from "jotai" import { Lethargy } from "lethargy" -import type { FC, PropsWithChildren } from "react" -import { - useCallback, - useLayoutEffect, - useRef, - useState, -} from "react" +import type { PropsWithChildren } from "react" +import { useCallback, useLayoutEffect, useRef } from "react" import { isHotkeyPressed, useHotkeys } from "react-hotkeys-hook" -import { Link } from "react-router-dom" -import { toast } from "sonner" import { WindowUnderBlur } from "../../components/ui/background" +import { FeedColumnHeader } from "./header" import { FeedList } from "./list" const lethargy = new Lethargy() @@ -168,6 +153,7 @@ export function FeedColumn({ children }: PropsWithChildren) { const handler = () => { const width = $carousel.clientWidth + jotaiStore.set(carouselWidthAtom, width) } handler() @@ -177,9 +163,6 @@ export function FeedColumn({ children }: PropsWithChildren) { } }, []) - const normalStyle = - !window.electron || window.electron.process.platform !== "darwin" - const unreadByView = useUnreadByView() const showSidebarUnreadCount = useUISettingKey("sidebarShowUnreadCount") @@ -193,42 +176,7 @@ export function FeedColumn({ children }: PropsWithChildren) { className="relative flex h-full flex-col space-y-3 rounded-l-[12px] pt-2.5" onClick={useCallback(() => navigateBackHome(), [navigateBackHome])} > -
- {normalStyle && ( - -
{ - e.stopPropagation() - navigateBackHome() - }} - > - - - {APP_NAME} -
-
- )} -
- - - - - - - - -
-
+
) } - -const SearchActionButton = () => { - const canSearch = useGeneralSettingKey("dataPersist") - if (!canSearch) return null - return ( - setAppSearchOpen(true)} - > - - - ) -} - -const LogoContextMenu: FC = ({ children }) => { - const [open, setOpen] = useState(false) - const logoRef = useRef(null) - - return ( - - { - setOpen(true) - }} - > - {children} - - - - - - ) -} diff --git a/src/renderer/src/pages/(main)/layout.tsx b/src/renderer/src/pages/(main)/layout.tsx index fc75da58c..709d8669c 100644 --- a/src/renderer/src/pages/(main)/layout.tsx +++ b/src/renderer/src/pages/(main)/layout.tsx @@ -1,7 +1,7 @@ import { repository } from "@pkg" -import { useFeedColumnShow } from "@renderer/atoms/app" import { setMainContainerElement } from "@renderer/atoms/dom" import { getUISettings, setUISetting } from "@renderer/atoms/settings/ui" +import { setFeedColumnShow, useFeedColumnShow } from "@renderer/atoms/sidebar" import { useLoginModalShow, useWhoami } from "@renderer/atoms/user" import { AppErrorBoundary } from "@renderer/components/common/AppErrorBoundary" import { ErrorComponentType } from "@renderer/components/errors" @@ -9,6 +9,7 @@ import { PanelSplitter } from "@renderer/components/ui/divider" import { DeclarativeModal } from "@renderer/components/ui/modal/stacked/declarative-modal" import { NoopChildren } from "@renderer/components/ui/modal/stacked/utils" import { RootPortal } from "@renderer/components/ui/portal" +import { shortcuts } from "@renderer/constants/shortcuts" import { preventDefault } from "@renderer/lib/dom" import { cn } from "@renderer/lib/utils" import { EnvironmentIndicator } from "@renderer/modules/app/EnvironmentIndicator" @@ -20,8 +21,10 @@ import { CornerPlayer } from "@renderer/modules/feed-column/corner-player" import { CmdF } from "@renderer/modules/panel/cmdf" import { SearchCmdK } from "@renderer/modules/panel/cmdk" import { CmdNTrigger } from "@renderer/modules/panel/cmdn" +import { throttle } from "lodash-es" import type { PropsWithChildren } from "react" -import { useRef } from "react" +import { useEffect, useRef, useState } from "react" +import { useHotkeys } from "react-hotkeys-hook" import { useResizable } from "react-resizable-layout" import { Outlet } from "react-router-dom" @@ -87,9 +90,7 @@ export function Component() { // NOTE: tabIndex for main element can get by `document.activeElement` tabIndex={-1} > - + @@ -138,14 +139,61 @@ const FeedResponsiveResizerContainer = ({ }) const feedColumnShow = useFeedColumnShow() + const [feedColumnTempShow, setFeedColumnTempShow] = useState(false) + + useEffect(() => { + const handler = throttle((e: MouseEvent) => { + const mouseX = e.clientX + const { feedColWidth } = getUISettings() + + if (mouseX < feedColWidth) { + setFeedColumnTempShow(true) + } else { + setFeedColumnTempShow(false) + } + }, 300) + + document.addEventListener("mousemove", handler) + return () => { + document.removeEventListener("mousemove", handler) + } + }, []) + + useHotkeys(shortcuts.layout.toggleSidebar.key, () => { + setFeedColumnShow(!feedColumnShow) + }) + + const [delayShowSplitter, setDelayShowSplitter] = useState(feedColumnShow) + + useEffect(() => { + let timer: any + if (feedColumnShow) { + // eslint-disable-next-line @eslint-react/web-api/no-leaked-timeout + timer = setTimeout(() => { + setDelayShowSplitter(true) + }, 200) + } else { + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect + setDelayShowSplitter(false) + } + + return () => { + timer = clearTimeout(timer) + } + }, [feedColumnShow]) return ( <>
- {feedColumnShow && } + {delayShowSplitter && } ) } diff --git a/src/renderer/src/styles/tailwind-extend.css b/src/renderer/src/styles/tailwind-extend.css index 960953646..47ef7924d 100644 --- a/src/renderer/src/styles/tailwind-extend.css +++ b/src/renderer/src/styles/tailwind-extend.css @@ -279,6 +279,13 @@ [data-theme="dark"] .shadow-drawer-left { box-shadow: -12px 0px 17px 2px rgba(0, 0, 0, 0.653); } + + .shadow-drawer-right { + box-shadow: 12px 0px 17px 2px rgba(41, 41, 41, 0.1); + } + [data-theme="dark"] .shadow-drawer-right { + box-shadow: 12px 0px 17px 2px rgba(0, 0, 0, 0.653); + } } @layer components {