prefer client timezone for site announcements

This commit is contained in:
cita
2026-03-20 14:06:11 +08:00
parent 1af1fe244a
commit f880e3a25f
4 changed files with 28 additions and 2 deletions
+2 -1
View File
@@ -6,6 +6,7 @@ import { clearFocusParams, readFocusAnnouncementId } from './helpers/navigationF
import { import {
formatSiteAnnouncementSeenAt, formatSiteAnnouncementSeenAt,
readClientTimeZone, readClientTimeZone,
resolveSiteAnnouncementTimeZone,
SiteAnnouncementContent, SiteAnnouncementContent,
} from './helpers/siteAnnouncementPresentation.js'; } from './helpers/siteAnnouncementPresentation.js';
import { tr } from '../i18n.js'; import { tr } from '../i18n.js';
@@ -44,7 +45,7 @@ export default function SiteAnnouncements() {
const rowRefs = useRef<Map<number, HTMLDivElement>>(new Map()); const rowRefs = useRef<Map<number, HTMLDivElement>>(new Map());
const highlightTimerRef = useRef<number | null>(null); const highlightTimerRef = useRef<number | null>(null);
const viewerTimeZone = useMemo(() => readClientTimeZone(), []); const viewerTimeZone = useMemo(() => readClientTimeZone(), []);
const displayTimeZone = serverTimeZone || viewerTimeZone; const displayTimeZone = resolveSiteAnnouncementTimeZone(viewerTimeZone, serverTimeZone);
const siteNameById = useMemo(() => { const siteNameById = useMemo(() => {
const map = new Map<number, string>(); const map = new Map<number, string>();
@@ -5,6 +5,7 @@ import {
SiteAnnouncementContent, SiteAnnouncementContent,
formatSiteAnnouncementSeenAt, formatSiteAnnouncementSeenAt,
readClientTimeZone, readClientTimeZone,
resolveSiteAnnouncementTimeZone,
} from './siteAnnouncementPresentation.js'; } from './siteAnnouncementPresentation.js';
type BrowserGlobals = { type BrowserGlobals = {
@@ -85,6 +86,11 @@ describe('siteAnnouncementPresentation helpers', () => {
expect(formatSiteAnnouncementSeenAt('2026-03-20 04:23:27', 'Asia/Shanghai')).toBe('2026/03/20 12:23:27'); expect(formatSiteAnnouncementSeenAt('2026-03-20 04:23:27', 'Asia/Shanghai')).toBe('2026/03/20 12:23:27');
}); });
it('prefers client timezone over server timezone', () => {
expect(resolveSiteAnnouncementTimeZone('Asia/Shanghai', 'UTC')).toBe('Asia/Shanghai');
expect(resolveSiteAnnouncementTimeZone('', 'UTC')).toBe('UTC');
});
it('reads the browser timezone when available', () => { it('reads the browser timezone when available', () => {
expect(readClientTimeZone()).toBeTruthy(); expect(readClientTimeZone()).toBeTruthy();
}); });
@@ -340,6 +340,17 @@ export function readClientTimeZone(): string | undefined {
} }
} }
export function resolveSiteAnnouncementTimeZone(
clientTimeZone?: string | null,
serverTimeZone?: string | null,
): string | undefined {
const client = String(clientTimeZone || '').trim();
if (client) return client;
const server = String(serverTimeZone || '').trim();
return server || undefined;
}
export function formatSiteAnnouncementSeenAt( export function formatSiteAnnouncementSeenAt(
value: string | null | undefined, value: string | null | undefined,
timeZone = readClientTimeZone(), timeZone = readClientTimeZone(),
+9 -1
View File
@@ -21,6 +21,14 @@ vi.mock('../api.js', () => ({
api: apiMock, api: apiMock,
})); }));
vi.mock('./helpers/siteAnnouncementPresentation.js', async () => {
const actual = await vi.importActual<typeof import('./helpers/siteAnnouncementPresentation.js')>('./helpers/siteAnnouncementPresentation.js');
return {
...actual,
readClientTimeZone: () => 'Asia/Shanghai',
};
});
async function flushMicrotasks() { async function flushMicrotasks() {
await act(async () => { await act(async () => {
await Promise.resolve(); await Promise.resolve();
@@ -107,7 +115,7 @@ describe('SiteAnnouncements page', () => {
expect(apiMock.getRuntimeSettings).toHaveBeenCalled(); expect(apiMock.getRuntimeSettings).toHaveBeenCalled();
expect(JSON.stringify(root!.toJSON())).toContain('站点公告'); expect(JSON.stringify(root!.toJSON())).toContain('站点公告');
expect(JSON.stringify(root!.toJSON())).toContain('Maintenance'); expect(JSON.stringify(root!.toJSON())).toContain('Maintenance');
expect(JSON.stringify(root!.toJSON())).toContain('2026/03/20 04:23:27'); expect(JSON.stringify(root!.toJSON())).toContain('2026/03/20 12:23:27');
const highlightedRows = root!.root.findAll((node) => { const highlightedRows = root!.root.findAll((node) => {
const className = typeof node.props.className === 'string' ? node.props.className : ''; const className = typeof node.props.className === 'string' ? node.props.className : '';