Better Messaging

This commit is contained in:
dirwin31
2026-08-27 18:25:51 -07:00
parent 386a6f9216
commit 6c1ec6798f
3 changed files with 45 additions and 6 deletions
@@ -12,6 +12,7 @@ import {
groupRoutesForView,
MAX_RENDERED_ROUTES,
normalizeRoute,
routeMetadataErrorMessage,
routeViewRenderKey,
} from "/assets/components/recordings/dashcam_routes_helpers.js"
@@ -738,8 +739,8 @@ async function openOverlay(route) {
try {
const response = await fetch(`/api/routes/${route.name}`)
if (!response.ok) throw new Error(`Route metadata request failed (${response.status})`)
const data = await response.json()
const data = await response.json().catch(() => ({}))
if (!response.ok) throw new Error(routeMetadataErrorMessage(response.status, data?.error))
segments = Array.isArray(data.segment_urls) ? data.segment_urls.filter(url => typeof url === "string") : []
const availableCameras = ["forward", "wide", "driver"].filter(camera => data.available_cameras?.includes(camera))
if (!segments.length) throw new Error("No video segments are stored for this route")
@@ -884,12 +885,15 @@ export function RouteRecordings() {
const view = buildRouteView(state.routes, { preservedOnly: state.showPreservedOnly, searchQuery: state.searchQuery, sortOrder: state.sortOrder })
const groups = groupRoutesForView(view.visible, state.sortOrder)
const renderKey = routeViewRenderKey(view.visible, state.sortOrder, state.viewMode)
const hasActiveSearch = Boolean(state.searchQuery.trim())
return html`
<div class="dashcam-results-summary" aria-live="polite">
<span>${view.matching.length} matching drive${view.matching.length === 1 ? "" : "s"}</span>
${state.loading ? html`<span>Loading ${state.progress} of ${state.total}</span>` : html`<span>${state.routes.length} total local</span>`}
</div>
${state.loading || hasActiveSearch ? html`
<div class="dashcam-results-summary" aria-live="polite">
${hasActiveSearch ? html`<span>${view.matching.length} matching drive${view.matching.length === 1 ? "" : "s"}</span>` : ""}
${state.loading ? html`<span>Loading routes</span>` : ""}
</div>
` : ""}
${state.error ? html`<p class="screen-recordings-message dashcam-error">${state.error}</p>` : ""}
${state.isDeletingAll ? html`<p class="screen-recordings-message">Deleting routes&hellip;</p>` : ""}
${!view.visible.length && state.loading ? html`<div class="dashcam-loading"><span></span><p>Finding local routes&hellip;</p></div>` : ""}
@@ -264,6 +264,14 @@ export function cameraVideoUrl(segmentUrl, camera, quality) {
return quality ? `${url}&quality=${encodeURIComponent(quality)}` : url
}
export function routeMetadataErrorMessage(status, serverError) {
if (status === 404) {
return "This route is no longer available on this device. Its local video segments may have been deleted or moved."
}
const detail = String(serverError || "").trim()
return detail || `Could not load route details (${status}).`
}
// loggerd only writes qcamera.ts alongside the road camera.
export function supportsLowQuality(camera) {
return camera === "forward"
@@ -200,6 +200,17 @@ def test_route_search_input_updates_on_every_keystroke():
assert '@input="${event => { state.searchQuery = event.target.value }}"' in source
def test_route_summary_only_shows_loading_or_active_search_status():
source = COMPONENT_PATH.read_text(encoding="utf-8")
assert "const hasActiveSearch = Boolean(state.searchQuery.trim())" in source
assert '${state.loading || hasActiveSearch ? html`' in source
assert '${hasActiveSearch ? html`<span>${view.matching.length} matching drive' in source
assert '${state.loading ? html`<span>Loading routes</span>` : ""}' in source
assert "Loading ${state.progress} of ${state.total}" not in source
assert "total local" not in source
def test_search_indexes_the_displayed_time_for_every_route():
result = evaluate('''
const routes = [
@@ -368,6 +379,22 @@ def test_camera_video_url_carries_an_optional_quality_tier():
assert result["low"] == "/video/0000006a--9f0a7bdf9c--7?camera=forward&quality=low"
def test_route_metadata_errors_explain_missing_local_segments():
result = evaluate('''
return {
missing: routeMetadataErrorMessage(404, "Route not found"),
backend: routeMetadataErrorMessage(400, "Invalid route name"),
fallback: routeMetadataErrorMessage(503),
}
''')
assert result == {
"missing": "This route is no longer available on this device. Its local video segments may have been deleted or moved.",
"backend": "Invalid route name",
"fallback": "Could not load route details (503).",
}
def test_only_the_road_camera_has_a_preview():
"""loggerd writes qcamera.ts alongside the road camera only."""
assert evaluate('return ["forward", "wide", "driver"].map(supportsLowQuality)') == [True, False, False]