Files
bincio-activity/mobile/db/index.ts
T
Davide Scaini 2f53fbc359 feat(mobile): batch import + Karoo auto-import from watch folder
- Import tab now accepts multiple files at once (DocumentPicker multiple:true),
  processes them sequentially through Pyodide, and shows a summary with per-file
  errors on completion.

- DB migration v2 adds source_path column (original filesystem path before copy)
  and an index on it, enabling O(1) deduplication for watch-folder imports.

- On Android, if auto_import_path is set, the Import tab scans the directory on
  mount and on AppState 'active' (app foreground), then automatically imports any
  FIT files not yet in the DB. Designed for Karoo: finish a ride, open the app,
  new files import without any manual steps.

- insertActivity now accepts optional source_path; both importBasJson and
  importNativeFile pass it through (null for files picked via DocumentPicker,
  real path for watch-folder files).
2026-04-25 21:25:54 +02:00

39 lines
1.3 KiB
TypeScript

import type { SQLiteDatabase } from 'expo-sqlite';
export async function migrateDb(db: SQLiteDatabase): Promise<void> {
await db.execAsync('PRAGMA journal_mode = WAL;');
await db.execAsync(`
CREATE TABLE IF NOT EXISTS activities (
id TEXT PRIMARY KEY,
source_hash TEXT NOT NULL,
detail_json TEXT NOT NULL,
timeseries_json TEXT,
geojson TEXT,
original_path TEXT,
synced_at INTEGER,
origin TEXT NOT NULL CHECK(origin IN ('local', 'remote')),
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE INDEX IF NOT EXISTS idx_activities_created_at
ON activities(created_at DESC);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
`);
// Migration v2: source_path stores the original filesystem path a file was
// imported from (e.g. /sdcard/Karoo/Rides/ride.fit), used for watch-folder
// deduplication without re-hashing files.
try {
await db.execAsync('ALTER TABLE activities ADD COLUMN source_path TEXT');
await db.execAsync(
'CREATE INDEX IF NOT EXISTS idx_activities_source_path ON activities(source_path)',
);
} catch {
// Column already exists — migration already ran, ignore.
}
}