Files
bincio-activity/mobile/extraction/extractServer.ts
T
Davide Scaini cbe3e0eeaf feat(mobile): Karoo GPU crash fix, server-side extraction, upload fix, feed redesign
- Skip MapLibre on Android <29 (Karoo): SELinux denies kgsl-3d0 access
  from untrusted_app context, crashing the GPU driver on any OpenGL
  surface. Replace with SvgRouteView — equirectangular SVG route trace
  using react-native-svg, no native GL surface needed.
- Add +/- zoom buttons to full-screen MapLibre map on modern devices
  via Camera ref and onRegionDidChange.
- Skip PyodideWebView on Android <29: same GPU driver conflict; set
  _engineUnavailable at module init via API level gate (< 29).
- Add engine_unavailable fast path in PyodideWebView: post message
  immediately if WebAssembly.Global is absent (Chrome <69) instead of
  attempting 30 MB Pyodide download.
- Add server-side extraction fallback (extractServer.ts): when engine
  unavailable, POST raw file as base64 to /api/upload/raw; server runs
  full Python pipeline and returns extracted data.
- Add /api/upload/raw endpoint in server.py.
- Add pre-flight auth check (checkServerAuth) before batch import so
  an expired token errors immediately rather than after N files.
- Fix uploadLocalActivities in sync.ts: was reading original_path as
  JSON (binary FIT file, always threw), silently skipping every upload.
  Now reads detail_json from DB directly.
- Redesign Feed header: replace single Sync button with Upload /
  Download / Refresh. Pull-to-refresh and Refresh button are local-only.
  Auto-refresh on tab focus via useFocusEffect.
- Replace ActivityIndicator with plain Text everywhere (native animation
  also crashes Karoo GPU driver).
- Raise macOS open-file limit in dev_test.py to prevent EMFILE errors
  from Astro file watcher.
- Document all Karoo hardware constraints in docs/mobile-app.md.
2026-04-26 21:00:12 +02:00

64 lines
1.9 KiB
TypeScript

import type { ExtractionResult } from './extractActivity';
export async function checkServerAuth(instanceUrl: string, token: string): Promise<void> {
let resp: Response;
try {
resp = await fetch(`${instanceUrl}/api/feed`, {
headers: { Authorization: `Bearer ${token}` },
});
} catch {
throw new Error('Could not reach Bincio instance — check your connection.');
}
if (resp.status === 401) throw new Error('Session expired — reconnect in Settings.');
if (!resp.ok) throw new Error(`Server error (${resp.status})`);
}
export async function extractFileViaServer(
filename: string,
base64: string,
instanceUrl: string,
token: string,
onStatus: (msg: string) => void = () => {},
): Promise<ExtractionResult> {
onStatus('Uploading to Bincio instance…');
let resp: Response;
try {
resp = await fetch(`${instanceUrl}/api/upload/raw`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ filename, base64 }),
});
} catch {
throw new Error('Could not reach Bincio instance — check your connection.');
}
if (resp.status === 401) throw new Error('Session expired — reconnect in Settings.');
if (resp.status === 422) {
const body = await resp.json().catch(() => ({})) as { detail?: string };
throw new Error(body.detail ?? 'Server could not process this file.');
}
if (!resp.ok) throw new Error(`Server error (${resp.status})`);
onStatus('Processing on server…');
const data = await resp.json() as {
ok: boolean;
id: string;
detail: object;
timeseries: object | null;
geojson: object | null;
source_hash: string;
};
return {
id: data.id,
detail: data.detail,
timeseries: data.timeseries,
geojson: data.geojson,
sourceHash: data.source_hash,
};
}