Files
bincio-activity/mobile/db/sync.ts
T
Davide Scaini 02726034c7 fix: read activity shards in GET /api/feed; improve sync feedback
_merged/index.json is a shard manifest with activities:[] when the user
has >FEED_PAGE_SIZE activities. The endpoint now collects from all
index-{year}.json shard files before returning.

SyncResult gains a `total` field (activities received from server) so the
feed screen can distinguish "server returned nothing" from "all already
stored locally". Messages: "No activities on instance" / "Up to date (N)"
/ "X of N activities synced".
2026-04-24 15:07:52 +02:00

66 lines
2.0 KiB
TypeScript

import type { SQLiteDatabase } from 'expo-sqlite';
import { getSetting, upsertRemoteActivity } from './queries';
export type SyncResult = { synced: number; total: number; error?: string };
export async function syncFeed(db: SQLiteDatabase): Promise<SyncResult> {
const instanceUrl = (await getSetting(db, 'instance_url'))?.replace(/\/$/, '');
const token = await getSetting(db, 'api_token');
if (!instanceUrl || !token) {
return { synced: 0, total: 0, error: 'No instance configured — add one in Settings.' };
}
let resp: Response;
try {
resp = await fetch(`${instanceUrl}/api/feed`, {
headers: { Authorization: `Bearer ${token}` },
});
} catch {
return { synced: 0, total: 0, error: 'Could not reach instance — check your connection.' };
}
if (resp.status === 401) {
return { synced: 0, total: 0, error: 'Session expired — reconnect in Settings.' };
}
if (!resp.ok) {
return { synced: 0, total: 0, error: `Server error (${resp.status})` };
}
const data: { activities?: RemoteSummary[] } = await resp.json();
const activities = data.activities ?? [];
let synced = 0;
for (const a of activities) {
const detailJson = JSON.stringify({
id: a.id,
title: a.title ?? a.id,
sport: a.sport ?? null,
started_at: a.started_at ?? null,
distance_m: a.distance_m ?? null,
moving_time_s: a.moving_time_s ?? null,
elevation_gain_m: a.elevation_gain_m ?? null,
avg_speed_kmh: a.avg_speed_kmh ?? null,
avg_hr_bpm: a.avg_hr_bpm ?? null,
avg_power_w: a.avg_power_w ?? null,
});
const changed = await upsertRemoteActivity(db, a.id, detailJson);
if (changed) synced++;
}
return { synced, total: activities.length };
}
type RemoteSummary = {
id: string;
title?: string;
sport?: string;
started_at?: string;
distance_m?: number | null;
moving_time_s?: number | null;
elevation_gain_m?: number | null;
avg_speed_kmh?: number | null;
avg_hr_bpm?: number | null;
avg_power_w?: number | null;
};