feat: dynamic update relative time

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-08-06 13:08:14 +08:00
parent 86d366db76
commit 744422fc12
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
2 changed files with 136 additions and 37 deletions

View File

@ -1,8 +1,13 @@
import dayjs from "dayjs"
import type { FC } from "react"
import { useEffect, useState } from "react"
import { useEffect, useRef, useState } from "react"
import { Tooltip, TooltipContent, TooltipPortal, TooltipTrigger } from "../tooltip"
import {
Tooltip,
TooltipContent,
TooltipPortal,
TooltipTrigger,
} from "../tooltip"
const formatTime = (date: string | Date, relativeBeforeDay?: number) => {
if (
@ -15,23 +20,56 @@ const formatTime = (date: string | Date, relativeBeforeDay?: number) => {
.duration(dayjs(date).diff(dayjs(), "minute"), "minute")
.humanize()
}
const getUpdateInterval = (date: string | Date, relativeBeforeDay?: number) => {
if (!relativeBeforeDay) return null
const diffInSeconds = Math.abs(dayjs(date).diff(new Date(), "second"))
if (diffInSeconds <= 60) {
return 1000 // Update every second
}
const diffInMinutes = Math.abs(dayjs(date).diff(new Date(), "minute"))
if (diffInMinutes <= 60) {
return 60000 // Update every minute
}
const diffInHours = Math.abs(dayjs(date).diff(new Date(), "hour"))
if (diffInHours <= 24) {
return 3600000 // Update every hour
}
const diffInDays = Math.abs(dayjs(date).diff(new Date(), "day"))
if (diffInDays <= relativeBeforeDay) {
return 86400000 // Update every day
}
return null // No need to update
}
export const RelativeTime: FC<{
date: string | Date
displayAbsoluteTimeAfterDay?: number
}> = (props) => {
const { displayAbsoluteTimeAfterDay = 29 } = props
const [relative, setRelative] = useState<string>(
const [relative, setRelative] = useState<string>(() =>
formatTime(props.date, displayAbsoluteTimeAfterDay),
)
const timerRef = useRef<any>(null)
useEffect(() => {
setRelative(formatTime(props.date, displayAbsoluteTimeAfterDay))
let timer: any = setInterval(() => {
const updateRelativeTime = () => {
setRelative(formatTime(props.date, displayAbsoluteTimeAfterDay))
}, 1000)
const updateInterval = getUpdateInterval(
props.date,
displayAbsoluteTimeAfterDay,
)
if (updateInterval !== null) {
timerRef.current = setTimeout(updateRelativeTime, updateInterval)
}
}
updateRelativeTime()
return () => {
timer = clearInterval(timer)
clearTimeout(timerRef.current)
}
}, [props.date, displayAbsoluteTimeAfterDay])
@ -45,3 +83,59 @@ export const RelativeTime: FC<{
</Tooltip>
)
}
export const RelativeDay = ({ date }: { date: Date }) => {
const formatDateString = (date: Date) => {
const now = new Date()
// Remove the time part for comparison
const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const inputDate = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
)
const diffTime = nowDate.getTime() - inputDate.getTime()
const diffDays = diffTime / (1000 * 3600 * 24)
if (diffDays === 0) {
return "Today"
} else if (diffDays === 1) {
return "Yesterday"
} else {
return date.toLocaleDateString("en-US", {
weekday: "long",
month: "short",
day: "numeric",
})
}
}
const timerRef = useRef<any>(null)
const [dateString, setDateString] = useState<string>(() =>
formatDateString(date),
)
useEffect(() => {
const updateInterval = getUpdateInterval(date, 3)
if (updateInterval !== null) {
timerRef.current = setTimeout(() => {
setDateString(formatDateString(date))
}, updateInterval)
}
return () => {
clearTimeout(timerRef.current)
}
}, [date])
return (
<Tooltip>
<TooltipTrigger>{dateString}</TooltipTrigger>
<TooltipPortal>
<TooltipContent>{dayjs(date).format("llll")}</TooltipContent>
</TooltipPortal>
</Tooltip>
)
}

View File

@ -1,3 +1,4 @@
import { RelativeDay } from "@renderer/components/ui/datetime"
import { FeedViewType } from "@renderer/lib/enum"
import { cn } from "@renderer/lib/utils"
import { memo } from "react"
@ -5,37 +6,41 @@ import { memo } from "react"
import { MarkAllButton } from "./mark-all-button"
import { SocialMediaDateItem } from "./social-media-item"
export const DateItem = memo(({
date,
view,
isFirst,
}: {
date: string
view: FeedViewType
isFirst: boolean
}) => {
const dateObj = new Date(date)
const dateString = dateObj.toLocaleDateString("en-US", { weekday: "long", month: "short", day: "numeric" })
const startOfDay = new Date(dateObj.setHours(0, 0, 0, 0)).getTime()
const endOfDay = new Date(dateObj.setHours(23, 59, 59, 999)).getTime()
export const DateItem = memo(
({
date,
view,
isFirst,
}: {
date: string
view: FeedViewType
isFirst: boolean
}) => {
const dateObj = new Date(date)
const className = cn(isFirst ? "pt-2" : "pt-8", "flex items-center gap-1 pl-2 text-sm font-bold text-zinc-800 dark:text-neutral-400")
const startOfDay = new Date(dateObj.setHours(0, 0, 0, 0)).getTime()
const endOfDay = new Date(dateObj.setHours(23, 59, 59, 999)).getTime()
if (view === FeedViewType.SocialMedia) {
return <SocialMediaDateItem date={date} className={className} />
}
const className = cn(
isFirst ? "pt-2" : "pt-8",
"flex items-center gap-1 pl-2 text-sm font-bold text-zinc-800 dark:text-neutral-400",
)
return (
<div className={className}>
<MarkAllButton
className="size-7 text-base"
filter={{
startTime: startOfDay,
endTime: endOfDay,
}}
/>
{dateString}
</div>
)
},
if (view === FeedViewType.SocialMedia) {
return <SocialMediaDateItem date={date} className={className} />
}
return (
<div className={className}>
<MarkAllButton
className="size-7 text-base"
filter={{
startTime: startOfDay,
endTime: endOfDay,
}}
/>
<RelativeDay date={dateObj} />
</div>
)
},
)