Capture location at every step, not just step 1

- Show the location button on all three steps
- Send lat/lon to the server for every photo upload
- Clear gpsCoords after each upload so each step gets a fresh capture
- Store location_1 (step 1) and location_2 (step 2) in the session
- Ride 1 LogEntry uses location_1; Ride 2 LogEntry uses location_2

https://claude.ai/code/session_015myTTMs6yDsAGarATe5ePZ
This commit is contained in:
Claude 2026-03-18 20:56:45 +00:00
parent 8c5450348a
commit 1faaf4f65b
No known key found for this signature in database
2 changed files with 28 additions and 13 deletions

View file

@ -22,7 +22,8 @@ struct Session {
date: Option<String>,
time_1: Option<String>, // start of ride 1
time_2: Option<String>, // end of ride 1 / start of ride 2
location: Option<String>,
location_1: Option<String>, // location at step 1 (start of ride 1)
location_2: Option<String>, // location at step 2 (start of ride 2)
}
type AppState = Arc<Mutex<Session>>;
@ -144,7 +145,7 @@ async fn upload(
s.reading_1 = Some(reading);
s.date = Some(date_str);
s.time_1 = Some(time_str);
s.location = Some(location.clone());
s.location_1 = Some(location.clone());
}
Ok(Json(UploadResponse {
@ -158,11 +159,21 @@ async fn upload(
}
2 => {
let (reading_1, location) = {
// Reverse-geocode before acquiring the lock (async operation)
let location = if let (Some(la), Some(lo)) = (lat, lon) {
geocode::reverse_geocode(la, lo)
.await
.unwrap_or_else(|_| format!("{:.5}, {:.5}", la, lo))
} else {
String::new()
};
let reading_1 = {
let mut s = state.lock().unwrap();
s.reading_2 = Some(reading);
s.time_2 = Some(time_str);
(s.reading_1, s.location.clone())
s.location_2 = Some(location.clone());
s.reading_1
};
let ride1_km = reading_1.map(|r1| reading.saturating_sub(r1));
@ -170,7 +181,7 @@ async fn upload(
Ok(Json(UploadResponse {
step: 2,
reading,
location,
location: Some(location),
ride1_km,
ride2_km: None,
done: false,
@ -179,7 +190,7 @@ async fn upload(
3 => {
// Read everything we need from the session and release the lock
let (r1, r2, date, time_1, time_2, location) = {
let (r1, r2, date, time_1, time_2, location_1, location_2) = {
let s = state.lock().unwrap();
(
s.reading_1,
@ -187,7 +198,8 @@ async fn upload(
s.date.clone(),
s.time_1.clone(),
s.time_2.clone(),
s.location.clone().unwrap_or_default(),
s.location_1.clone().unwrap_or_default(),
s.location_2.clone().unwrap_or_default(),
)
};
@ -209,7 +221,7 @@ async fn upload(
odometer_start: r1,
odometer_end: r2,
trip_km: r2.saturating_sub(r1),
location: location.clone(),
location: location_1,
notes: "Ride 1".into(),
},
LogEntry {
@ -218,7 +230,7 @@ async fn upload(
odometer_start: r2,
odometer_end: r3,
trip_km: r3.saturating_sub(r2),
location: location.clone(),
location: location_2.clone(),
notes: "Ride 2".into(),
},
];
@ -232,7 +244,7 @@ async fn upload(
Ok(Json(UploadResponse {
step: 3,
reading: r3,
location: Some(location),
location: Some(location_2),
ride1_km: Some(r2.saturating_sub(r1)),
ride2_km: Some(r3.saturating_sub(r2)),
done: true,

View file

@ -203,9 +203,9 @@
3: 'End of ride 2',
};
dots(n, n - 1);
const locBlock = n === 1 ? `
const locBlock = `
<button class="btn btn-gray" style="margin-bottom:10px" onclick="getLocation()">📍 Share location (optional)</button>
<div id="loc-status" style="font-size:0.85rem;text-align:center;min-height:1.2em;margin-bottom:6px"></div>` : '';
<div id="loc-status" style="font-size:0.85rem;text-align:center;min-height:1.2em;margin-bottom:6px"></div>`;
render(`
<div class="step-label">Step ${n} of 3</div>
<div class="step-title">${labels[n]}</div>
@ -230,10 +230,13 @@
const form = new FormData();
form.append('image', file);
if (step === 1 && gpsCoords) {
if (gpsCoords) {
form.append('lat', gpsCoords.lat);
form.append('lon', gpsCoords.lon);
}
// Clear so next step starts fresh
gpsCoords = null;
sessionStorage.removeItem('gpsCoords');
try {
const resp = await fetch('/upload?step=' + step, { method: 'POST', body: form });