feat: remove ai daily report
This commit is contained in:
parent
d8bd4f6730
commit
651bc4d099
|
|
@ -1,42 +0,0 @@
|
|||
import { cn } from "@follow/utils/utils"
|
||||
import { useSetAtom } from "jotai"
|
||||
import type { FC } from "react"
|
||||
import { use } from "react"
|
||||
|
||||
import { EntryContentPlaceholderContext } from "~/modules/app-layout/entry-content/EntryContentPlaceholderContext"
|
||||
|
||||
import { DayOf } from "./constants"
|
||||
import { DailyReportTitle } from "./daily"
|
||||
import type { DailyItemProps, DailyView } from "./types"
|
||||
import { useParseDailyDate } from "./useParseDailyDate"
|
||||
|
||||
export const EntryPlaceholderDaily = ({
|
||||
view,
|
||||
className,
|
||||
}: {
|
||||
view: DailyView
|
||||
className?: string
|
||||
}) => (
|
||||
<div className={cn(className, "mx-auto flex w-full max-w-[75ch] flex-col gap-6")}>
|
||||
<NormalDailyReportTitle day={DayOf.Today} view={view} />
|
||||
<NormalDailyReportTitle day={DayOf.Yesterday} view={view} />
|
||||
</div>
|
||||
)
|
||||
|
||||
const NormalDailyReportTitle: FC<DailyItemProps> = ({ day }) => {
|
||||
const { title, startDate, endDate } = useParseDailyDate(day)
|
||||
|
||||
const setOpenedSummary = useSetAtom(use(EntryContentPlaceholderContext).openedSummary)
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setOpenedSummary(day)
|
||||
}}
|
||||
className="border-b last:border-b-0"
|
||||
>
|
||||
<DailyReportTitle title={title} startDate={startDate} endDate={endDate} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@follow/components/ui/tabs/index.jsx"
|
||||
import { FeedViewType } from "@follow/constants"
|
||||
|
||||
import { DayOf } from "./constants"
|
||||
import { DailyReportModalContent, DailyReportTitle } from "./daily"
|
||||
import { useParseDailyDate } from "./useParseDailyDate"
|
||||
|
||||
const tabs = [DayOf.Today, DayOf.Yesterday]
|
||||
|
||||
export const FeedDailyModalContent = () => {
|
||||
const today = useParseDailyDate(DayOf.Today)
|
||||
const yesterday = useParseDailyDate(DayOf.Yesterday)
|
||||
|
||||
return (
|
||||
<Tabs defaultValue={DayOf.Today as any} className="flex h-full flex-col">
|
||||
<TabsList className="w-full">
|
||||
{tabs.map((tab: any) => (
|
||||
<TabsTrigger key={tab} value={tab}>
|
||||
<DailyReportTitle
|
||||
containerClassName="pb-0"
|
||||
{...(tab === DayOf.Today ? today : yesterday)}
|
||||
/>
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
|
||||
<div className="-mx-2 flex grow flex-col items-center overflow-auto lg:mx-0">
|
||||
{tabs.map((tab: any) => (
|
||||
<TabsContent
|
||||
key={tab}
|
||||
value={tab}
|
||||
className="flex grow flex-col data-[state=inactive]:hidden"
|
||||
>
|
||||
<DailyReportModalContent
|
||||
view={FeedViewType.SocialMedia}
|
||||
{...(tab === DayOf.Today ? today : yesterday)}
|
||||
/>
|
||||
</TabsContent>
|
||||
))}
|
||||
</div>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
export enum DayOf {
|
||||
Today = 1,
|
||||
Yesterday,
|
||||
}
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
import { Spring } from "@follow/components/constants/spring.js"
|
||||
import { EmptyIcon } from "@follow/components/icons/empty.jsx"
|
||||
import { CollapseControlled } from "@follow/components/ui/collapse/Collapse.js"
|
||||
import type { LinkProps } from "@follow/components/ui/link/LinkWithTooltip.js"
|
||||
import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
|
||||
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipPortal,
|
||||
TooltipTrigger,
|
||||
} from "@follow/components/ui/tooltip/index.jsx"
|
||||
import { cn, isBizId } from "@follow/utils/utils"
|
||||
import { noop } from "foxact/noop"
|
||||
import type { Components } from "hast-util-to-jsx-runtime"
|
||||
import { m } from "motion/react"
|
||||
import { useMemo, useState } from "react"
|
||||
import { Trans, useTranslation } from "react-i18next"
|
||||
|
||||
import { useGeneralSettingSelector } from "~/atoms/settings/general"
|
||||
import { Markdown } from "~/components/ui/markdown/Markdown"
|
||||
import { MarkdownLink } from "~/components/ui/markdown/renderers"
|
||||
import { usePeekModal } from "~/hooks/biz/usePeekModal"
|
||||
import { useAuthQuery } from "~/hooks/common"
|
||||
import { apiClient } from "~/lib/api-fetch"
|
||||
import { defineQuery } from "~/lib/defineQuery"
|
||||
|
||||
import { remarkSnowflakeId } from "./plugins/parse-snowflake"
|
||||
import type { DailyItemProps, DailyView } from "./types"
|
||||
import { useParseDailyDate } from "./useParseDailyDate"
|
||||
|
||||
export const DailyItem = ({
|
||||
view,
|
||||
day,
|
||||
className,
|
||||
onClick,
|
||||
isOpened,
|
||||
}: DailyItemProps & {
|
||||
isOpened: boolean
|
||||
onClick: () => void
|
||||
}) => {
|
||||
const { title, startDate, endDate } = useParseDailyDate(day)
|
||||
|
||||
return (
|
||||
<CollapseControlled
|
||||
isOpened={isOpened}
|
||||
onOpenChange={noop}
|
||||
collapseId={`${day}`}
|
||||
hideArrow
|
||||
contentClassName="flex-1 flex flex-col"
|
||||
title={
|
||||
<button type="button" className="container" onClick={onClick}>
|
||||
<DailyReportTitle title={title} startDate={startDate} endDate={endDate} />
|
||||
</button>
|
||||
}
|
||||
className={cn(className, "mx-auto w-full max-w-lg border-b last:border-b-0")}
|
||||
>
|
||||
<DailyReportContent endDate={endDate} view={view} startDate={startDate} />
|
||||
</CollapseControlled>
|
||||
)
|
||||
}
|
||||
|
||||
export const DailyReportTitle = ({
|
||||
endDate,
|
||||
startDate,
|
||||
title,
|
||||
containerClassName,
|
||||
}: {
|
||||
title: string
|
||||
startDate: number
|
||||
endDate: number
|
||||
containerClassName?: string
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const language = useGeneralSettingSelector((s) => s.language)
|
||||
const locale = useMemo(() => {
|
||||
try {
|
||||
return new Intl.Locale(language)
|
||||
} catch {
|
||||
return new Intl.Locale("en-US")
|
||||
}
|
||||
}, [language])
|
||||
|
||||
return (
|
||||
<m.div
|
||||
className={cn("flex items-center justify-center gap-2 pb-6 text-base", containerClassName)}
|
||||
layoutId={`daily-report-title-${title}`}
|
||||
transition={Spring.presets.smooth}
|
||||
>
|
||||
<i className="i-mgc-ai-cute-re" />
|
||||
<div className="font-medium">{t("ai_daily.title", { title })}</div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<i className="i-mgc-question-cute-re text-sm" />
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>
|
||||
<ul className="list-outside list-decimal text-wrap pl-6 text-left text-sm">
|
||||
<li>
|
||||
<Trans
|
||||
i18nKey="ai_daily.tooltip.content"
|
||||
components={{
|
||||
From: (
|
||||
<span>
|
||||
{new Date(startDate).toLocaleTimeString(locale, {
|
||||
weekday: "short",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
})}
|
||||
</span>
|
||||
),
|
||||
To: (
|
||||
<span>
|
||||
{new Date(endDate + 1).toLocaleTimeString(locale, {
|
||||
weekday: "short",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
})}
|
||||
</span>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
<li>{t("ai_daily.tooltip.update_schedule")}</li>
|
||||
</ul>
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
</m.div>
|
||||
)
|
||||
}
|
||||
|
||||
const useQueryData = ({
|
||||
endDate,
|
||||
startDate,
|
||||
view,
|
||||
}: Pick<DailyReportContentProps, "view" | "startDate" | "endDate">) =>
|
||||
useAuthQuery(
|
||||
defineQuery(["daily", view, startDate, endDate], async () => {
|
||||
const res = await apiClient.ai.daily.$get({
|
||||
query: {
|
||||
startDate: `${+startDate}`,
|
||||
view: `${view}`,
|
||||
},
|
||||
})
|
||||
return res.data
|
||||
}),
|
||||
{
|
||||
meta: {
|
||||
persist: true,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
interface DailyReportContentProps {
|
||||
view: DailyView
|
||||
startDate: number
|
||||
endDate: number
|
||||
}
|
||||
|
||||
const DailyReportContent: Component<DailyReportContentProps> = ({
|
||||
endDate,
|
||||
startDate,
|
||||
|
||||
view,
|
||||
}) => {
|
||||
const content = useQueryData({ endDate, startDate, view })
|
||||
|
||||
const RelatedEntryLink = useState(() => createRelatedEntryLink("modal"))[0]
|
||||
|
||||
return (
|
||||
<div className="relative h-0 flex-1 grow">
|
||||
<div className="absolute inset-0 flex">
|
||||
<ScrollArea.ScrollArea mask flex viewportClassName="grow" rootClassName="grow">
|
||||
{content.isLoading ? (
|
||||
<LoadingCircle size="large" className="center flex h-[calc(100vh-176px)] text-center" />
|
||||
) : (
|
||||
!!content.data && (
|
||||
<m.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={Spring.smooth(0.35)}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<Markdown
|
||||
applyMiddleware={(pipeline) => {
|
||||
pipeline.use(remarkSnowflakeId)
|
||||
|
||||
return pipeline
|
||||
}}
|
||||
components={
|
||||
{
|
||||
"snowflake-id": SnowflakeId,
|
||||
a: RelatedEntryLink as Components["a"],
|
||||
} as any as Components
|
||||
}
|
||||
className="prose-sm prose-p:my-1 prose-ul:my-1 prose-ul:list-outside prose-ul:list-disc prose-li:marker:text-accent mt-4 px-6"
|
||||
>
|
||||
{content.data}
|
||||
</Markdown>
|
||||
</m.div>
|
||||
)
|
||||
)}
|
||||
</ScrollArea.ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const DailyReportModalContent: Component<DailyReportContentProps> = ({
|
||||
endDate,
|
||||
startDate,
|
||||
view,
|
||||
}) => {
|
||||
const content = useQueryData({ endDate, startDate, view })
|
||||
const { t } = useTranslation()
|
||||
const RelatedEntryLink = useState(() => createRelatedEntryLink("toast"))[0]
|
||||
|
||||
if (!content.data && !content.isLoading)
|
||||
return (
|
||||
<div className="center pointer-events-none inset-0 my-8 flex-col gap-4 opacity-80 lg:absolute lg:my-0 lg:translate-y-6">
|
||||
<EmptyIcon />
|
||||
<p>{t("ai_daily.no_found")}</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="center grow flex-col">
|
||||
<div className="flex grow flex-col">
|
||||
{content.isLoading ? (
|
||||
<LoadingCircle
|
||||
size="large"
|
||||
className="center pointer-events-none h-24 text-center lg:absolute lg:inset-0 lg:mt-8 lg:h-auto"
|
||||
/>
|
||||
) : content.data ? (
|
||||
<Markdown
|
||||
components={{
|
||||
a: RelatedEntryLink as Components["a"],
|
||||
}}
|
||||
className="prose-sm prose-p:my-1 prose-ul:my-1 prose-ul:list-outside prose-ul:list-disc prose-li:marker:text-accent mt-4 grow overflow-auto px-2 lg:h-0 lg:px-6"
|
||||
>
|
||||
{content.data}
|
||||
</Markdown>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const createRelatedEntryLink = (variant: "toast" | "modal") => (props: LinkProps) => {
|
||||
const { href, children } = props
|
||||
const entryId = isBizId(href) ? href : null
|
||||
|
||||
const peekModal = usePeekModal()
|
||||
if (!entryId) {
|
||||
return <MarkdownLink {...props} />
|
||||
}
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="follow-link--underline text-text cursor-pointer font-semibold no-underline"
|
||||
onClick={() => {
|
||||
peekModal(entryId, variant)
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<i className="i-mgc-arrow-right-up-cute-re size-[0.9em] translate-y-[2px] opacity-70" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
interface SnowflakeIdProps {
|
||||
id: string
|
||||
children?: React.ReactNode
|
||||
index: number
|
||||
}
|
||||
|
||||
const SnowflakeId: React.FC<SnowflakeIdProps> = ({ id: entryId, index }) => {
|
||||
const peekModal = usePeekModal()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="follow-link--underline text-text cursor-pointer font-semibold no-underline"
|
||||
onClick={() => {
|
||||
peekModal(entryId, "modal")
|
||||
}}
|
||||
>
|
||||
<sup className="inline-flex items-center gap-0.5 font-medium">
|
||||
<span className="text-[0.65rem] opacity-70">{index}</span>
|
||||
</sup>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
import type { Parent } from "mdast"
|
||||
import type { Node } from "unist"
|
||||
import { visit } from "unist-util-visit"
|
||||
|
||||
interface SnowflakeOptions {
|
||||
component?: string
|
||||
}
|
||||
|
||||
const SNOWFLAKE_PATTERN = /\[(\d{15,20})\]/g
|
||||
|
||||
function isTextNode(node: Node): node is { type: "text"; value: string } {
|
||||
return node.type === "text"
|
||||
}
|
||||
|
||||
export function remarkSnowflakeId(options: SnowflakeOptions = {}) {
|
||||
const { component = "snowflake-id" } = options
|
||||
|
||||
return (tree: Node) => {
|
||||
let idIndex = 1
|
||||
visit(tree, "text", (node, index, parent: Parent) => {
|
||||
if (!isTextNode(node) || !parent) return
|
||||
|
||||
const { value } = node as { value: string }
|
||||
const matches = Array.from(value.matchAll(SNOWFLAKE_PATTERN))
|
||||
|
||||
if (matches.length === 0) return
|
||||
|
||||
const result: (Node | string)[] = []
|
||||
let lastIndex = 0
|
||||
|
||||
for (const match of matches) {
|
||||
if (!Array.isArray(match)) continue
|
||||
|
||||
const [fullMatch, snowflakeId] = match
|
||||
const matchIndex = match.index!
|
||||
|
||||
// Add text before the match
|
||||
if (matchIndex > lastIndex) {
|
||||
result.push({
|
||||
type: "text",
|
||||
// @ts-expect-error
|
||||
value: value.slice(lastIndex, matchIndex),
|
||||
})
|
||||
}
|
||||
|
||||
// Add snowflake node
|
||||
result.push({
|
||||
type: "snowflakeId",
|
||||
data: {
|
||||
hName: component,
|
||||
hProperties: {
|
||||
id: snowflakeId,
|
||||
index: idIndex++,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
children: [
|
||||
{
|
||||
type: "text",
|
||||
value: fullMatch,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
lastIndex = matchIndex + fullMatch.length
|
||||
}
|
||||
|
||||
// Add any remaining text after the last match
|
||||
if (lastIndex < value.length) {
|
||||
result.push({
|
||||
type: "text",
|
||||
// @ts-expect-error
|
||||
value: value.slice(lastIndex),
|
||||
})
|
||||
}
|
||||
|
||||
// Replace the current node with the new nodes
|
||||
// @ts-expect-error
|
||||
parent.children.splice(index, 1, ...result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import type { FeedViewType } from "@follow/constants"
|
||||
|
||||
import type { DayOf } from "./constants"
|
||||
|
||||
export type DailyView = Extract<FeedViewType, FeedViewType.Articles | FeedViewType.SocialMedia>
|
||||
export interface DailyItemProps {
|
||||
view: DailyView
|
||||
day: DayOf
|
||||
|
||||
className?: string
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import { useCallback } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
|
||||
import { FeedDailyModalContent } from "./FeedDailyModalContent"
|
||||
|
||||
export const useAIDailyReportModal = () => {
|
||||
const { present } = useModalStack()
|
||||
const { t } = useTranslation()
|
||||
|
||||
return useCallback(() => {
|
||||
present({
|
||||
content: () => <FeedDailyModalContent />,
|
||||
title: t("ai_daily.header"),
|
||||
resizeable: true,
|
||||
clickOutsideToDismiss: true,
|
||||
|
||||
resizeDefaultSize: { width: 660, height: 450 },
|
||||
})
|
||||
}, [present, t])
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { DayOf } from "./constants"
|
||||
|
||||
export const useParseDailyDate = (day: DayOf) => {
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
return useMemo(() => {
|
||||
const dateObj = new Date()
|
||||
|
||||
const nowHour = dateObj.getHours()
|
||||
let startDate: number
|
||||
let endDate: number
|
||||
let title: string
|
||||
|
||||
const today8AM = dateObj.setHours(8, 0, 0, 0)
|
||||
const today8PM = dateObj.setHours(20, 0, 0, 0)
|
||||
dateObj.setDate(dateObj.getDate() - 1)
|
||||
const yesterday8AM = dateObj.setHours(8, 0, 0, 0)
|
||||
const yesterday8PM = dateObj.setHours(20, 0, 0, 0)
|
||||
dateObj.setDate(dateObj.getDate() - 1)
|
||||
const dayBeforeYesterday8PM = dateObj.setHours(20, 0, 0, 0)
|
||||
|
||||
const isToday = day === DayOf.Today
|
||||
// For index 0, get the last past 8 AM or 8 PM; for index 1, get the second last past 8 AM or 8 PM.
|
||||
if (nowHour >= 20) {
|
||||
if (isToday) {
|
||||
endDate = today8PM - 1
|
||||
startDate = today8AM
|
||||
title = t("time.today")
|
||||
} else {
|
||||
endDate = today8AM - 1
|
||||
startDate = yesterday8PM
|
||||
title = t("time.last_night")
|
||||
}
|
||||
} else if (nowHour >= 8) {
|
||||
if (isToday) {
|
||||
endDate = today8AM - 1
|
||||
startDate = yesterday8PM
|
||||
title = t("time.last_night")
|
||||
} else {
|
||||
endDate = yesterday8PM - 1
|
||||
startDate = yesterday8AM
|
||||
title = t("time.yesterday")
|
||||
}
|
||||
} else {
|
||||
if (isToday) {
|
||||
endDate = yesterday8PM - 1
|
||||
startDate = yesterday8AM
|
||||
title = t("time.yesterday")
|
||||
} else {
|
||||
endDate = yesterday8AM - 1
|
||||
startDate = dayBeforeYesterday8PM
|
||||
title = t("time.the_night_before_last")
|
||||
}
|
||||
}
|
||||
|
||||
return { title, startDate, endDate }
|
||||
}, [day, t])
|
||||
}
|
||||
|
|
@ -1,106 +1,19 @@
|
|||
import { CollapseGroup } from "@follow/components/ui/collapse/Collapse.js"
|
||||
import { FeedViewType } from "@follow/constants"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { atom, useAtomValue, useSetAtom } from "jotai"
|
||||
import { AnimatePresence, LayoutGroup, m } from "motion/react"
|
||||
import * as React from "react"
|
||||
|
||||
import { GlassButton } from "~/components/ui/button/GlassButton"
|
||||
import { ROUTE_FEED_PENDING } from "~/constants"
|
||||
import { useRouteParams } from "~/hooks/biz/useRouteParams"
|
||||
import { DayOf } from "~/modules/ai-daily/constants"
|
||||
import { DailyItem } from "~/modules/ai-daily/daily"
|
||||
import { EntryPlaceholderDaily } from "~/modules/ai-daily/EntryPlaceholderDaily"
|
||||
import type { DailyView } from "~/modules/ai-daily/types"
|
||||
import { EntryPlaceholderLogo } from "~/modules/entry-content/components/EntryPlaceholderLogo"
|
||||
|
||||
import type { EntryContentPlaceholderContextValue } from "./EntryContentPlaceholderContext"
|
||||
import { EntryContentPlaceholderContext } from "./EntryContentPlaceholderContext"
|
||||
|
||||
export const EntryContentPlaceholder = () => {
|
||||
const { feedId, view } = useRouteParams()
|
||||
|
||||
const ctxValue = React.useMemo<EntryContentPlaceholderContextValue>(
|
||||
() => ({
|
||||
openedSummary: atom<null | DayOf>(null),
|
||||
}),
|
||||
[],
|
||||
)
|
||||
|
||||
const openedSummary = useAtomValue(ctxValue.openedSummary)
|
||||
return (
|
||||
<LayoutGroup>
|
||||
<EntryContentPlaceholderContext value={ctxValue}>
|
||||
<AnimatePresence>
|
||||
{openedSummary === null ? (
|
||||
<m.div
|
||||
className="center size-full flex-col"
|
||||
initial={{ opacity: 0.01, y: 300 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
<EntryPlaceholderLogo />
|
||||
{feedId === ROUTE_FEED_PENDING && view === FeedViewType.Articles && (
|
||||
<EntryPlaceholderDaily view={view} />
|
||||
)}
|
||||
</m.div>
|
||||
) : (
|
||||
<SummaryDetailContent />
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</EntryContentPlaceholderContext>
|
||||
<AnimatePresence>
|
||||
<m.div
|
||||
className="center size-full flex-col"
|
||||
initial={{ opacity: 0.01, y: 300 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
<EntryPlaceholderLogo />
|
||||
</m.div>
|
||||
</AnimatePresence>
|
||||
</LayoutGroup>
|
||||
)
|
||||
}
|
||||
|
||||
const SummaryDetailContent = () => {
|
||||
const { view } = useRouteParams()
|
||||
const ctxValue = React.use(EntryContentPlaceholderContext)
|
||||
const openedSummary = useAtomValue(ctxValue.openedSummary)
|
||||
const setOpenedSummary = useSetAtom(ctxValue.openedSummary)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fade-in-0 absolute right-4 top-4 duration-200">
|
||||
<GlassButton
|
||||
description="Back"
|
||||
onClick={() => setOpenedSummary(null)}
|
||||
size="sm"
|
||||
className="opacity-100"
|
||||
variant="flat"
|
||||
>
|
||||
<i className="i-mgc-close-cute-re" />
|
||||
</GlassButton>
|
||||
</div>
|
||||
|
||||
<CollapseGroup defaultOpenId={`${openedSummary}`}>
|
||||
<DailyItem
|
||||
isOpened={openedSummary === DayOf.Today}
|
||||
onClick={() => {
|
||||
if (openedSummary === DayOf.Today) {
|
||||
setOpenedSummary(null)
|
||||
} else {
|
||||
setOpenedSummary(DayOf.Today)
|
||||
}
|
||||
}}
|
||||
day={DayOf.Today}
|
||||
view={view as DailyView}
|
||||
className={cn(openedSummary === DayOf.Today && "grow", "pt-6")}
|
||||
/>
|
||||
|
||||
<DailyItem
|
||||
isOpened={openedSummary === DayOf.Yesterday}
|
||||
onClick={() => {
|
||||
if (openedSummary === DayOf.Yesterday) {
|
||||
setOpenedSummary(null)
|
||||
} else {
|
||||
setOpenedSummary(DayOf.Yesterday)
|
||||
}
|
||||
}}
|
||||
day={DayOf.Yesterday}
|
||||
view={view as DailyView}
|
||||
className={cn(openedSummary === DayOf.Yesterday && "grow", "pt-6")}
|
||||
/>
|
||||
</CollapseGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
import type { PrimitiveAtom } from "jotai"
|
||||
import { createContext } from "react"
|
||||
|
||||
import type { DayOf } from "~/modules/ai-daily/constants"
|
||||
|
||||
export const EntryContentPlaceholderContext = createContext<EntryContentPlaceholderContextValue>(
|
||||
null!,
|
||||
)
|
||||
|
||||
export type EntryContentPlaceholderContextValue = {
|
||||
openedSummary: PrimitiveAtom<null | DayOf>
|
||||
}
|
||||
|
|
@ -30,7 +30,6 @@ import { useFeedHeaderTitle } from "~/store/feed/hooks"
|
|||
import { MarkAllReadButton } from "../components/mark-all-button"
|
||||
import { useIsPreviewFeed } from "../hooks/useIsPreviewFeed"
|
||||
import { AppendTaildingDivider } from "./AppendTaildingDivider"
|
||||
import { DailyReportButton } from "./buttons/DailyReportButton"
|
||||
import { SwitchToMasonryButton } from "./buttons/SwitchToMasonryButton"
|
||||
import { WideModeButton } from "./buttons/WideModeButton"
|
||||
|
||||
|
|
@ -99,7 +98,6 @@ export const EntryListHeader: FC<{
|
|||
|
||||
<AppendTaildingDivider>
|
||||
{!views[view]!.wideMode && !aiEnabled && <WideModeButton />}
|
||||
{view === FeedViewType.SocialMedia && <DailyReportButton />}
|
||||
{view === FeedViewType.Pictures && <SwitchToMasonryButton />}
|
||||
</AppendTaildingDivider>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
import { ActionButton } from "@follow/components/ui/button/action-button.js"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import type { FC } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useAIDailyReportModal } from "~/modules/ai-daily/useAIDailyReportModal"
|
||||
|
||||
export const DailyReportButton: FC = () => {
|
||||
const present = useAIDailyReportModal()
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
onClick={() => {
|
||||
present()
|
||||
tracker.dailyReportModal()
|
||||
}}
|
||||
tooltip={t("entry_list_header.daily_report")}
|
||||
>
|
||||
<i className="i-mgc-ai-cute-re" />
|
||||
</ActionButton>
|
||||
)
|
||||
}
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
"activation.plan.title": "Upgrade Plan",
|
||||
"activation.plan.upgrade": "Upgrade",
|
||||
"activation.title": "Invitation Code",
|
||||
"ai_daily.header": "AI Daily Report",
|
||||
"ai_daily.no_found": "No AI news found for this period.",
|
||||
"ai_daily.title": "Top News - {{title}}",
|
||||
"ai_daily.tooltip.content": "Here is news selected by AI from your timeline (<From /> - <To />) that may be important to you.",
|
||||
"ai_daily.tooltip.update_schedule": "Update daily at 8 AM and 8 PM.",
|
||||
"app.copy_logo_svg": "Copy Logo SVG",
|
||||
"app.copy_logo_text_svg": "Copy Logo Text SVG",
|
||||
"app.toggle_sidebar": "Toggle Sidebar",
|
||||
|
|
@ -188,7 +183,6 @@
|
|||
"entry_content.support_creator": "Support Creator",
|
||||
"entry_content.web_app_notice": "Maybe web app doesn't support this content type. But you can download the desktop app.",
|
||||
"entry_list.zero_unread": "Zero Unread",
|
||||
"entry_list_header.daily_report": "Daily Report",
|
||||
"entry_list_header.grid": "Grid",
|
||||
"entry_list_header.items": "items",
|
||||
"entry_list_header.masonry": "Masonry",
|
||||
|
|
|
|||
|
|
@ -26,11 +26,6 @@
|
|||
"activation.plan.title": "プランのアップグレード",
|
||||
"activation.plan.upgrade": "アップグレード",
|
||||
"activation.title": "招待コード",
|
||||
"ai_daily.header": "AI デイリーリポート",
|
||||
"ai_daily.no_found": "この期間の AI ニュースはありません。",
|
||||
"ai_daily.title": "トップニュース - {{title}}",
|
||||
"ai_daily.tooltip.content": "こちらは、AI があなたのタイムラインから選んだニュースです(<From /> - <To />)。あなたにとって重要なニュースかもしれません。",
|
||||
"ai_daily.tooltip.update_schedule": "毎日午前 8 時と午後 8 時に更新されます。",
|
||||
"app.copy_logo_svg": "ロゴ SVG をコピー",
|
||||
"app.copy_logo_text_svg": "ロゴテキスト SVG をコピー",
|
||||
"app.toggle_sidebar": "サイドバーを切り替え",
|
||||
|
|
@ -188,7 +183,6 @@
|
|||
"entry_content.support_creator": "作成者をサポート",
|
||||
"entry_content.web_app_notice": "このコンテンツタイプはウェブアプリではサポートされていないかもしれません。デスクトップアプリをダウンロードしてください。",
|
||||
"entry_list.zero_unread": "未読ゼロ",
|
||||
"entry_list_header.daily_report": "日報",
|
||||
"entry_list_header.grid": "グリッド",
|
||||
"entry_list_header.items": "項目",
|
||||
"entry_list_header.masonry": "メイソンリー",
|
||||
|
|
|
|||
|
|
@ -26,11 +26,6 @@
|
|||
"activation.plan.title": "升级计划",
|
||||
"activation.plan.upgrade": "升级",
|
||||
"activation.title": "邀请码",
|
||||
"ai_daily.header": "AI 日报",
|
||||
"ai_daily.no_found": "在此期间未找到 AI 新闻。",
|
||||
"ai_daily.title": "热点 - {{title}}",
|
||||
"ai_daily.tooltip.content": "AI 根据时间线(<From /> - <To />)提取的重点信息。",
|
||||
"ai_daily.tooltip.update_schedule": "每天 8:00 和 20:00 定时更新。",
|
||||
"app.copy_logo_svg": "复制 Logo SVG",
|
||||
"app.copy_logo_text_svg": "复制徽标 SVG 文本",
|
||||
"app.toggle_sidebar": "切换侧边栏",
|
||||
|
|
@ -188,7 +183,6 @@
|
|||
"entry_content.support_creator": "支持作者",
|
||||
"entry_content.web_app_notice": "网页版不支持展示此类型,请下载客户端查看。",
|
||||
"entry_list.zero_unread": "全部已读",
|
||||
"entry_list_header.daily_report": "每日总结",
|
||||
"entry_list_header.grid": "网格布局",
|
||||
"entry_list_header.items": "内容",
|
||||
"entry_list_header.masonry": "瀑布流布局",
|
||||
|
|
|
|||
|
|
@ -23,11 +23,6 @@
|
|||
"activation.activate": "啟用",
|
||||
"activation.description": "在公開測試階段,您需要邀請碼才能使用此功能。",
|
||||
"activation.title": "邀請碼",
|
||||
"ai_daily.header": "AI 日報",
|
||||
"ai_daily.no_found": "該期間未找到 AI 新聞",
|
||||
"ai_daily.title": "頭條新聞 - {{title}}",
|
||||
"ai_daily.tooltip.content": "以下是 AI 從您的時間軸中選擇的新聞(<From /> - <To />),可能對您很重要。",
|
||||
"ai_daily.tooltip.update_schedule": "每天早上 8 點和晚上 8 點更新。",
|
||||
"app.copy_logo_svg": "複製 Logo SVG",
|
||||
"app.copy_logo_text_svg": "複製圖標 SVG 文字",
|
||||
"app.toggle_sidebar": "切換側邊欄",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"entry_content.support_creator": "贊助作者",
|
||||
"entry_content.web_app_notice": "Web 應用程式不支援此類型內容。您可以下載桌面應用程式。",
|
||||
"entry_list.zero_unread": "全部已讀",
|
||||
"entry_list_header.daily_report": "每日報告",
|
||||
"entry_list_header.grid": "格線佈局",
|
||||
"entry_list_header.items": "內容",
|
||||
"entry_list_header.masonry": "瀑布式佈局",
|
||||
|
|
|
|||
|
|
@ -43,10 +43,6 @@ export class TrackerPoints {
|
|||
this.track(TrackerMapper.Integration, props)
|
||||
}
|
||||
|
||||
dailyReportModal() {
|
||||
this.track(TrackerMapper.DailyReportModal)
|
||||
}
|
||||
|
||||
switchToMasonry() {
|
||||
this.track(TrackerMapper.SwitchToMasonry)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue