From a0fe9c88ceee2b1e38aac286f868d5ee2432d5fb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 09:46:34 +0000 Subject: [PATCH] sync: show the target athlete's name, not the session's own /sync/{athlete_id} always rendered 'Sync: ', even when athlete_id (now possibly a foreign athlete, see the previous 'allow syncing athletes other than the current session' change) differed from the session's own athlete. The header now uses the display name of the athlete actually being browsed (resolved via local_athlete, which already existed for import-status lookups and now also returns display_name), falling back to the raw Intervals.icu athlete id if that athlete has never logged in locally and so has no local display name to show. --- src/main.rs | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6826f69..d124d0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -249,14 +249,22 @@ async fn sync_athlete( /* * athlete_id is whichever athlete the session's access token is * pointed at, which is not necessarily the session's own athlete - * (see require_authenticated_session). Import status and the default - * "oldest" bound are tracked per local athlete row, so both are - * resolved against athlete_id, not the session. + * (see require_authenticated_session). Import status, the default + * "oldest" bound, and the display name shown on this page are all + * tracked per local athlete row, so they're resolved against + * athlete_id, not the session. */ - let target_athlete_id = local_athlete_id(&state.db, &athlete_id) + let target_athlete = local_athlete(&state.db, &athlete_id) .await .map_err(http_error)?; + let target_athlete_id = target_athlete.as_ref().map(|(id, _)| *id); + + let target_display_name = target_athlete + .as_ref() + .map(|(_, name)| name.clone()) + .unwrap_or_else(|| athlete_id.clone()); + /* * Default: * @@ -446,7 +454,7 @@ input[type="datetime-local"] { html.push_str(&format!( "

Sync: {}

", - html::escape(&session.display_name) + html::escape(&target_display_name) )); html.push_str( @@ -796,9 +804,10 @@ async fn sync_import_visible( let client = IntervalsClient::with_api_key(state.config.clone(), session.access_token.clone()); - let target_athlete_id = local_athlete_id(&state.db, &athlete_id) + let target_athlete_id = local_athlete(&state.db, &athlete_id) .await - .map_err(http_error)?; + .map_err(http_error)? + .map(|(id, _)| id); let activities = client .activities( @@ -898,15 +907,16 @@ async fn activity_exists(db: &PgPool, athlete_id: i64, activity_id: &str) -> Res Ok(row.try_get("exists")?) } -/// Resolves the local database id for an Intervals.icu athlete id, if that -/// athlete has ever logged in locally (via OAuth or an API key). Used when -/// browsing/importing an athlete other than the current session, since -/// import status and "last synced" bookkeeping is tracked per local -/// athlete row, not per session. -async fn local_athlete_id(db: &PgPool, intervals_athlete_id: &str) -> Result> { +/// Resolves the local database id and display name for an Intervals.icu +/// athlete id, if that athlete has ever logged in locally (via OAuth or an +/// API key). Used when browsing/importing an athlete other than the +/// current session, since import status, "last synced" bookkeeping, and +/// the athlete's display name are tracked per local athlete row, not per +/// session. +async fn local_athlete(db: &PgPool, intervals_athlete_id: &str) -> Result> { let row = sqlx::query( r#" - SELECT id + SELECT id, display_name FROM athletes WHERE intervals_athlete_id = $1 "#, @@ -917,7 +927,7 @@ async fn local_athlete_id(db: &PgPool, intervals_athlete_id: &str) -> Result Option> {