aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-27 22:30:35 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-27 22:30:35 +0200
commit843fd11774531a4fd22456093b112506cbffbc18 (patch)
tree9e0148554bdf53be3896f6038904b89c54257b91 /web
parent4a61f3eb3103aa512bef1b0d82295403bc4bc977 (diff)
Add Strava external link to ride detail
Diffstat (limited to 'web')
-rw-r--r--web/rides.go9
-rw-r--r--web/templates.templ7
-rw-r--r--web/views.go17
3 files changed, 26 insertions, 7 deletions
diff --git a/web/rides.go b/web/rides.go
index f59c0a4..c945801 100644
--- a/web/rides.go
+++ b/web/rides.go
@@ -53,7 +53,7 @@ func (s *Server) handleRide(w http.ResponseWriter, r *http.Request) {
}
if item.FitPath == "" && item.GPXPath == "" {
slog.Info("Loaded ride detail without route", "ride_id", id)
- kcore.RenderPage(r.Context(), RideDetailPage(RideDetailView{RideView: buildRideView(item), Notice: rideDetailNotice(r)}), w)
+ kcore.RenderPage(r.Context(), RideDetailPage(RideDetailView{RideView: buildRideView(item), ExternalURL: externalRideURL(item.ExternalID), Notice: rideDetailNotice(r)}), w)
return
}
// Prefer FIT; fallback to GPX for legacy rides pending backfill.
@@ -71,9 +71,10 @@ func (s *Server) handleRide(w http.ResponseWriter, r *http.Request) {
}
if err != nil {
kcore.RenderPage(r.Context(), RideDetailPage(RideDetailView{
- RideView: buildRideView(item),
- RouteError: "The recorded route is unavailable.",
- Notice: rideDetailNotice(r),
+ RideView: buildRideView(item),
+ ExternalURL: externalRideURL(item.ExternalID),
+ RouteError: "The recorded route is unavailable.",
+ Notice: rideDetailNotice(r),
}), w)
return
}
diff --git a/web/templates.templ b/web/templates.templ
index 10008d2..db60ca9 100644
--- a/web/templates.templ
+++ b/web/templates.templ
@@ -207,7 +207,12 @@ templ RideDetailPage(data RideDetailView) {
templ RideDetailContent(data RideDetailView) {
<div class="toolbar">
<div><h1 class="ride-detail-title">{ data.Ride.Name }</h1><p class="lead">{ data.Ride.Type } ยท { formatRideDate(data.Ride.StartDate) }</p></div>
- <a class="button secondary" href="/">Back to rides</a>
+ <div class="toolbar-actions">
+ if data.ExternalURL != "" {
+ <a class="button secondary" href={ templ.URL(data.ExternalURL) } target="_blank" rel="noopener">View on Strava</a>
+ }
+ <a class="button secondary" href="/">Back to rides</a>
+ </div>
</div>
if data.RouteError != "" {
<div class="error">{ data.RouteError }</div>
diff --git a/web/views.go b/web/views.go
index 9a79906..ddea27e 100644
--- a/web/views.go
+++ b/web/views.go
@@ -75,6 +75,7 @@ type RideDetailView struct {
RouteError string
ActionError string
Notice string
+ ExternalURL string
}
type RideMapPass struct {
@@ -295,8 +296,9 @@ func buildRideDetailView(item ride.StoredRide, parsed ride.Ride, passes []mounta
}
profile := buildRideProfile(parsed, passes, officialClimbs, matchPolicy)
return RideDetailView{
- RideView: buildRideView(item),
- Passes: buildRideMapPasses(passes),
+ RideView: buildRideView(item),
+ Passes: buildRideMapPasses(passes),
+ ExternalURL: externalRideURL(item.ExternalID),
Route: GeoJSONFeatureCollection{
Type: "FeatureCollection",
Features: []GeoJSONFeature{{
@@ -312,6 +314,17 @@ func buildRideDetailView(item ride.StoredRide, parsed ride.Ride, passes []mounta
}
}
+func externalRideURL(externalID string) string {
+ const prefix = "strava:"
+ if len(externalID) > len(prefix) && externalID[:len(prefix)] == prefix {
+ id := externalID[len(prefix):]
+ if id != "" {
+ return "https://www.strava.com/activities/" + id
+ }
+ }
+ return ""
+}
+
func buildRideMapPasses(passes []mountain_pass.MountainPass) []RideMapPass {
mapPasses := make([]RideMapPass, 0, len(passes))
for _, mountainPass := range passes {