mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(dawarich): the stay times follow the clock the traveller set
Every time Dawarich shows was cut straight out of the timestamp and printed, so on a 12-hour setting the app said "2:05 PM" everywhere and "14:05" here. Both ends of a range go through the same formatter the rest of the app uses now, and the argument is required rather than optional so the next caller answers the question instead of inheriting the default. That covers the rows in the panel, on the phone and in the planner, and the summary line a journal day carries, which reads its span through the very same function so the fold and the stays under it cannot disagree about the clock. The accept dialog was already right: it fills a CustomTimePicker, which knows the setting, from the 24-hour value it keeps internally.
This commit is contained in:
@@ -4,6 +4,7 @@ import type { DawarichAccept, DawarichSuggestion, DawarichSuggestionTarget } fro
|
||||
import { useTranslation } from '../../i18n'
|
||||
import { useDawarichSuggestions } from '../../hooks/useDawarichSuggestions'
|
||||
import { useTripStore } from '../../store/tripStore'
|
||||
import { useSettingsStore } from '../../store/settingsStore'
|
||||
import { useIsPhone } from '../../mobile/useIsPhone'
|
||||
import { relativeTime } from '../../utils/relativeTime'
|
||||
import { Tooltip } from '../shared/Tooltip'
|
||||
@@ -309,6 +310,9 @@ export function SuggestionRow({
|
||||
onRestore?: () => void
|
||||
}): React.ReactElement {
|
||||
const { t, locale } = useTranslation()
|
||||
// The traveller's own clock: these times used to come straight out of the timestamp, so
|
||||
// a 12-hour setting got 24-hour times here and nowhere else.
|
||||
const is12h = useSettingsStore(s => s.settings.time_format) === '12h'
|
||||
|
||||
return (
|
||||
<div className="flex items-stretch gap-[6px] px-3 py-[5px]">
|
||||
@@ -355,7 +359,7 @@ export function SuggestionRow({
|
||||
{showDate && (
|
||||
<Badge icon={CalendarDays}>{formatDayHeading(suggestion.localDate, locale)}</Badge>
|
||||
)}
|
||||
<Badge icon={Clock}>{timeRange(suggestion.startedAt, suggestion.endedAt)}</Badge>
|
||||
<Badge icon={Clock}>{timeRange(suggestion.startedAt, suggestion.endedAt, is12h)}</Badge>
|
||||
<Badge icon={Hourglass}>{formatDuration(suggestion.durationMinutes, t)}</Badge>
|
||||
|
||||
{suggestion.state === 'accepted' && (
|
||||
|
||||
@@ -105,12 +105,22 @@ describe('clockOf / timeRange', () => {
|
||||
})
|
||||
|
||||
it('FE-DAWARICH-SUGMODEL-006: a range when both ends read, the arrival alone when the end does not', () => {
|
||||
expect(timeRange('2026-09-10T10:15:00+02:00', '2026-09-10T12:40:00+02:00')).toBe('10:15 – 12:40')
|
||||
expect(timeRange('2026-09-10T10:15:00+02:00', 'nonsense')).toBe('10:15')
|
||||
expect(timeRange('2026-09-10T10:15:00+02:00', '2026-09-10T12:40:00+02:00', false)).toBe('10:15 – 12:40')
|
||||
expect(timeRange('2026-09-10T10:15:00+02:00', 'nonsense', false)).toBe('10:15')
|
||||
})
|
||||
|
||||
it('FE-DAWARICH-SUGMODEL-007: no arrival, no range', () => {
|
||||
expect(timeRange('nonsense', '2026-09-10T12:40:00+02:00')).toBe('')
|
||||
expect(timeRange('nonsense', '2026-09-10T12:40:00+02:00', false)).toBe('')
|
||||
})
|
||||
|
||||
it('FE-DAWARICH-SUGMODEL-012: a twelve-hour clock gets twelve-hour times, both ends of the range', () => {
|
||||
// These were cut out of the timestamp and printed as they stood, so a traveller on a
|
||||
// 12-hour clock read every Dawarich time in 24-hour while the rest of the app obeyed
|
||||
// the setting.
|
||||
expect(timeRange('2026-09-10T10:15:00+02:00', '2026-09-10T12:40:00+02:00', true)).toBe('10:15 AM – 12:40 PM')
|
||||
expect(timeRange('2026-09-10T14:05:00+02:00', '2026-09-10T23:00:00+02:00', true)).toBe('2:05 PM – 11:00 PM')
|
||||
// Midnight is 12 AM, not 0 AM, and noon is 12 PM.
|
||||
expect(timeRange('2026-09-10T00:30:00+02:00', '2026-09-10T12:00:00+02:00', true)).toBe('12:30 AM – 12:00 PM')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { DawarichSuggestion } from '@trek/shared'
|
||||
import { dayColor } from '../Map/dawarichTrail'
|
||||
import { formatClockTime } from '../../utils/formatters'
|
||||
|
||||
/**
|
||||
* Pure shaping for the suggestions panel — no React, so the grouping and the
|
||||
@@ -54,10 +55,15 @@ export function clockOf(iso: string): string {
|
||||
*
|
||||
* An en dash with hair spaces rather than a hyphen: this is a range, and it sits
|
||||
* next to times often enough that the difference is visible.
|
||||
*
|
||||
* `is12h` is not optional on purpose. These clocks were cut straight out of the
|
||||
* timestamp and printed, so a traveller on a 12-hour clock read every Dawarich time in
|
||||
* 24-hour while the rest of the app obeyed the setting. A required argument makes the
|
||||
* next caller answer the question rather than inherit the bug.
|
||||
*/
|
||||
export function timeRange(startedAt: string, endedAt: string): string {
|
||||
const from = clockOf(startedAt)
|
||||
const to = clockOf(endedAt)
|
||||
export function timeRange(startedAt: string, endedAt: string, is12h: boolean): string {
|
||||
const from = formatClockTime(clockOf(startedAt), is12h)
|
||||
const to = formatClockTime(clockOf(endedAt), is12h)
|
||||
if (!from) return ''
|
||||
return to ? `${from} – ${to}` : from
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import type { DawarichSuggestion, DawarichSuggestionTarget } from '@trek/shared'
|
||||
import { useTranslation } from '../../i18n'
|
||||
import DawarichIcon from '../shared/DawarichIcon'
|
||||
import { SuggestionRow } from '../Dawarich/DawarichSuggestionsPanel'
|
||||
import { clockOf } from '../Dawarich/dawarichSuggestionModel'
|
||||
import { timeRange } from '../Dawarich/dawarichSuggestionModel'
|
||||
import { useSettingsStore } from '../../store/settingsStore'
|
||||
|
||||
/**
|
||||
* The stays Dawarich recorded on ONE day of the journal, folded into that day.
|
||||
@@ -36,15 +37,15 @@ export default function JourneyDayDawarich({
|
||||
onDismiss: (suggestion: DawarichSuggestion) => void
|
||||
}): React.ReactElement | null {
|
||||
const { t } = useTranslation()
|
||||
const is12h = useSettingsStore(s => s.settings.time_format) === '12h'
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
if (suggestions.length === 0) return null
|
||||
|
||||
// From the first arrival to the last departure: what part of the day this covers, in
|
||||
// one line, so the row says something even while it is shut.
|
||||
const from = clockOf(suggestions[0]!.startedAt)
|
||||
const to = clockOf(suggestions[suggestions.length - 1]!.endedAt)
|
||||
const span = from && to ? `${from} – ${to}` : from
|
||||
// one line, so the row says something even while it is shut. Through the same formatter
|
||||
// the rows use, so the summary and the stays under it cannot disagree about the clock.
|
||||
const span = timeRange(suggestions[0]!.startedAt, suggestions[suggestions.length - 1]!.endedAt, is12h)
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-edge bg-surface-secondary overflow-hidden">
|
||||
|
||||
Reference in New Issue
Block a user