Skip to content

Commit

Permalink
fix: daily report animation
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Aug 7, 2024
1 parent 0ffe1e5 commit ff16272
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 41 deletions.
10 changes: 8 additions & 2 deletions src/renderer/src/components/ui/auto-resize-height.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ export const AutoResizeHeight: React.FC<AnimateChangeInHeightProps> = ({
if (!containerRef.current) return
const resizeObserver = new ResizeObserver((entries) => {
// We only have one entry, so we can use entries[0].
const target = entries[0].target as HTMLElement
const observedHeight = entries[0].contentRect.height
const style = getComputedStyle(target)

const marginHeight =
Number.parseFloat(style.marginTop) +
Number.parseFloat(style.marginBottom)
// add margin top
setHeight(observedHeight)
setHeight(observedHeight + marginHeight)
})

resizeObserver.observe(containerRef.current)
Expand All @@ -59,7 +65,7 @@ export const AutoResizeHeight: React.FC<AnimateChangeInHeightProps> = ({
}
}
>
<div ref={containerRef} className={innerClassName}>
<div className={cn("overflow-hidden", innerClassName)} ref={containerRef}>
{children}
</div>
</m.div>
Expand Down
21 changes: 15 additions & 6 deletions src/renderer/src/components/ui/collapse/Collapse.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import { cn } from "@renderer/lib/utils"
import { createContextState } from "foxact/context-state"
import type { Variants } from "framer-motion"
import { AnimatePresence, m } from "framer-motion"
import * as React from "react"

import {
CollapseGroupItemStateProvider,
CollapseStateProvider,
useCurrentCollapseId,
useSetCollapseGroupItemState,
useSetCurrentCollapseId,
} from "./hooks"

interface CollapseProps {
title: React.ReactNode
hideArrow?: boolean
}
const [CollapseStateProvider, useCurrentCollapseId, useSetCurrentCollapseId] =
createContextState<string | null>(null)

export const CollapseGroup: Component = ({ children }) => (
<CollapseStateProvider>{children}</CollapseStateProvider>
<CollapseStateProvider>
<CollapseGroupItemStateProvider>{children}</CollapseGroupItemStateProvider>
</CollapseStateProvider>
)
export const Collapse: Component<CollapseProps> = (props) => {
const [isOpened, setIsOpened] = React.useState(false)
const id = React.useId()
const setCurrentId = useSetCurrentCollapseId()
const currentId = useCurrentCollapseId()

const setItemStatus = useSetCollapseGroupItemState()
React.useEffect(() => {
if (isOpened) {
setCurrentId(id)
}
}, [id, isOpened, setCurrentId])
setItemStatus((prev) => ({ ...prev, [id]: isOpened }))
}, [id, isOpened, setCurrentId, setItemStatus])
React.useEffect(() => {
setIsOpened(currentId === id)
}, [currentId, id])
Expand Down
19 changes: 19 additions & 0 deletions src/renderer/src/components/ui/collapse/hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createContextState } from "foxact/context-state"
import { useContext } from "react"

export const [
CollapseStateProvider,
useCurrentCollapseId,
useSetCurrentCollapseId,
____CollapseStateContext,
] = createContextState<string | null>(null)

export const [
CollapseGroupItemStateProvider,
useCollapseGroupItemState,
useSetCollapseGroupItemState,
____CollapseGroupItemStateContext,
] = createContextState<Record<string, boolean>>({})
export const useCollapseId = () => useContext(____CollapseStateContext)
export const useCollapseGroupItem = () =>
useContext(____CollapseGroupItemStateContext)
8 changes: 2 additions & 6 deletions src/renderer/src/lib/parse-markdown.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
MarkdownLink,
} from "@renderer/components/ui/markdown/renderers"
import { MarkdownLink } from "@renderer/components/ui/markdown/renderers"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import { createElement } from "react"
import { Fragment, jsx, jsxs } from "react/jsx-runtime"
Expand All @@ -11,9 +9,7 @@ import remarkRehype from "remark-rehype"
import { unified } from "unified"
import { VFile } from "vfile"

