fix: respect time format preference in UI
Add shared time formatting helpers that apply the Appearance time format preference consistently. Update visible time labels across chat, quota usage, scheduled tasks, tunnels, context details, git views, PR metadata, and passkey settings to use the selected 12-hour, 24-hour, or automatic format. Leave date-only and non-UI formatting untouched so unrelated behavior does not change.
This commit is contained in:
@@ -15,8 +15,9 @@ import {
|
||||
type StoredPasskey,
|
||||
} from '@/lib/passkeys';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { useUIStore, type TimeFormatPreference } from '@/stores/useUIStore';
|
||||
|
||||
const formatTimestamp = (timestamp: number | null, neverUsedText: string) => {
|
||||
const formatTimestamp = (timestamp: number | null, neverUsedText: string, timeFormatPreference: TimeFormatPreference) => {
|
||||
if (!timestamp || !Number.isFinite(timestamp)) {
|
||||
return neverUsedText;
|
||||
}
|
||||
@@ -24,11 +25,13 @@ const formatTimestamp = (timestamp: number | null, neverUsedText: string) => {
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
hour12: timeFormatPreference === 'auto' ? undefined : timeFormatPreference === '12h',
|
||||
}).format(timestamp);
|
||||
};
|
||||
|
||||
export const PasskeySettings: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const timeFormatPreference = useUIStore((state) => state.timeFormatPreference);
|
||||
const [supportsPasskeys, setSupportsPasskeys] = React.useState(false);
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
const [isRegistering, setIsRegistering] = React.useState(false);
|
||||
@@ -230,10 +233,10 @@ export const PasskeySettings: React.FC = () => {
|
||||
<span className="typography-meta text-muted-foreground truncate">
|
||||
{passkey.lastUsedAt
|
||||
? t('settings.openchamber.passkeys.item.lastUsed', {
|
||||
time: formatTimestamp(passkey.lastUsedAt, t('settings.openchamber.passkeys.time.neverUsed')),
|
||||
time: formatTimestamp(passkey.lastUsedAt, t('settings.openchamber.passkeys.time.neverUsed'), timeFormatPreference),
|
||||
})
|
||||
: t('settings.openchamber.passkeys.item.added', {
|
||||
time: formatTimestamp(passkey.createdAt, t('settings.openchamber.passkeys.time.neverUsed')),
|
||||
time: formatTimestamp(passkey.createdAt, t('settings.openchamber.passkeys.time.neverUsed'), timeFormatPreference),
|
||||
})}
|
||||
</span>
|
||||
<Button
|
||||
|
||||
@@ -14,6 +14,8 @@ import { useI18n } from '@/lib/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { openExternalUrl } from '@/lib/url';
|
||||
import { getRuntimeApiBaseUrl } from '@/lib/runtime-switch';
|
||||
import { formatTimeForPreference } from '@/lib/timeFormat';
|
||||
import { useUIStore, type TimeFormatPreference } from '@/stores/useUIStore';
|
||||
|
||||
type TunnelState =
|
||||
| 'checking'
|
||||
@@ -209,8 +211,8 @@ const formatRemaining = (remainingMs: number): string => {
|
||||
return `${seconds}s`;
|
||||
};
|
||||
|
||||
const formatAbsoluteTime = (timestamp: number): string => {
|
||||
return new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
const formatAbsoluteTime = (timestamp: number, timeFormatPreference: TimeFormatPreference): string => {
|
||||
return formatTimeForPreference(timestamp, timeFormatPreference, { hour: '2-digit', precision: 'second' });
|
||||
};
|
||||
|
||||
const normalizePresetHostname = (value: string): string => {
|
||||
@@ -267,6 +269,7 @@ const createPresetId = (): string => {
|
||||
|
||||
export const TunnelSettings: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const timeFormatPreference = useUIStore((state) => state.timeFormatPreference);
|
||||
const tUnsafe = React.useCallback((key: string) => t(key as Parameters<typeof t>[0]), [t]);
|
||||
const [state, setState] = React.useState<TunnelState>('checking');
|
||||
const [tunnelInfo, setTunnelInfo] = React.useState<TunnelInfo | null>(null);
|
||||
@@ -1167,7 +1170,7 @@ export const TunnelSettings: React.FC = () => {
|
||||
{modeLabel}
|
||||
</span>
|
||||
<span className="typography-meta text-muted-foreground/80">
|
||||
{t('settings.openchamber.tunnel.session.redeemedAt', { time: formatAbsoluteTime(record.createdAt) })}
|
||||
{t('settings.openchamber.tunnel.session.redeemedAt', { time: formatAbsoluteTime(record.createdAt, timeFormatPreference) })}
|
||||
</span>
|
||||
<span className="typography-meta text-foreground">
|
||||
{record.isActive
|
||||
|
||||
@@ -5,6 +5,7 @@ import { UsageProgressBar } from './UsageProgressBar';
|
||||
import { PaceIndicator } from './PaceIndicator';
|
||||
import { useQuotaStore } from '@/stores/useQuotaStore';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
|
||||
interface UsageCardProps {
|
||||
title: string;
|
||||
@@ -25,10 +26,11 @@ export const UsageCard: React.FC<UsageCardProps> = ({
|
||||
}) => {
|
||||
const displayMode = useQuotaStore((state) => state.displayMode);
|
||||
const showPredValues = useQuotaStore((state) => state.showPredValues);
|
||||
const timeFormatPreference = useUIStore((state) => state.timeFormatPreference);
|
||||
const displayPercent = displayMode === 'remaining' ? window.remainingPercent : window.usedPercent;
|
||||
const barLabel = displayMode === 'remaining' ? 'remaining' : 'used';
|
||||
const percentLabel = formatQuotaValueLabel(window.valueLabel, displayPercent);
|
||||
const resetLabel = formatQuotaResetLabel(window.resetAt, window.resetAfterFormatted ?? window.resetAtFormatted);
|
||||
const resetLabel = formatQuotaResetLabel(window.resetAt, window.resetAfterFormatted ?? window.resetAtFormatted, timeFormatPreference);
|
||||
const windowLabel = formatWindowLabel(title);
|
||||
|
||||
const paceInfo = React.useMemo(() => {
|
||||
|
||||
@@ -16,14 +16,13 @@ import { getAllModelFamilies, getDisplayModelName, sortModelFamilies, groupModel
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { formatTimeForPreference } from '@/lib/timeFormat';
|
||||
import { useUIStore, type TimeFormatPreference } from '@/stores/useUIStore';
|
||||
|
||||
const formatTime = (timestamp: number | null) => {
|
||||
const formatTime = (timestamp: number | null, timeFormatPreference: TimeFormatPreference) => {
|
||||
if (!timestamp) return '-';
|
||||
try {
|
||||
return new Date(timestamp).toLocaleTimeString(undefined, {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit'
|
||||
});
|
||||
return formatTimeForPreference(timestamp, timeFormatPreference, { fallback: '-' });
|
||||
} catch {
|
||||
return '-';
|
||||
}
|
||||
@@ -36,6 +35,7 @@ interface ModelInfo {
|
||||
|
||||
export const UsagePage: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const timeFormatPreference = useUIStore((state) => state.timeFormatPreference);
|
||||
const results = useQuotaStore((state) => state.results);
|
||||
const selectedProviderId = useQuotaStore((state) => state.selectedProviderId);
|
||||
const setSelectedProvider = useQuotaStore((state) => state.setSelectedProvider);
|
||||
@@ -163,7 +163,7 @@ export const UsagePage: React.FC = () => {
|
||||
{isLoading ? (
|
||||
<span className="animate-pulse">{t('settings.usage.page.header.refreshing')}</span>
|
||||
) : (
|
||||
t('settings.usage.page.header.lastUpdated', { time: formatTime(lastUpdated) })
|
||||
t('settings.usage.page.header.lastUpdated', { time: formatTime(lastUpdated, timeFormatPreference) })
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user