Skip to content

Commit

Permalink
feat: ai daily modal
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Aug 7, 2024
1 parent ff16272 commit 67d9559
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 258 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"!unused-imports/no-unused-imports",
"!@stylistic/jsx-self-closing-comp",
"!tailwindcss/classnames-order",
"!arrow-body-style",
"*"
],

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/components/common/ErrorElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function ErrorElement() {
}

return (
<div className="m-auto flex min-h-full max-w-prose select-text flex-col p-8 pt-12">
<div className="m-auto flex min-h-full max-w-prose select-text flex-col p-8 pt-24">
<div className="drag-region fixed inset-x-0 top-0 h-12" />
<div className="center flex flex-col">
<i className="i-mgc-bug-cute-re size-12 text-red-400" />
Expand Down
33 changes: 33 additions & 0 deletions src/renderer/src/modules/ai/ai-daily/EntryPlaceholderDaily.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { CollapseGroup } from "@renderer/components/ui/collapse"
import { useCollapseGroupItemState } from "@renderer/components/ui/collapse/hooks"
import { cn } from "@renderer/lib/utils"
import { setEntryContentPlaceholderLogoShow } from "@renderer/modules/entry-content/atoms"
import { useEffect } from "react"

import { DayOf } from "./constants"
import { DailyItem } from "./daily"
import type { DailyView } from "./types"

export const EntryPlaceholderDaily = ({
view,
className,
}: {
view: DailyView
className?: string
}) => (
<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
}
43 changes: 43 additions & 0 deletions src/renderer/src/modules/ai/ai-daily/FeedDailyModalContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { AutoResizeHeight } from "@renderer/components/ui/auto-resize-height"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@renderer/components/ui/tabs"
import { FeedViewType } from "@renderer/lib/enum"

import { DayOf } from "./constants"
import { DailyReportContent, DailyReportTitle } from "./daily"
import { useParseDailyDate } from "./hooks"

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}>
<TabsList className="w-full">
{tabs.map((tab: any) => (
<TabsTrigger key={tab} value={tab}>
{/* {tab} */}
<DailyReportTitle {...(tab === DayOf.Today ? today : yesterday)} />
</TabsTrigger>
))}
</TabsList>
<AutoResizeHeight spring>
{tabs.map((tab: any) => (
<TabsContent key={tab} value={tab} className="mt-8">
<DailyReportContent
// TODO support other view types
view={FeedViewType.SocialMedia}
{...(tab === DayOf.Today ? today : yesterday)}
/>
</TabsContent>
))}
</AutoResizeHeight>
</Tabs>
)
}
4 changes: 4 additions & 0 deletions src/renderer/src/modules/ai/ai-daily/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum DayOf {
Today,
Yesterday,
}
160 changes: 160 additions & 0 deletions src/renderer/src/modules/ai/ai-daily/daily.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { AutoResizeHeight } from "@renderer/components/ui/auto-resize-height"
import { Card, CardHeader } from "@renderer/components/ui/card"
import { Collapse } from "@renderer/components/ui/collapse"
import { LoadingCircle } from "@renderer/components/ui/loading"
import { ScrollArea } from "@renderer/components/ui/scroll-area"
import {
Tooltip,
TooltipContent,
TooltipPortal,
TooltipTrigger,
} from "@renderer/components/ui/tooltip"
import { useAuthQuery } from "@renderer/hooks/common"
import { apiClient } from "@renderer/lib/api-fetch"
import { defineQuery } from "@renderer/lib/defineQuery"
import { parseMarkdown } from "@renderer/lib/parse-markdown"
import { MarkAllButton } from "@renderer/modules/entry-column/mark-all-button"
import type { FC } from "react"
import { useMemo, useState } from "react"

import { useParseDailyDate } from "./hooks"
import type { DailyItemProps, DailyView } from "./types"

export const DailyItem = ({ view, day }: DailyItemProps) => {
const { title, startDate, endDate } = useParseDailyDate(day)
return (
<Collapse
hideArrow
title={
<DailyReportTitle title={title} startDate={startDate} endDate={endDate} />
}
className="mx-auto w-full max-w-lg border-b pb-6 last:border-b-0"
>
<DailyReportContent endDate={endDate} view={view} startDate={startDate} />
</Collapse>
)
}

export const DailyReportTitle = ({
endDate,
startDate,
title,
}: {
title: string
startDate: number
endDate: number
}) => (
<div className="flex items-center justify-center gap-2 text-base">
<i className="i-mgc-magic-2-cute-re" />
<div className="font-medium">
{title}
's Top News
</div>
<Tooltip>
<TooltipTrigger asChild>
<i className="i-mgc-question-cute-re translate-y-px" />
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>
<ul className="list-outside list-decimal text-wrap pl-6 text-left text-sm">
<li>
Here is news selected by AI from your timeline
{" ("}
{new Date(startDate).toLocaleTimeString("en-US", {
weekday: "short",
hour: "numeric",
minute: "numeric",
})}
{" "}
-
{" "}
{new Date(endDate + 1).toLocaleTimeString("en-US", {
weekday: "short",
hour: "numeric",
minute: "numeric",
})}
{") "}
that may be important to you.
</li>
<li>Update daily at 8 AM and 8 PM.</li>
</ul>
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
)

