Sync clock to minute boundary and stabilize "Updated" text

Clock: Instead of starting a 60s interval from mount time (which drifts
from the system clock), calculate ms until the next :00 second mark,
setTimeout to that point, then setInterval every 60s from there.

Updated text: Replaced formatDistanceToNow (which flickered between
"less than a minute ago" / "a minute ago" / "2 minutes ago" on each
render) with a stable minute-based calculation derived from clockNow:
"just now" / "1 min ago" / "N min ago".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-12 18:18:00 +08:00
parent 3afa894e1b
commit b663455c26

View File

@ -1,7 +1,7 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { format, formatDistanceToNow } from 'date-fns';
import { format } from 'date-fns';
import { Bell, Plus, Calendar as CalIcon, ListTodo, RefreshCw } from 'lucide-react';
import api from '@/lib/api';
import type { DashboardData, UpcomingResponse, WeatherData } from '@/types';
@ -44,10 +44,16 @@ export default function DashboardPage() {
const dropdownRef = useRef<HTMLDivElement>(null);
const [clockNow, setClockNow] = useState(() => new Date());
// Live clock — update every minute
// Live clock — synced to the minute boundary
useEffect(() => {
const interval = setInterval(() => setClockNow(new Date()), 60_000);
return () => clearInterval(interval);
let intervalId: ReturnType<typeof setInterval>;
// Wait until the next :00 second mark, then tick every 60s
const msUntilNextMinute = (60 - new Date().getSeconds()) * 1000 - new Date().getMilliseconds();
const timeoutId = setTimeout(() => {
setClockNow(new Date());
intervalId = setInterval(() => setClockNow(new Date()), 60_000);
}, msUntilNextMinute);
return () => { clearTimeout(timeoutId); clearInterval(intervalId); };
}, []);
// Click outside to close dropdown
@ -167,7 +173,12 @@ export default function DashboardPage() {
}
const updatedAgo = dataUpdatedAt
? formatDistanceToNow(new Date(dataUpdatedAt), { addSuffix: true })
? (() => {
const mins = Math.floor((clockNow.getTime() - dataUpdatedAt) / 60_000);
if (mins < 1) return 'just now';
if (mins === 1) return '1 min ago';
return `${mins} min ago`;
})()
: null;
return (