parent
07c4565a17
commit
355a36c152
|
|
@ -27,8 +27,9 @@ const Thumb = React.forwardRef<
|
|||
ref={forwardedRef}
|
||||
className={cn(
|
||||
"relative w-full flex-1 rounded-xl transition-colors duration-150",
|
||||
"bg-gray-300 hover:bg-neutral-400/80 dark:bg-neutral-500",
|
||||
"active:bg-neutral-400/50",
|
||||
"bg-gray-300 hover:bg-neutral-400/80",
|
||||
"active:bg-neutral-400",
|
||||
"dark:bg-neutral-500 hover:dark:bg-neutral-400/80 active:dark:bg-neutral-400",
|
||||
"before:absolute before:-left-1/2 before:-top-1/2 before:h-full before:min-h-[44]",
|
||||
"before:w-full before:min-w-[44] before:-translate-x-full before:-translate-y-full before:content-[\"\"]",
|
||||
className,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@renderer/components/ui/popover"
|
||||
import { ScrollArea } from "@renderer/components/ui/scroll-area"
|
||||
import { EllipsisHorizontalTextWithTooltip } from "@renderer/components/ui/typography"
|
||||
import {
|
||||
FEED_COLLECTION_LIST,
|
||||
|
|
@ -60,7 +61,11 @@ import type {
|
|||
import { Virtuoso, VirtuosoGrid } from "react-virtuoso"
|
||||
|
||||
import { useEntriesByView, useEntryMarkReadHandler } from "./hooks"
|
||||
import { EntryItem, EntryItemSkeleton } from "./item"
|
||||
import {
|
||||
EntryItem,
|
||||
EntryItemSkeleton,
|
||||
EntryItemSkeletonWithDelayShow,
|
||||
} from "./item"
|
||||
|
||||
const scrollSeekConfiguration: ScrollSeekConfiguration = {
|
||||
enter: (velocity) => Math.abs(velocity) > 500,
|
||||
|
|
@ -101,12 +106,13 @@ export function EntryColumn() {
|
|||
|
||||
const handleMarkReadInRange = useEntryMarkReadHandler(entriesIds)
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const virtuosoOptions = {
|
||||
components: {
|
||||
List: ListContent,
|
||||
Footer: useCallback(() => {
|
||||
if (!isFetchingNextPage) return null
|
||||
return <EntryItemSkeleton view={view} />
|
||||
return <EntryItemSkeletonWithDelayShow delay={500} view={view} />
|
||||
}, [isFetchingNextPage, view]),
|
||||
ScrollSeekPlaceholder: useCallback(
|
||||
() => <EntryItemSkeleton view={view} single />,
|
||||
|
|
@ -119,6 +125,8 @@ export function EntryColumn() {
|
|||
// @ts-expect-error
|
||||
handleMarkReadInRange(...args, isInteracted.current)
|
||||
},
|
||||
customScrollParent: scrollRef.current!,
|
||||
|
||||
totalCount: entries.totalCount,
|
||||
endReached: () => entries.hasNextPage && entries.fetchNextPage(),
|
||||
data: entriesIds,
|
||||
|
|
@ -164,33 +172,35 @@ export function EntryColumn() {
|
|||
</AutoResizeHeight>
|
||||
<m.div
|
||||
key={`${routeFeedId}-${view}`}
|
||||
className="h-full"
|
||||
className="relative h-0 grow"
|
||||
initial={{ opacity: 0.01, y: 100 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0.01, y: -100 }}
|
||||
>
|
||||
{virtuosoOptions.totalCount === 0 ? (
|
||||
entries.isLoading ?
|
||||
<ScrollArea.ScrollArea mask={false} ref={scrollRef} flex rootClassName="h-full">
|
||||
{virtuosoOptions.totalCount === 0 ? (
|
||||
entries.isLoading ?
|
||||
(
|
||||
<LoadingCircle
|
||||
className="center h-full -translate-y-12"
|
||||
size="large"
|
||||
/>
|
||||
) :
|
||||
(
|
||||
<EmptyList />
|
||||
)
|
||||
) : view && views[view].gridMode ?
|
||||
(
|
||||
<LoadingCircle
|
||||
className="center h-full -translate-y-12"
|
||||
size="large"
|
||||
<VirtuosoGrid
|
||||
listClassName="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 px-4"
|
||||
{...virtuosoOptions}
|
||||
ref={virtuosoRef}
|
||||
/>
|
||||
) :
|
||||
(
|
||||
<EmptyList />
|
||||
)
|
||||
) : view && views[view].gridMode ?
|
||||
(
|
||||
<VirtuosoGrid
|
||||
listClassName="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 px-4"
|
||||
{...virtuosoOptions}
|
||||
ref={virtuosoRef}
|
||||
/>
|
||||
) :
|
||||
(
|
||||
<EntryList {...virtuosoOptions} virtuosoRef={virtuosoRef} />
|
||||
)}
|
||||
<EntryList {...virtuosoOptions} virtuosoRef={virtuosoRef} />
|
||||
)}
|
||||
</ScrollArea.ScrollArea>
|
||||
</m.div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import type { FlatEntryModel } from "@renderer/store/entry"
|
|||
import { entryActions } from "@renderer/store/entry"
|
||||
import { useEntry } from "@renderer/store/entry/hooks"
|
||||
import type { FC, ReactNode } from "react"
|
||||
import { memo, useCallback } from "react"
|
||||
import { memo, useCallback, useEffect, useState } from "react"
|
||||
import { useDebounceCallback } from "usehooks-ts"
|
||||
|
||||
import { ReactVirtuosoItemPlaceholder } from "../../components/ui/placeholder"
|
||||
|
|
@ -243,3 +243,31 @@ const createSkeletonItems = (element: ReactNode) => {
|
|||
}
|
||||
return children
|
||||
}
|
||||
|
||||
export const EntryItemSkeletonWithDelayShow: FC<{
|
||||
view: FeedViewType
|
||||
delay: number
|
||||
}> = memo(({ view, delay }) => {
|
||||
const SkeletonItem = SkeletonItemMap[view]
|
||||
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setShow(true)
|
||||
}, delay)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [delay])
|
||||
if (!show) return LoadingCircleFallback
|
||||
|
||||
return SkeletonItem ?
|
||||
(
|
||||
<div className="flex flex-col">{createSkeletonItems(SkeletonItem)}</div>
|
||||
) :
|
||||
(
|
||||
LoadingCircleFallback
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useMe } from "@renderer/atoms/user"
|
|||
import { m } from "@renderer/components/common/Motion"
|
||||
import { Logo } from "@renderer/components/icons/logo"
|
||||
import { AutoResizeHeight } from "@renderer/components/ui/auto-resize-height"
|
||||
import { ScrollArea } from "@renderer/components/ui/scroll-area"
|
||||
import { getRouteParams } from "@renderer/hooks/biz/useRouteParams"
|
||||
import { useAuthQuery, useTitle } from "@renderer/hooks/common"
|
||||
import { stopPropagation } from "@renderer/lib/dom"
|
||||
|
|
@ -81,6 +82,7 @@ function EntryContentRender({ entryId }: { entryId: string }) {
|
|||
data?.entries.content,
|
||||
entry?.entries.content,
|
||||
readerRenderInlineStyle,
|
||||
// Only for dx, hmr
|
||||
parseHtml,
|
||||
])
|
||||
|
||||
|
|
@ -128,10 +130,10 @@ function EntryContentRender({ entryId }: { entryId: string }) {
|
|||
<EntryHeader
|
||||
entryId={entry.entries.id}
|
||||
view={0}
|
||||
className="h-[55px] px-5"
|
||||
className="h-[55px] shrink-0 px-5"
|
||||
/>
|
||||
|
||||
<div className="h-[calc(100%-3.5rem)] min-w-0 overflow-y-auto @container">
|
||||
<ScrollArea.ScrollArea mask={false} rootClassName="h-0 grow min-w-0 overflow-y-auto @container">
|
||||
<m.div
|
||||
style={
|
||||
readerFontFamily ?
|
||||
|
|
@ -225,7 +227,7 @@ function EntryContentRender({ entryId }: { entryId: string }) {
|
|||
)}
|
||||
</article>
|
||||
</m.div>
|
||||
</div>
|
||||
</ScrollArea.ScrollArea>
|
||||
</EntryContentProvider>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const Component = () => {
|
|||
return (
|
||||
<AnimatePresence>
|
||||
{!inWideMode && (
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<EntryContent entryId={entryId === ROUTE_ENTRY_PENDING ? "" : entryId} />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue