forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAtcUpdateSheet.tsx
More file actions
106 lines (96 loc) · 4.36 KB
/
Copy pathAtcUpdateSheet.tsx
File metadata and controls
106 lines (96 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// The sheet for an ATC trail update, and the reason it is not ClosureSheet.
//
// features/ATC_TRAIL_UPDATES.md §4 is the requirement, and #461 is the issue.
// The rule it enforces is one sentence: **OurHike did not verify this; the
// ATC published it, and those are different statements.** A sheet that said
// "Closed · mi 476.6 – 485.8" and nothing else would be OurHike asserting a
// closure it never checked - which misrepresents the ATC as much as it
// misleads the hiker, and is the failure OurHikeValues.md #4 exists to
// prevent.
//
// Three things here are therefore not optional detail:
//
// **The ATC's name, on the claim.** Not in a map corner, not in a settings
// screen - on the thing being claimed, next to the date they last edited it.
//
// **Both dates, because there are two and they differ.** ATC's own
// `updated_at` is when the notice was last true as far as they know; our
// `reviewedAt` is when a person last checked our copy against their page. A
// notice ATC edited yesterday that nobody here has looked at since May is a
// real state, and the honest thing is to say so rather than to show whichever
// date flatters the app.
//
// **The link, which is load-bearing rather than a nicety.** The artifact
// carries facts and not ATC's prose - their paragraphs are their writing, and
// pipeline/sources.json's `licence` field records why we stop at the facts -
// so this link IS the detail. A hiker who needs to know what the detour
// actually is has nowhere else to go, which is exactly why the scheme is
// checked before it is rendered.
// The formatting lives in lib/atcNoticeText.ts, shared with AtcNoticeList.tsx.
// Two surfaces rendering the same mile marker to different precision is a
// hiker reading two claims where there is one.
import { atcUpdatedAt, isSafeLink, longDate, mileRange } from '../lib/atcNoticeText'
import type { AtcUpdate } from '../lib/atcUpdates'
export interface AtcUpdateSheetProps {
update: AtcUpdate
/** When a person last checked the reviewed file against ATC's page, or null
* if the artifact does not say. */
reviewedAt: Date | null
onClose: () => void
}
export function AtcUpdateSheet({ update, reviewedAt, onClose }: AtcUpdateSheetProps) {
const updatedAt = atcUpdatedAt(update)
const range = mileRange(update)
return (
<div
className="closure-sheet"
role="dialog"
aria-label="Appalachian Trail Conservancy trail update"
>
<div className="legend__head">
{/* ATC's own category, verbatim. Not mapped onto ClosureReason: that
enum would render a Detour as "Closed", which is a claim they did
not make. */}
<h2 className="legend__title">{update.category}</h2>
<button type="button" className="legend__close" onClick={onClose}>
<span className="visually-hidden">Close</span>
<span aria-hidden="true">×</span>
</button>
</div>
{/* Their headline, which is the most specific thing the artifact is
allowed to carry. */}
<p className="closure-sheet__status">{update.title}</p>
<p className="closure-sheet__range">
{update.states.length > 0 ? `${update.states.join(', ')} · ${range}` : range}
</p>
{/* The attribution, and the whole point of a separate component. */}
<p className="closure-sheet__meta">
{updatedAt === null
? 'Appalachian Trail Conservancy'
: `Appalachian Trail Conservancy — updated ${longDate(updatedAt)}`}
</p>
{isSafeLink(update.source_url) && (
<a
className="closure-sheet__link"
href={update.source_url}
target="_blank"
rel="noreferrer"
>
Read the ATC’s notice
</a>
)}
<p className="closure-sheet__limit" role="note">
This is the ATC’s notice, not OurHike’s. OurHike has not checked the
trail itself, and does not work out detours — follow their notice, or the
signage on the ground.
</p>
{/* The second age. Deliberately last and deliberately plain: it is the
one a hiker only needs when something looks wrong. */}
<p className="closure-sheet__age">
{reviewedAt === null
? 'OurHike cannot tell when it last checked ATC’s updates.'
: `OurHike last checked ATC’s updates on ${longDate(reviewedAt)}.`}
</p>
</div>
)
}