diff --git a/site/astro.config.mjs b/site/astro.config.mjs index b18a482..6c48186 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -9,15 +9,20 @@ const env = loadEnv(process.env.NODE_ENV ?? 'development', process.cwd(), ''); const apiPort = process.env.VITE_API_PORT || '4041'; const serveTarget = env.PUBLIC_EDIT_URL || `http://localhost:${apiPort}`; -// In production, nginx serves activity/index.html for all /activity// URLs -// via try_files. In dev (Astro dev server), no nginx — this plugin replicates it. -const activityFallbackPlugin = { - name: 'activity-shell-fallback', +// In production, nginx serves the shell for dynamic sub-paths via try_files. +// In dev (Astro dev server), no nginx — this plugin replicates those rules. +const shellFallbackPlugin = { + name: 'shell-fallback', configureServer(server) { server.middlewares.use((req, _res, next) => { if (req.url && /^\/activity\/[^/]+\/?(\?|$)/.test(req.url)) { req.url = '/activity/'; } + // /segments/{id}/ → /segments/ (but not /segments/new/ which has its own page) + const segMatch = req.url?.match(/^\/segments\/([^/?]+)\//); + if (segMatch && segMatch[1] !== 'new') { + req.url = '/segments/'; + } next(); }); }, @@ -30,7 +35,7 @@ export default defineConfig({ // When hosting at a subdirectory (e.g. GitHub Pages project site), set: // base: "/repo-name", vite: { - plugins: [activityFallbackPlugin], + plugins: [shellFallbackPlugin], optimizeDeps: { include: ['maplibre-gl'], esbuildOptions: { target: 'es2022' }, diff --git a/site/src/components/AthleteView.svelte b/site/src/components/AthleteView.svelte index dc10fbc..32d7823 100644 --- a/site/src/components/AthleteView.svelte +++ b/site/src/components/AthleteView.svelte @@ -32,6 +32,7 @@ } let segmentSummary: SegmentSummaryItem[] = []; let segmentsLoading = false; + let segmentsFetched = false; let segmentsHandle = ''; let rescanning = false; let rescanMsg: string | null = null; @@ -46,8 +47,9 @@ history.replaceState(null, '', qs ? `?${qs}` : window.location.pathname); } - $: if (activeTab === 'segments' && segmentsHandle && segmentSummary.length === 0 && !segmentsLoading) { + $: if (activeTab === 'segments' && segmentsHandle && !segmentsFetched && !segmentsLoading) { segmentsLoading = true; + segmentsFetched = true; fetch(`/api/users/${segmentsHandle}/segment_summary`) .then(r => r.ok ? r.json() : []) .then(d => { segmentSummary = d; }) @@ -191,8 +193,8 @@ const d = await r.json(); if (r.ok) { rescanMsg = `Found ${d.efforts_found} effort${d.efforts_found !== 1 ? 's' : ''}.`; - // Refresh the summary segmentSummary = []; + segmentsFetched = false; } else rescanMsg = d.detail ?? 'Rescan failed.'; } catch { rescanMsg = 'Could not reach server.'; } rescanning = false; @@ -203,9 +205,9 @@ {#if segmentsLoading} -

Loading segments…

+

Loading…

{:else if segmentSummary.length === 0} -

No segment efforts yet.

+

No segment efforts yet. Use "Rescan all activities" to detect efforts from existing activities.

{:else}