unify single user and multi user behaviour

This commit is contained in:
Davide Scaini
2026-04-09 08:58:35 +02:00
parent 2007f53580
commit 98c42dc443
25 changed files with 678 additions and 232 deletions
+14 -6
View File
@@ -1,9 +1,17 @@
---
import Base from '../../layouts/Base.astro';
import AthleteView from '../../components/AthleteView.svelte';
/**
* Legacy route — redirects to /u/{handle}/athlete/
*/
import { readShardHandles } from '../../lib/manifest';
const base = import.meta.env.BASE_URL;
const shards = readShardHandles();
const handle = shards[0]?.handle ?? null;
---
<Base title="Athlete — BincioActivity">
<h1 class="text-2xl font-bold text-white mb-6">Athlete</h1>
<AthleteView {base} client:load />
</Base>
{handle ? (
<meta http-equiv="refresh" content={`0;url=${base}u/${handle}/athlete/`} />
<script define:vars={{ base, handle }}>
window.location.replace(base + 'u/' + handle + '/athlete/');
</script>
) : (
<p>No data found. Run <code>bincio extract</code> first.</p>
)}
+18 -4
View File
@@ -1,8 +1,22 @@
---
import Base from '../layouts/Base.astro';
import ActivityFeed from '../components/ActivityFeed.svelte';
import { readShardHandles } from '../lib/manifest';
const base = import.meta.env.BASE_URL;
const shards = readShardHandles();
const isSingleUser = shards.length === 1;
const singleHandle = isSingleUser ? shards[0].handle : null;
---
<Base title="BincioActivity — Feed">
<h1 class="text-2xl font-bold text-white mb-6">Activities</h1>
<ActivityFeed client:load />
</Base>
{isSingleUser ? (
<!-- Single-user: redirect / → /u/{handle}/ -->
<meta http-equiv="refresh" content={`0;url=${base}u/${singleHandle}/`} />
<script define:vars={{ base, singleHandle }}>
window.location.replace(base + 'u/' + singleHandle + '/');
</script>
) : (
<Base title="BincioActivity — Feed">
<h1 class="text-2xl font-bold text-white mb-6">Feed</h1>
<ActivityFeed {base} client:only="svelte" />
</Base>
)}
+16 -6
View File
@@ -1,8 +1,18 @@
---
import Base from '../../layouts/Base.astro';
import StatsView from '../../components/StatsView.svelte';
/**
* Legacy route — redirects to /u/{handle}/stats/
* In multi-user mode the user-widget script in the nav handles the correct link.
*/
import { readShardHandles } from '../../lib/manifest';
const base = import.meta.env.BASE_URL;
const shards = readShardHandles();
const handle = shards[0]?.handle ?? null;
---
<Base title="Stats — BincioActivity">
<h1 class="text-2xl font-bold text-white mb-6">Stats</h1>
<StatsView client:load />
</Base>
{handle ? (
<meta http-equiv="refresh" content={`0;url=${base}u/${handle}/stats/`} />
<script define:vars={{ base, handle }}>
window.location.replace(base + 'u/' + handle + '/stats/');
</script>
) : (
<p>No data found. Run <code>bincio extract</code> first.</p>
)}
-48
View File
@@ -1,48 +0,0 @@
---
/**
* Per-user profile page: /u/{handle}/
*
* In multi-user mode, getStaticPaths reads the root index.json shard manifest
* to discover all handles. In single-user mode this page is never generated.
*/
import { readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import Base from '../../layouts/Base.astro';
import ActivityFeed from '../../components/ActivityFeed.svelte';
export async function getStaticPaths() {
try {
const candidates = [
process.env.BINCIO_DATA_DIR,
resolve(process.cwd(), 'public', 'data'),
resolve(process.cwd(), '..', 'bincio_data'),
].filter(Boolean) as string[];
const dataDir = candidates.find(d => {
try { readFileSync(join(d, 'index.json')); return true; } catch { return false; }
});
if (!dataDir) return [];
const root = JSON.parse(readFileSync(join(dataDir, 'index.json'), 'utf-8'));
const shards: Array<{ handle?: string; url: string }> = root.shards ?? [];
const handles = shards.map(s => s.handle).filter(Boolean) as string[];
return handles.map(handle => ({
params: { handle },
props: { handle, indexUrl: `${handle}/index.json` },
}));
} catch {
return [];
}
}
const { handle, indexUrl } = Astro.props as { handle: string; indexUrl: string };
const base = import.meta.env.BASE_URL;
---
<Base title={`@${handle} — BincioActivity`}>
<div class="max-w-5xl mx-auto px-4 pt-6 pb-2">
<h1 class="text-2xl font-bold text-white mb-1">@{handle}</h1>
<p class="text-zinc-500 text-sm">Activities by this user</p>
</div>
<ActivityFeed {base} filterHandle={handle} profileIndexUrl={indexUrl} client:only="svelte" />
</Base>
@@ -0,0 +1,32 @@
---
/**
* Per-user athlete page: /u/{handle}/athlete/
*/
import Base from '../../../../layouts/Base.astro';
import AthleteView from '../../../../components/AthleteView.svelte';
import { readShardHandles } from '../../../../lib/manifest';
export function getStaticPaths() {
return readShardHandles().map(({ handle }) => ({
params: { handle },
props: { handle },
}));
}
const { handle } = Astro.props as { handle: string };
const base = import.meta.env.BASE_URL;
const mergedBase = `${base}data/${handle}/_merged/`;
const indexUrl = `${mergedBase}index.json`;
const athleteUrl = `${mergedBase}athlete.json`;
---
<Base title={`@${handle} Athlete — BincioActivity`}>
<div class="max-w-5xl mx-auto px-4 pt-6 pb-2">
<h1 class="text-2xl font-bold text-white mb-0.5">@{handle}</h1>
<nav class="flex gap-4 mt-1 mb-6">
<a href={`${base}u/${handle}/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Feed</a>
<a href={`${base}u/${handle}/stats/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Stats</a>
<a href={`${base}u/${handle}/athlete/`} class="text-sm text-[--accent]">Athlete</a>
</nav>
</div>
<AthleteView {base} {indexUrl} {athleteUrl} client:only="svelte" />
</Base>
+34
View File
@@ -0,0 +1,34 @@
---
/**
* Per-user profile / feed page: /u/{handle}/
*
* Shows only this user's activities. In multi-user mode, getStaticPaths
* reads the root shard manifest to discover all handles.
*/
import Base from '../../../layouts/Base.astro';
import ActivityFeed from '../../../components/ActivityFeed.svelte';
import { readShardHandles } from '../../../lib/manifest';
export function getStaticPaths() {
return readShardHandles().map(({ handle, url }) => ({
params: { handle },
props: { handle, shardUrl: url },
}));
}
const { handle, shardUrl } = Astro.props as { handle: string; shardUrl: string };
const base = import.meta.env.BASE_URL;
---
<Base title={`@${handle} — BincioActivity`}>
<div class="max-w-5xl mx-auto px-4 pt-6 pb-2 flex items-center gap-4">
<div>
<h1 class="text-2xl font-bold text-white mb-0.5">@{handle}</h1>
<nav class="flex gap-4 mt-1">
<a href={`${base}u/${handle}/`} class="text-sm text-[--accent]">Feed</a>
<a href={`${base}u/${handle}/stats/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Stats</a>
<a href={`${base}u/${handle}/athlete/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Athlete</a>
</nav>
</div>
</div>
<ActivityFeed {base} filterHandle={handle} profileIndexUrl={shardUrl} client:only="svelte" />
</Base>
@@ -0,0 +1,30 @@
---
/**
* Per-user stats page: /u/{handle}/stats/
*/
import Base from '../../../../layouts/Base.astro';
import StatsView from '../../../../components/StatsView.svelte';
import { readShardHandles } from '../../../../lib/manifest';
export function getStaticPaths() {
return readShardHandles().map(({ handle }) => ({
params: { handle },
props: { handle },
}));
}
const { handle } = Astro.props as { handle: string };
const base = import.meta.env.BASE_URL;
const indexUrl = `${base}data/${handle}/_merged/index.json`;
---
<Base title={`@${handle} Stats — BincioActivity`}>
<div class="max-w-5xl mx-auto px-4 pt-6 pb-2">
<h1 class="text-2xl font-bold text-white mb-0.5">@{handle}</h1>
<nav class="flex gap-4 mt-1 mb-6">
<a href={`${base}u/${handle}/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Feed</a>
<a href={`${base}u/${handle}/stats/`} class="text-sm text-[--accent]">Stats</a>
<a href={`${base}u/${handle}/athlete/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Athlete</a>
</nav>
</div>
<StatsView {indexUrl} client:only="svelte" />
</Base>