feat: dynamic update relative time
Signed-off-by: Innei <i@innei.in>
This commit is contained in:
parent
86d366db76
commit
744422fc12
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue