feat(mobile): editable activity title for local activities

Adds edits_json column (migration v3) to store user overrides separately
from detail_json so Option A server re-extraction never clobbers them.

- Tap the title in the detail screen to edit (local activities only, shown
  with a ✎ hint). Saves on keyboard dismiss via onEndEditing.
- Cards and search display user_title ?? title.
- Raw upload: user_title sent to server -> sidecar written so web UI shows
  the correct title (server re-extracts from FIT, which has Karoo's title).
- BAS upload: detail.title overridden before sending, no sidecar needed.
This commit is contained in:
Davide Scaini
2026-04-27 15:20:19 +02:00
parent 090d4bd8dc
commit 946da685e5
6 changed files with 81 additions and 18 deletions
+8 -2
View File
@@ -165,8 +165,9 @@ async function uploadLocalActivities(
timeseries_json: string | null;
geojson: string | null;
original_path: string | null;
edits_json: string | null;
}>(
`SELECT id, detail_json, timeseries_json, geojson, original_path
`SELECT id, detail_json, timeseries_json, geojson, original_path, edits_json
FROM activities WHERE origin = 'local' AND synced_at IS NULL`,
);
@@ -189,6 +190,10 @@ async function uploadLocalActivities(
row.original_path !== null &&
(await FileSystem.getInfoAsync(row.original_path)).exists;
const userTitle: string | null = row.edits_json
? (JSON.parse(row.edits_json).title ?? null)
: null;
if (useRaw) {
const filename = row.original_path!.split('/').pop() ?? 'activity.fit';
const base64 = await FileSystem.readAsStringAsync(row.original_path!, {
@@ -197,10 +202,11 @@ async function uploadLocalActivities(
resp = await fetch(`${instanceUrl}/api/upload/raw`, {
method: 'POST',
headers,
body: JSON.stringify({ filename, base64 }),
body: JSON.stringify({ filename, base64, ...(userTitle ? { user_title: userTitle } : {}) }),
});
} else {
const detail = JSON.parse(row.detail_json);
if (userTitle) detail.title = userTitle;
const body: Record<string, unknown> = { activity: { id: row.id, ...detail } };
if (row.timeseries_json) body.timeseries = JSON.parse(row.timeseries_json);
if (row.geojson) body.geojson = JSON.parse(row.geojson);