export const DailyReportContent: FC<{
view: DailyView
startDate: number
endDate: number
}> = ({ endDate, startDate, view }) => {
const content = 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,
},
},
)

const [markAllButtonRef, setMarkAllButtonRef] =
useState<HTMLButtonElement | null>(null)

const eleContent = useMemo(() => {
if (!content.data) return null
const { content: _content } = parseMarkdown(content.data)
return _content
}, [content.data])

return (
<Card className="border-none bg-transparent">
<CardHeader className="space-y-0 p-0">
<ScrollArea.ScrollArea
mask={false}
flex
viewportClassName="max-h-[calc(100vh-500px)]"
>
<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
type="button"
onClick={() => {
markAllButtonRef?.click()
}}
className="!mt-4 ml-auto flex items-center rounded-lg py-1 pr-2 duration-200 hover:!bg-theme-button-hover"
>
<MarkAllButton
ref={setMarkAllButtonRef}
filter={{
startTime: startDate,
endTime: endDate,
}}
className="pointer-events-none text-[1.05rem]"
/>
<span>Mark all as read</span>
</button>
)}
</CardHeader>
</Card>
)
}
71 changes: 71 additions & 0 deletions src/renderer/src/modules/ai/ai-daily/hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useModalStack } from "@renderer/components/ui/modal"
import { useCallback, useMemo } from "react"

import { DayOf } from "./constants"
import { FeedDailyModalContent } from "./FeedDailyModalContent"

export const useParseDailyDate = (day: DayOf) =>
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 = "Today"
} else {
endDate = today8AM - 1
startDate = yesterday8PM
title = "Last Night"
}
} else if (nowHour >= 8) {
if (isToday) {
endDate = today8AM - 1
startDate = yesterday8PM
title = "Last Night"
} else {
endDate = yesterday8PM - 1
startDate = yesterday8AM
title = "Yesterday"
}
} else {
if (isToday) {
endDate = yesterday8PM - 1
startDate = yesterday8AM
title = "Yesterday"
} else {
endDate = yesterday8AM - 1
startDate = dayBeforeYesterday8PM
title = "The Night Before Last"
}
}

return { title, startDate, endDate }
}, [day])

export const useAIDailyReportModal = () => {
const { present } = useModalStack()

return useCallback(() => {
present({
content: () => <FeedDailyModalContent />,
title: "AI Daily Report",
clickOutsideToDismiss: true,
})
}, [present])
}
12 changes: 12 additions & 0 deletions src/renderer/src/modules/ai/ai-daily/types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { FeedViewType } from "@renderer/lib/enum"

import type { DayOf } from "./constants"

export type DailyView = Extract<
FeedViewType,
FeedViewType.Articles | FeedViewType.SocialMedia
>
export interface DailyItemProps {
view: DailyView
day: DayOf
}
18 changes: 3 additions & 15 deletions src/renderer/src/modules/entry-column/EntryListHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
import { useWhoami } from "@renderer/atoms/user"
import { ActionButton } from "@renderer/components/ui/button"
import { DividerVertical } from "@renderer/components/ui/divider"
import { useModalStack } from "@renderer/components/ui/modal"
import { EllipsisHorizontalTextWithTooltip } from "@renderer/components/ui/typography"
import {
FEED_COLLECTION_LIST,
Expand All @@ -23,7 +22,7 @@ import { useRefreshFeedMutation } from "@renderer/queries/feed"
import { useFeedById, useFeedHeaderTitle } from "@renderer/store/feed"
import type { FC } from "react"

import { Daily } from "../entry-content/daily"
import { useAIDailyReportModal } from "../ai/ai-daily/hooks"
import { EntryHeader } from "../entry-content/header"
import { MarkAllButton } from "./mark-all-button"

Expand Down Expand Up @@ -169,21 +168,10 @@ export const EntryListHeader: FC<{
}

const DailyReportButton: FC = () => {
const { present } = useModalStack()

const present = useAIDailyReportModal()
return (
<ActionButton
onClick={() => {
present({
title: "Daily Report",
content: () => (
<Daily
view={FeedViewType.SocialMedia}

/>
),
})
}}
onClick={present}
tooltip="Daily Report"
>
<i className="i-mgc-magic-2-cute-re" />
Expand Down
Loading

0 comments on commit 67d9559

Please sign in to comment.