export const parseMarkdown = async (
content: string,
) => {
export const parseMarkdown = (content: string) => {
const file = new VFile(content)

const pipeline = unified()
Expand Down
9 changes: 9 additions & 0 deletions src/renderer/src/modules/entry-content/atoms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ export const [
getEntryContentScrollToTop,
setEntryContentScrollToTop,
] = createAtomHooks(atom(false))

export const [
,
,
useEntryContentPlaceholderLogoShow,
,
getEntryContentPlaceholderLogoShow,
setEntryContentPlaceholderLogoShow,
] = createAtomHooks(atom(true))
50 changes: 32 additions & 18 deletions src/renderer/src/modules/entry-content/daily.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { AutoResizeHeight } from "@renderer/components/ui/auto-resize-height"
import { Card, CardHeader } from "@renderer/components/ui/card"
import { Collapse, CollapseGroup } from "@renderer/components/ui/collapse"
import {
useCollapseGroupItemState,
} from "@renderer/components/ui/collapse/hooks"
import { LoadingCircle } from "@renderer/components/ui/loading"
import { ScrollArea } from "@renderer/components/ui/scroll-area"
import {
Expand All @@ -15,7 +19,9 @@ import { parseMarkdown } from "@renderer/lib/parse-markdown"
import { cn } from "@renderer/lib/utils"
import { MarkAllButton } from "@renderer/modules/entry-column/mark-all-button"
import type { FC } from "react"
import { useEffect, useState } from "react"
import { useEffect, useMemo, useState } from "react"

import { setEntryContentPlaceholderLogoShow } from "./atoms"

type DailyView = Extract<
FeedViewType,
Expand Down Expand Up @@ -153,13 +159,10 @@ const DailyReportContent: FC<{
const [markAllButtonRef, setMarkAllButtonRef] =
useState<HTMLButtonElement | null>(null)

const [eleContent, setEleContent] = useState<JSX.Element>()
useEffect(() => {
if (content.data) {
parseMarkdown(content.data).then(({ content }) => {
setEleContent(content)
})
}
const eleContent = useMemo(() => {
if (!content.data) return null
const { content: _content } = parseMarkdown(content.data)
return _content
}, [content.data])

return (
Expand All @@ -170,15 +173,17 @@ const DailyReportContent: FC<{
flex
viewportClassName="max-h-[calc(100vh-500px)]"
>
{content.isLoading ? (
<LoadingCircle size="large" className="mt-8 text-center" />
) : (
eleContent && (
<div className="prose-sm mt-4 px-6 prose-p:my-1 prose-ul:my-1">
{eleContent}
</div>
)
)}
<AutoResizeHeight spring>
{content.isLoading ? (
<LoadingCircle size="large" className="mt-8 text-center" />
) : (
eleContent && (
<div className="prose-sm mt-4 px-6 prose-p:my-1 prose-ul:my-1 prose-ul:list-outside prose-ul:list-disc prose-li:marker:text-theme-accent">
{eleContent}
</div>
)
)}
</AutoResizeHeight>
</ScrollArea.ScrollArea>
{eleContent && (
<button
Expand Down Expand Up @@ -213,9 +218,18 @@ export const Daily = ({
}) => (
<div className={cn(className, "mx-auto flex w-[75ch] flex-col gap-6")}>
<CollapseGroup>
<CtxConsumer />
<DailyItem day={DayOf.Today} view={view} />

<DailyItem day={DayOf.Yesterday} view={view} />
</CollapseGroup>
</div>
)

const CtxConsumer = () => {
const status = useCollapseGroupItemState()
const isAllCollapsed = Object.values(status).every((v) => !v)
useEffect(() => {
setEntryContentPlaceholderLogoShow(isAllCollapsed)
}, [isAllCollapsed])
return null
}
25 changes: 25 additions & 0 deletions src/renderer/src/modules/entry-content/entry-placeholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Logo } from "@renderer/components/icons/logo"
import { stopPropagation } from "@renderer/lib/dom"
import { cn } from "@renderer/lib/utils"
import { useFeedHeaderTitle } from "@renderer/store/feed"

import { useEntryContentPlaceholderLogoShow } from "./atoms"

export const EntryPlaceholderLogo = () => {
const title = useFeedHeaderTitle()

const logoShow = useEntryContentPlaceholderLogoShow()

return (
<div
onContextMenu={stopPropagation}
className={cn(
"flex w-full min-w-0 flex-col items-center justify-center gap-1 text-balance px-12 pb-6 text-center text-lg font-medium text-zinc-400 duration-500",
!logoShow && "translate-y-[-50px] opacity-0",
)}
>
<Logo className="size-16 opacity-40 grayscale" />
<span className="max-w-[60ch]">{title}</span>
</div>
)
}
15 changes: 6 additions & 9 deletions src/renderer/src/modules/entry-content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
import { useUISettingKey } from "@renderer/atoms/settings/ui"
import { useWhoami } 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 { StyledButton } from "@renderer/components/ui/button"
import { ScrollArea } from "@renderer/components/ui/scroll-area"
import { ROUTE_FEED_PENDING } from "@renderer/constants"
import { useEntryReadabilityToggle } from "@renderer/hooks/biz/useEntryActions"
import { useRouteParamsSelector, useRouteParms } from "@renderer/hooks/biz/useRouteParams"
import {
useRouteParamsSelector,
useRouteParms,
} from "@renderer/hooks/biz/useRouteParams"
import { useAuthQuery, useTitle } from "@renderer/hooks/common"
import { stopPropagation } from "@renderer/lib/dom"
import { FeedViewType } from "@renderer/lib/enum"
Expand All @@ -33,6 +35,7 @@ import { LoadingCircle } from "../../components/ui/loading"
import { EntryTranslation } from "../entry-column/translation"
import { setEntryContentScrollToTop, setEntryTitleMeta } from "./atoms"
import { Daily } from "./daily"
import { EntryPlaceholderLogo } from "./entry-placeholder"
import { EntryHeader } from "./header"
import { EntryContentProvider } from "./provider"

Expand All @@ -48,13 +51,7 @@ export const EntryContent = ({ entryId }: { entryId: ActiveEntryId }) => {
initial={{ opacity: 0.01, y: 300 }}
animate={{ opacity: 1, y: 0 }}
>
<div
onContextMenu={stopPropagation}
className="flex w-full min-w-0 flex-col items-center justify-center gap-1 text-balance px-12 pb-6 text-center text-lg font-medium text-zinc-400"
>
<Logo className="size-16 opacity-40 grayscale" />
<span className="max-w-[60ch]">{title}</span>
</div>
<EntryPlaceholderLogo />
{feedId === ROUTE_FEED_PENDING && view === FeedViewType.Articles && (
<Daily view={view} />
)}
Expand Down

0 comments on commit ff16272

Please sign in to comment.