fix(mobile): clear technical debt — real SHA-256, feed pagination, search

source_hash: BAS JSON import now computes SHA-256 via crypto.subtle.digest
instead of the '${id}-${length}' stub. No extra package — Hermes supports
Web Crypto API natively.

Feed pagination: useActivities(query, limit) accepts a LIMIT parameter.
The feed screen starts at 50, calls loadMore() via FlatList onEndReached
(threshold 0.3) to increment by 50 each time. useActivityCount(query)
drives the hasMore guard so loadMore is a no-op at the end of the list.

Feed search: compact TextInput below the header filters by title via
SQLite json_extract LIKE. Changing the query resets limit to PAGE_SIZE
so stale paginated results don't linger.

Docs: close the three resolved debt items; keep only the accepted
background-polling limitation as a known gap.
This commit is contained in:
Davide Scaini
2026-04-27 11:53:43 +02:00
parent 93247d510f
commit 7a65ed2078
4 changed files with 68 additions and 40 deletions
+20 -4
View File
@@ -29,10 +29,11 @@ export type ActivitySummary = {
// ── Activities ─────────────────────────────────────────────────────────────
export function useActivities(): ActivitySummary[] {
const PAGE_SIZE = 50;
export function useActivities(searchQuery = '', limit = PAGE_SIZE): ActivitySummary[] {
const db = useSQLiteContext();
// Summaries are derived from the stored detail_json at query time.
// JSON extraction via SQLite's json_extract keeps the table schema simple.
const like = `%${searchQuery}%`;
const rows = db.getAllSync<{
id: string;
origin: 'local' | 'remote';
@@ -53,11 +54,26 @@ export function useActivities(): ActivitySummary[] {
json_extract(detail_json, '$.duration_s') AS duration_s,
json_extract(detail_json, '$.elevation_gain_m') AS elevation_gain_m
FROM activities
WHERE (? = '' OR json_extract(detail_json, '$.title') LIKE ?)
ORDER BY json_extract(detail_json, '$.started_at') DESC
`);
LIMIT ?
`, [searchQuery, like, limit]);
return rows;
}
export function useActivityCount(searchQuery = ''): number {
const db = useSQLiteContext();
const like = `%${searchQuery}%`;
const row = db.getFirstSync<{ n: number }>(
`SELECT COUNT(*) as n FROM activities
WHERE (? = '' OR json_extract(detail_json, '$.title') LIKE ?)`,
[searchQuery, like],
);
return row?.n ?? 0;
}
export { PAGE_SIZE };
export function useActivity(id: string): ActivityRow | null {
const db = useSQLiteContext();
return db.getFirstSync<ActivityRow>(