refactor(fires): read both age formatters' unit from one helper

formatAge and formatAgeLabel repeated the validity guard, the <1h case,
the rounding and the 48-hour switch, which is why the rounding fix had to
land twice. Both now read { unit, count } from a private ageParts, so
they cannot pick different units again. Output is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
quanticsoul4772andClaude Opus 5.5 committed 2026-11-09 05:35:23 -08:00
1 parent a42b38c4b3
commit fc6944951b
2 files changed
+42 -13

No files matched your search

+23 -13
View File
@@ -535,15 +535,26 @@ export function createModel({
return `${isolate(latPart)} ${isolate(lonPart)}`;
}
/**
* Millisecond delta → `{ unit: '<1h' | 'h' | 'd', count }`, or null for
* invalid input. Both age formatters read it, so they pick the same unit.
*/
function ageParts(deltaMs) {
if (!Number.isFinite(deltaMs) || deltaMs < 0) return null;
const hours = deltaMs / 3600000;
if (hours < 1) return { unit: '<1h', count: 0 };
const wholeHours = Math.round(hours);
if (wholeHours < 48) return { unit: 'h', count: wholeHours };
return { unit: 'd', count: Math.round(hours / 24) };
}
/** Millisecond delta → "<1h" / "Xh" / "Xd", or '' for invalid input. */
function formatAge(deltaMs) {
if (!Number.isFinite(deltaMs) || deltaMs < 0) return '';
const hours = deltaMs / 3600000;
if (hours < 1) return '<1h';
const wholeHours = Math.round(hours);
if (wholeHours < 48) return `${wholeHours}h`;
return `${Math.round(hours / 24)}d`;
const age = ageParts(deltaMs);
if (!age) return '';
return age.unit === '<1h' ? '<1h' : `${age.count}${age.unit}`;
}
/** Millisecond delta → "<1m ago" / "Xm ago" / "Xh ago" (fresh-feed readout). */
@@ -574,13 +585,12 @@ export function createModel({
/** formatAge for a card: "<1h", "24h", "3d" in the UI language ("24 ч"). */
function formatAgeLabel(deltaMs) {
if (!Number.isFinite(deltaMs) || deltaMs < 0) return '';
const hours = deltaMs / 3600000;
if (hours < 1) return t('<1h', { context: 'age' });
const wholeHours = Math.round(hours);
if (wholeHours < 48)
return t('{count}h', { context: 'age', count: wholeHours });
return t('{count}d', { context: 'age', count: Math.round(hours / 24) });
const age = ageParts(deltaMs);
if (!age) return '';
if (age.unit === '<1h') return t('<1h', { context: 'age' });
return age.unit === 'h'
? t('{count}h', { context: 'age', count: age.count })
: t('{count}d', { context: 'age', count: age.count });
}
/** Normalized 0..1 confidence → low/nominal/high display bucket. */
+19
View File
@@ -24,3 +24,22 @@ test('a fire age that rounds to 48 hours reads in days, as 48 hours does', () =>
setLocale(previousLocale);
}
});
test('both fire age formatters agree on invalid, sub-hour and multi-day ages', () => {
const previousLocale = getLocale();
const { formatAge, formatAgeLabel } = createModel({
services: { anchors: {} },
});
try {
setLocale('en');
for (const format of [formatAge, formatAgeLabel]) {
assert.equal(format(-1), '');
assert.equal(format(Number.NaN), '');
assert.equal(format(0.5 * HOUR_MS), '<1h');
assert.equal(format(1 * HOUR_MS), '1h');
assert.equal(format(72 * HOUR_MS), '3d');
}
} finally {
setLocale(previousLocale);
}
});