diff --git a/frontend/css/styles.css b/frontend/css/styles.css index b3e620a..64a0ca6 100644 --- a/frontend/css/styles.css +++ b/frontend/css/styles.css @@ -945,6 +945,38 @@ body { z-index: 1; } +/* Floating recording bar */ +.rec-floating-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + padding-top: calc(8px + var(--safe-area-top)); + background: rgba(187, 134, 252, 0.15); + border-bottom: 1px solid var(--primary); + cursor: pointer; + order: 0; +} + +.rec-bar-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #f44336; + animation: pulse 1.5s infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} + +#rec-bar-text { + font-size: 13px; + color: var(--primary); + font-weight: 500; +} + /* Recording screen */ .rec-screen { display: flex; diff --git a/frontend/js/gps.js b/frontend/js/gps.js index 0f38988..739319a 100644 --- a/frontend/js/gps.js +++ b/frontend/js/gps.js @@ -4,6 +4,23 @@ const GPS = {}; // --------------------------------------------------------------------------- +// Configurable thresholds (can be changed in settings) +GPS.config = { + minSpeedMs: 0.42, // 1.5 km/h — below this = stationary + minAccuracyM: 20, // skip fixes less accurate than this + minDistanceM: 1, // skip points closer than this +}; + +// Load saved config +try { + const saved = localStorage.getItem("baldav_gps_config"); + if (saved) Object.assign(GPS.config, JSON.parse(saved)); +} catch {} + +GPS.saveConfig = function() { + localStorage.setItem("baldav_gps_config", JSON.stringify(GPS.config)); +}; + // Haversine distance (meters) // --------------------------------------------------------------------------- GPS.distance = function(lat1, lon1, lat2, lon2) { @@ -114,6 +131,197 @@ GPS.recording = { activityType: "bike", }; +// Recording states: waiting_gps → ready → recording (auto on first movement) +// Pause: speed < 1 m/s for 5+ seconds → stop counting distance/time +// Start native GPS service on Android (foreground service survives background) +GPS._startNativeService = async function() { + try { + // Send config along with start command: start:minSpeed:maxAcc:minDist + const cmd = `start:${GPS.config.minSpeedMs}:${GPS.config.minAccuracyM}:${GPS.config.minDistanceM}`; + await window.__TAURI__.core.invoke("gps_service_command", { cmd }); + console.log("[GPS] Native service started with config:", cmd); + GPS.recording.nativeService = true; + } catch (e) { + console.log("[GPS] No native service (desktop?), using WebView GPS"); + GPS.recording.nativeService = false; + } +}; + +GPS._stopNativeService = async function() { + try { + await window.__TAURI__.core.invoke("gps_service_command", { cmd: "stop" }); + } catch {} +}; + +// Read points from native service file +GPS._readNativePoints = async function() { + try { + const raw = await window.__TAURI__.core.invoke("gps_service_read_points"); + if (!raw) return []; + return raw.trim().split("\n").filter(Boolean).map(line => { + const d = JSON.parse(line); + return { + lat: d.lat, lon: d.lon, altitude: d.alt, + speed_ms: d.speed, accuracy: d.acc, + ts: new Date(d.ts).toISOString(), + }; + }); + } catch { return []; } +}; + +// Core point processing — used by both WebView GPS and native service polling +GPS._processIncomingPoint = async function(p) { + if (!GPS.recording.active) return; + + // State: waiting_gps → ready (got first accurate fix) + if (GPS.recording.state === "waiting_gps") { + GPS.recording.state = "ready"; + if (GPS.recording.onStateChange) GPS.recording.onStateChange("ready", p); + } + + // Manual pause — show position but don't process + if (GPS.recording.manualPaused) { + if (GPS.recording.onPoint) GPS.recording.onPoint(p); + return; + } + + // Determine if moving + const speed = p.speed_ms != null ? p.speed_ms : 0; + let isMoving = speed > GPS.config.minSpeedMs; + if (!isMoving && GPS.recording.lastMovingPoint) { + const d = GPS.distance(GPS.recording.lastMovingPoint.lat, GPS.recording.lastMovingPoint.lon, p.lat, p.lon); + const dt = (new Date(p.ts) - new Date(GPS.recording.lastMovingPoint.ts)) / 1000; + if (d > GPS.config.minDistanceM * 3 && dt < 5 && dt > 0) isMoving = true; + } + + // State: ready → recording (first movement detected) + if (GPS.recording.state === "ready" && isMoving) { + GPS.recording.state = "recording"; + GPS.recording.startTime = new Date(); + const now = GPS.recording.startTime; + GPS.recording.sessionId = await DB.createGpsSession( + now.toISOString().slice(0, 10), + now.toTimeString().slice(0, 5), + GPS.recording.activityType + ); + GPS._persistState(); + if (GPS.recording.onStateChange) GPS.recording.onStateChange("recording", p); + } + + // If not yet recording, just update position display + if (GPS.recording.state !== "recording") { + if (GPS.recording.onPoint) GPS.recording.onPoint(p); + return; + } + + // Auto-pause detection + if (!isMoving) { + if (!GPS.recording.pauseStart) { + GPS.recording.pauseStart = new Date(p.ts); + } + if (GPS.recording.onPoint) GPS.recording.onPoint(p); + return; + } + + // Resumed from auto-pause + if (GPS.recording.pauseStart) { + const pauseMs = new Date(p.ts) - GPS.recording.pauseStart; + GPS.recording.totalPauseMs += pauseMs; + GPS.recording.pauseStart = null; + if (GPS.recording.onStateChange) GPS.recording.onStateChange("resumed", p); + } + + // Save moving point + GPS.recording.lastMovingPoint = p; + GPS.recording.points.push(p); + if (GPS.recording.sessionId) { + await DB.addGpsPoint(GPS.recording.sessionId, p.ts, p.lat, p.lon, p.altitude, p.speed_ms); + } + if (GPS.recording.onPoint) GPS.recording.onPoint(p); +}; + +// Persist recording state to localStorage for crash recovery +GPS._persistState = function() { + try { + localStorage.setItem("baldav_gps_active", JSON.stringify({ + sessionId: GPS.recording.sessionId, + activityType: GPS.recording.activityType, + weightKg: GPS.recording.weightKg, + startTime: GPS.recording.startTime?.toISOString(), + totalPauseMs: GPS.recording.totalPauseMs, + manualPaused: GPS.recording.manualPaused, + pauses: GPS.recording.pauses || [], + pointCount: GPS.recording.points.length, + })); + } catch {} +}; + +GPS._clearPersistedState = function() { + localStorage.removeItem("baldav_gps_active"); +}; + +// Manual pause/resume +GPS.pauseRecording = function() { + if (!GPS.recording.active || GPS.recording.manualPaused) return; + GPS.recording.manualPaused = true; + const last = GPS.recording.lastMovingPoint || GPS.recording.points[GPS.recording.points.length - 1]; + GPS.recording._currentPause = { + startTs: Date.now(), + startLat: last?.lat, startLon: last?.lon, + }; + GPS._persistState(); + if (GPS.recording.onStateChange) GPS.recording.onStateChange("manual_paused"); +}; + +GPS.resumeRecording = function() { + if (!GPS.recording.active || !GPS.recording.manualPaused) return; + GPS.recording.manualPaused = false; + const last = GPS.recording.lastMovingPoint || GPS.recording.points[GPS.recording.points.length - 1]; + if (GPS.recording._currentPause) { + GPS.recording._currentPause.endTs = Date.now(); + GPS.recording._currentPause.endLat = last?.lat; + GPS.recording._currentPause.endLon = last?.lon; + const dur = GPS.recording._currentPause.endTs - GPS.recording._currentPause.startTs; + GPS.recording.totalPauseMs += dur; + if (!GPS.recording.pauses) GPS.recording.pauses = []; + GPS.recording.pauses.push(GPS.recording._currentPause); + GPS.recording._currentPause = null; + } + GPS._persistState(); + if (GPS.recording.onStateChange) GPS.recording.onStateChange("manual_resumed"); +}; + +// Recover orphaned recording session on app startup +GPS.recoverSession = async function() { + const saved = localStorage.getItem("baldav_gps_active"); + if (!saved) return; + try { + const s = JSON.parse(saved); + if (!s.sessionId) { GPS._clearPersistedState(); return; } + // Import any remaining native points + const nativePoints = await GPS._readNativePoints(); + if (nativePoints.length > (s.pointCount || 0)) { + for (let i = s.pointCount || 0; i < nativePoints.length; i++) { + const p = nativePoints[i]; + await DB.addGpsPoint(s.sessionId, p.ts, p.lat, p.lon, p.altitude, p.speed_ms); + } + } + // Finalize the session + const pts = await DB.getGpsPoints(s.sessionId); + if (pts.length > 0) { + const stats = GPS.computeStats(pts, s.weightKg || 80); + await DB.finishGpsSession(s.sessionId, new Date().toTimeString().slice(0, 5), + stats.distance, stats.kcal, stats.avgSpeed, stats.maxSpeed, stats.elevGain, stats.elevLoss); + console.log("[GPS] Recovered session", s.sessionId, "with", pts.length, "points"); + } + GPS._clearPersistedState(); + await GPS._stopNativeService(); + } catch (e) { + console.error("[GPS] Recovery error:", e); + GPS._clearPersistedState(); + } +}; + GPS.startRecording = async function(activityType, weightKg) { if (GPS.recording.active) return; if (!navigator.geolocation) throw new Error("Geolocation not available"); @@ -121,34 +329,46 @@ GPS.startRecording = async function(activityType, weightKg) { GPS.recording.active = true; GPS.recording.activityType = activityType || "bike"; GPS.recording.points = []; - GPS.recording.startTime = new Date(); + GPS.recording.startTime = null; GPS.recording.weightKg = weightKg ?? 80; + GPS.recording.state = "waiting_gps"; + GPS.recording.sessionId = null; + GPS.recording.pauseStart = null; + GPS.recording.totalPauseMs = 0; + GPS.recording.lastMovingPoint = null; + GPS.recording.manualPaused = false; + GPS.recording.pauses = []; + GPS.recording._currentPause = null; + GPS.recording.nativeService = false; + GPS.recording._pollTimer = null; + GPS.recording._lastNativeCount = 0; - const now = GPS.recording.startTime; - GPS.recording.sessionId = await DB.createGpsSession( - now.toISOString().slice(0, 10), - now.toTimeString().slice(0, 5), - GPS.recording.activityType - ); + // Start native foreground service (Android) + await GPS._startNativeService(); + + // Poll native service output file + if (GPS.recording.nativeService) { + GPS.recording._pollTimer = setInterval(async () => { + if (!GPS.recording.active) { clearInterval(GPS.recording._pollTimer); return; } + const nativePoints = await GPS._readNativePoints(); + for (let i = GPS.recording._lastNativeCount; i < nativePoints.length; i++) { + await GPS._processIncomingPoint(nativePoints[i]); + } + GPS.recording._lastNativeCount = nativePoints.length; + }, 2000); + } GPS.recording.watchId = navigator.geolocation.watchPosition( async (pos) => { - // Skip inaccurate fixes (>100m accuracy = unreliable) - if (pos.coords.accuracy > 100) return; - - const p = { + if (pos.coords.accuracy > GPS.config.minAccuracyM) return; + await GPS._processIncomingPoint({ lat: pos.coords.latitude, lon: pos.coords.longitude, altitude: pos.coords.altitude, speed_ms: pos.coords.speed, accuracy: pos.coords.accuracy, ts: new Date(pos.timestamp).toISOString(), - }; - GPS.recording.points.push(p); - await DB.addGpsPoint( - GPS.recording.sessionId, p.ts, p.lat, p.lon, p.altitude, p.speed_ms - ); - if (GPS.recording.onPoint) GPS.recording.onPoint(p); + }); }, (err) => { console.error("GPS error:", err.message); @@ -162,11 +382,31 @@ GPS.stopRecording = async function() { if (!GPS.recording.active) return null; GPS.recording.active = false; + if (GPS.recording._pollTimer) clearInterval(GPS.recording._pollTimer); + if (GPS.recording.watchId != null) { navigator.geolocation.clearWatch(GPS.recording.watchId); GPS.recording.watchId = null; } + // Stop native service + if (GPS.recording.nativeService) { + // Import any remaining points from native service + const nativePoints = await GPS._readNativePoints(); + if (nativePoints.length > GPS.recording._lastNativeCount && GPS.recording.sessionId) { + for (let i = GPS.recording._lastNativeCount; i < nativePoints.length; i++) { + const p = nativePoints[i]; + GPS.recording.points.push(p); + await DB.addGpsPoint(GPS.recording.sessionId, p.ts, p.lat, p.lon, p.altitude, p.speed_ms); + } + } + await GPS._stopNativeService(); + } + + if (!GPS.recording.sessionId) { + return null; + } + const stats = GPS.computeStats(GPS.recording.points, GPS.recording.weightKg); const endTime = new Date().toTimeString().slice(0, 5); @@ -177,9 +417,13 @@ GPS.stopRecording = async function() { ); const sid = GPS.recording.sessionId; + const pauses = GPS.recording.pauses || []; GPS.recording.sessionId = null; GPS.recording.points = []; - return { sessionId: sid, stats }; + GPS.recording.pauses = []; + GPS.recording.manualPaused = false; + GPS._clearPersistedState(); + return { sessionId: sid, stats, pauses }; }; // --------------------------------------------------------------------------- diff --git a/frontend/js/main.js b/frontend/js/main.js index 57227d2..54c79e7 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -27,6 +27,9 @@ async function init() { // Wait for DB migrations if (DB._migrationsReady) await DB._migrationsReady; + // Recover any orphaned GPS recording session + await GPS.recoverSession(); + // Import seed products if needed try { app.innerHTML = `
Проверка продуктов...
`; @@ -69,10 +72,33 @@ function renderScreen(name) { today: renderToday, products: renderProducts, gps: renderGpsScreen, + recording: renderRecordingInline, supplements: renderSupplements, settings: renderSettings, }; app.innerHTML = ""; + + // Floating recording bar (visible on all screens except recording) + if (GPS.recording.active && name !== "recording") { + const bar = document.createElement("div"); + bar.className = "rec-floating-bar"; + bar.onclick = () => renderScreen("recording"); + bar.innerHTML = `Запись...`; + app.appendChild(bar); + // Update bar every second + if (window._recBarTimer) clearInterval(window._recBarTimer); + window._recBarTimer = setInterval(() => { + if (!GPS.recording.active) { clearInterval(window._recBarTimer); return; } + const pts = GPS.recording.points; + const stats = GPS.computeStats(pts, GPS.recording.weightKg); + const sec = GPS.recording.startTime ? Math.round((Date.now() - GPS.recording.startTime.getTime() - GPS.recording.totalPauseMs) / 1000) : 0; + const el = document.querySelector("#rec-bar-text"); + if (el) el.textContent = `${GPS.formatDistance(stats.distance)} | ${GPS.formatDuration(sec)}${GPS.recording.manualPaused ? " | Пауза" : ""}`; + }, 1000); + } else if (window._recBarTimer) { + clearInterval(window._recBarTimer); + } + app.appendChild(buildNav(name)); const content = document.createElement("div"); content.className = "screen-content"; @@ -86,7 +112,7 @@ function buildNav(active) { [ { id: "today", label: "Сегодня" }, { id: "products", label: "Продукты" }, - { id: "gps", label: "Трек" }, + { id: GPS.recording.active ? "recording" : "gps", label: GPS.recording.active ? "Запись" : "Трек" }, { id: "supplements", label: "Добавки" }, { id: "settings", label: "Настройки" }, ].forEach(item => { @@ -742,20 +768,15 @@ async function renderGpsScreen(container) { // Recording button container.querySelector("#gps-rec-btn").onclick = async () => { if (GPS.recording.active) { - showRecordingScreen(); + renderScreen("recording"); } else { const type = container.querySelector("#gps-type").value; const wkg = state.latestWeight?.weight_kg ?? 80; await GPS.startRecording(type, wkg); - showRecordingScreen(); + renderScreen("recording"); } }; - if (GPS.recording.active) { - // If already recording, go straight to recording screen - showRecordingScreen(); - } - // Session click → detail view container.querySelectorAll(".gps-session-item").forEach(item => { item.onclick = () => { @@ -814,8 +835,10 @@ async function renderGpsScreen(container) { }; } -function showRecordingScreen() { - showModal(` +function renderRecordingInline(container) { + if (!GPS.recording.active) { renderScreen("gps"); return; } + + container.innerHTML = `
@@ -825,16 +848,11 @@ function showRecordingScreen() {
—Высота
-
GPS...
+
${GPS.recording.state === "waiting_gps" ? "Поиск GPS..." : GPS.recording.state === "ready" ? "Начните движение..." : ""}
+
-
`, - () => {}); - - // Hide modal OK/Cancel buttons - const overlay = document.querySelector(".modal-overlay"); - const btns = overlay.querySelector(".modal-btns"); - if (btns) btns.style.display = "none"; + `; setTimeout(() => { const mapEl = document.querySelector("#rec-map"); @@ -853,15 +871,20 @@ function showRecordingScreen() { const trailLine = L.polyline([], { color: "#bb86fc", weight: 5 }).addTo(map); let posMarker = null; - // Center on user - navigator.geolocation?.getCurrentPosition( - pos => { - map.setView([pos.coords.latitude, pos.coords.longitude], 15); - document.querySelector("#rec-status").textContent = "GPS OK"; - }, - () => { document.querySelector("#rec-status").textContent = "GPS не найден"; }, - { enableHighAccuracy: true, timeout: 10000 } - ); + // State changes + GPS.recording.onStateChange = (state, p) => { + if (state === "ready") { + statusEl.textContent = "GPS OK. Начните движение..."; + statusEl.style.color = "#03dac6"; + map.setView([p.lat, p.lon], 16); + } else if (state === "recording") { + statusEl.textContent = ""; + statusEl.style.color = ""; + } else if (state === "resumed") { + statusEl.textContent = ""; + statusEl.style.color = ""; + } + }; // Live point updates const distEl = document.querySelector("#rec-dist"); @@ -887,17 +910,23 @@ function showRecordingScreen() { const stats = GPS.computeStats(pts, GPS.recording.weightKg); if (distEl) distEl.textContent = GPS.formatDistance(stats.distance); - // Speed: from device speed or computed from distance - let curSpeed = 0; - if (p.speed_ms != null && p.speed_ms >= 0) { - curSpeed = p.speed_ms * 3.6; - } else if (pts.length >= 2) { - // Fallback: compute from last 2 points - const prev = pts[pts.length - 2]; - const d = GPS.distance(prev.lat, prev.lon, p.lat, p.lon); - const dt = (new Date(p.ts) - new Date(prev.ts)) / 1000; - if (dt > 0) curSpeed = (d / dt) * 3.6; + // Show current state + const recState = GPS.recording.state; + if (recState === "waiting_gps") { + statusEl.textContent = "Поиск GPS..."; + statusEl.style.color = "#ffb74d"; + map.setView([p.lat, p.lon], 15); + return; } + if (recState === "ready") { + // Position marker only + if (posMarker) map.removeLayer(posMarker); + posMarker = L.circleMarker([p.lat, p.lon], { radius: 6, color: "#4285f4", fillColor: "#4285f4", fillOpacity: 1, weight: 2 }).addTo(map); + return; + } + + // Speed + const curSpeed = (p.speed_ms != null && p.speed_ms >= 0) ? p.speed_ms * 3.6 : 0; speedBuf.push(curSpeed); if (speedBuf.length > 5) speedBuf.shift(); const sorted = speedBuf.slice().sort((a, b) => a - b); @@ -908,8 +937,8 @@ function showRecordingScreen() { const alt = p.altitude; if (alt != null && elevEl) elevEl.textContent = Math.round(alt) + " м"; - // Pause detection — only if we have enough data and speed is actually low - if (speedBuf.length >= 3 && median < 1) { + // Pause indicator + if (GPS.recording.pauseStart) { statusEl.textContent = "Пауза"; statusEl.style.color = "#ffb74d"; } else { @@ -918,22 +947,36 @@ function showRecordingScreen() { } }; - // Timer + // Timer — excludes pauses, starts only when recording const timer = setInterval(() => { if (!GPS.recording.active) { clearInterval(timer); return; } - const sec = Math.round((Date.now() - GPS.recording.startTime.getTime()) / 1000); + if (!GPS.recording.startTime) return; // not started yet + const totalMs = Date.now() - GPS.recording.startTime.getTime(); + const activeMs = totalMs - GPS.recording.totalPauseMs - + (GPS.recording.pauseStart ? Date.now() - GPS.recording.pauseStart.getTime() : 0); + const sec = Math.max(0, Math.round(activeMs / 1000)); if (timeEl) timeEl.textContent = GPS.formatDuration(sec); }, 1000); + // Pause button + container.querySelector("#rec-pause").onclick = () => { + if (GPS.recording.manualPaused) { + GPS.resumeRecording(); + container.querySelector("#rec-pause").textContent = "Пауза"; + statusEl.textContent = ""; + statusEl.style.color = ""; + } else { + GPS.pauseRecording(); + container.querySelector("#rec-pause").textContent = "Продолжить"; + statusEl.textContent = "Пауза (ручная)"; + statusEl.style.color = "#ffb74d"; + } + }; + // Stop button - document.querySelector("#rec-stop").onclick = async () => { - const result = await GPS.stopRecording(); + container.querySelector("#rec-stop").onclick = async () => { + await GPS.stopRecording(); clearInterval(timer); - // Ask for name - // TODO: custom naming modal - // Close modal - document.querySelector(".modal-overlay")?.remove(); - history.back(); renderScreen("gps"); }; @@ -1640,6 +1683,31 @@ async function renderSettings(container) { +
+
GPS (запись треков)
+
+
+
Мин. скорость (км/ч)
+
Ниже = пауза
+
+ +
+
+
+
Макс. точность (м)
+
Фиксы точнее этого = пропуск
+
+ +
+
+
+
Мин. расстояние (м)
+
Точки ближе = пропуск
+
+ +
+
+
Разрешения
@@ -1673,6 +1741,18 @@ async function renderSettings(container) { offToggle.classList.toggle("on", newVal); }; + // GPS config inputs + const gpsInputs = ["s-min-speed", "s-min-acc", "s-min-dist"]; + for (const id of gpsInputs) { + const el = container.querySelector("#" + id); + if (el) el.onchange = () => { + GPS.config.minSpeedMs = parseFloat(container.querySelector("#s-min-speed").value) / 3.6; + GPS.config.minAccuracyM = parseFloat(container.querySelector("#s-min-acc").value); + GPS.config.minDistanceM = parseFloat(container.querySelector("#s-min-dist").value); + GPS.saveConfig(); + }; + } + // GPS permission check — works on both desktop and mobile const gpsStatus = container.querySelector("#s-gps-status"); const gpsToggle = container.querySelector("#s-gps-toggle"); diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8886a24..2ec9bb5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "baldav" -version = "0.1.28" +version = "0.1.39" edition = "2021" [lib] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 515e097..579d2fc 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -20,6 +20,28 @@ fn get_app_info() -> AppInfo { } } +// GPS service control — writes command file, Kotlin reads and starts/stops service +#[tauri::command] +fn gps_service_command(app: tauri::AppHandle, cmd: String) -> Result { + let data_dir = app.path().app_data_dir().map_err(|e| e.to_string())?; + std::fs::create_dir_all(&data_dir).ok(); + let cmd_path = data_dir.join("gps_command.txt"); + std::fs::write(&cmd_path, &cmd).map_err(|e| e.to_string())?; + Ok(format!("sent:{}", cmd)) +} + +// Read GPS points from service's JSONL file +#[tauri::command] +fn gps_service_read_points(app: tauri::AppHandle) -> Result { + let data_dir = app.path().app_data_dir().map_err(|e| e.to_string())?; + let pts_path = data_dir.join("gps_live_points.jsonl"); + if pts_path.exists() { + std::fs::read_to_string(&pts_path).map_err(|e| e.to_string()) + } else { + Ok(String::new()) + } +} + #[tauri::command] fn import_seed_products(app: tauri::AppHandle) -> Result { // Find seed DB — try multiple locations @@ -356,7 +378,7 @@ pub fn run() { .add_migrations("sqlite:baldav.db", migrations) .build(), ) - .invoke_handler(tauri::generate_handler![get_safe_insets, import_seed_products, get_app_info]) + .invoke_handler(tauri::generate_handler![get_safe_insets, import_seed_products, get_app_info, gps_service_command, gps_service_read_points]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d3f56e0..c45b449 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/nicegui/tauri-v2/refs/heads/main/tooling/cli/schema.json", "productName": "BalDav", - "version": "0.1.28", + "version": "0.1.39", "identifier": "dev.baldav.app", "build": { "frontendDist": "../frontend"