feat(mobile): search/filter tab — sport, date, sort; hidden on Karoo
Adds a fourth tab visible only on Android API 29+ (full phone, not Karoo). Filters by sport pill, date preset (7d/30d/6mo/year), and sort order (newest/distance/elevation). Paginated FlatList with the same activity cards. ActivityCard extracted to mobile/components/ActivityCard.tsx so both the feed tab and the new search tab share the same component without duplication.
This commit is contained in:
@@ -74,6 +74,48 @@ export function useActivityCount(searchQuery = ''): number {
|
||||
|
||||
export { PAGE_SIZE };
|
||||
|
||||
export type ActivityFilter = {
|
||||
sport: string; // '' = all sports
|
||||
dateFrom: string; // '' = no lower bound; ISO-like 'YYYY-MM-DDTHHMMSSZ' for comparison
|
||||
sort: 'date' | 'distance' | 'elevation';
|
||||
};
|
||||
|
||||
const SORT_SQL: Record<string, string> = {
|
||||
date: "json_extract(detail_json, '$.started_at') DESC",
|
||||
distance: "json_extract(detail_json, '$.distance_m') DESC",
|
||||
elevation: "json_extract(detail_json, '$.elevation_gain_m') DESC",
|
||||
};
|
||||
|
||||
export function useFilteredActivities(filter: ActivityFilter, limit = PAGE_SIZE): ActivitySummary[] {
|
||||
const db = useSQLiteContext();
|
||||
const order = SORT_SQL[filter.sort] ?? SORT_SQL.date;
|
||||
return db.getAllSync<ActivitySummary>(`
|
||||
SELECT
|
||||
id, origin, synced_at,
|
||||
json_extract(detail_json, '$.title') AS title,
|
||||
json_extract(detail_json, '$.sport') AS sport,
|
||||
json_extract(detail_json, '$.started_at') AS started_at,
|
||||
json_extract(detail_json, '$.distance_m') AS distance_m,
|
||||
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, '$.sport') = ?)
|
||||
AND (? = '' OR json_extract(detail_json, '$.started_at') >= ?)
|
||||
ORDER BY ${order}
|
||||
LIMIT ?
|
||||
`, [filter.sport, filter.sport, filter.dateFrom, filter.dateFrom, limit]);
|
||||
}
|
||||
|
||||
export function useFilteredCount(filter: ActivityFilter): number {
|
||||
const db = useSQLiteContext();
|
||||
const row = db.getFirstSync<{ n: number }>(`
|
||||
SELECT COUNT(*) as n FROM activities
|
||||
WHERE (? = '' OR json_extract(detail_json, '$.sport') = ?)
|
||||
AND (? = '' OR json_extract(detail_json, '$.started_at') >= ?)
|
||||
`, [filter.sport, filter.sport, filter.dateFrom, filter.dateFrom]);
|
||||
return row?.n ?? 0;
|
||||
}
|
||||
|
||||
export function useActivity(id: string): ActivityRow | null {
|
||||
const db = useSQLiteContext();
|
||||
return db.getFirstSync<ActivityRow>(
|
||||
|
||||
Reference in New Issue
Block a user