From 5398f94f49777fee8f5c12749f42b33e48810244 Mon Sep 17 00:00:00 2001 From: devthejo Date: Sun, 4 May 2025 12:39:03 +0200 Subject: [PATCH] chore: add location debug helpers --- src/location/emulatorService.js | 110 ++++++++++++++++++++++++++++++++ src/location/trackLocation.js | 4 ++ src/scenes/Developer/index.js | 87 ++++++++++++++++++++++++- 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/location/emulatorService.js diff --git a/src/location/emulatorService.js b/src/location/emulatorService.js new file mode 100644 index 0000000..deea548 --- /dev/null +++ b/src/location/emulatorService.js @@ -0,0 +1,110 @@ +import BackgroundGeolocation from "react-native-background-geolocation"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { createLogger } from "~/lib/logger"; +import { BACKGROUND_SCOPES } from "~/lib/logger/scopes"; + +const EMULATOR_MODE_KEY = "emulator_mode_enabled"; + +// Global variables +let emulatorIntervalId = null; +let isEmulatorModeEnabled = false; + +// Create a logger for the emulator service +const emulatorLogger = createLogger({ + module: BACKGROUND_SCOPES.GEOLOCATION, + feature: "emulator", +}); + +// Initialize emulator mode based on stored preference +export const initEmulatorMode = async () => { + try { + const storedValue = await AsyncStorage.getItem(EMULATOR_MODE_KEY); + emulatorLogger.debug("Initializing emulator mode", { storedValue }); + + if (storedValue === "true") { + await enableEmulatorMode(); + } + } catch (error) { + emulatorLogger.error("Failed to initialize emulator mode", { + error: error.message, + stack: error.stack, + }); + } +}; + +// Enable emulator mode +export const enableEmulatorMode = async () => { + emulatorLogger.info("Enabling emulator mode"); + + // Clear existing interval if any + if (emulatorIntervalId) { + clearInterval(emulatorIntervalId); + } + + try { + // Call immediately once + await BackgroundGeolocation.changePace(true); + emulatorLogger.debug("Initial changePace call successful"); + + // Then set up interval + emulatorIntervalId = setInterval( + () => { + BackgroundGeolocation.changePace(true); + emulatorLogger.debug("Interval changePace call executed"); + }, + 30 * 60 * 1000, + ); // 30 minutes + + isEmulatorModeEnabled = true; + + // Persist the setting + await AsyncStorage.setItem(EMULATOR_MODE_KEY, "true"); + emulatorLogger.debug("Emulator mode setting saved"); + } catch (error) { + emulatorLogger.error("Failed to enable emulator mode", { + error: error.message, + stack: error.stack, + }); + } +}; + +// Disable emulator mode +export const disableEmulatorMode = async () => { + emulatorLogger.info("Disabling emulator mode"); + + if (emulatorIntervalId) { + clearInterval(emulatorIntervalId); + emulatorIntervalId = null; + } + + isEmulatorModeEnabled = false; + + // Persist the setting + try { + await AsyncStorage.setItem(EMULATOR_MODE_KEY, "false"); + emulatorLogger.debug("Emulator mode setting saved"); + } catch (error) { + emulatorLogger.error("Failed to save emulator mode setting", { + error: error.message, + stack: error.stack, + }); + } +}; + +// Get current emulator mode state +export const getEmulatorModeState = () => { + return isEmulatorModeEnabled; +}; + +// Toggle emulator mode +export const toggleEmulatorMode = async (enabled) => { + emulatorLogger.info("Toggling emulator mode", { enabled }); + + if (enabled) { + await enableEmulatorMode(); + } else { + await disableEmulatorMode(); + } + + return isEmulatorModeEnabled; +}; diff --git a/src/location/trackLocation.js b/src/location/trackLocation.js index 314b079..00d76f3 100644 --- a/src/location/trackLocation.js +++ b/src/location/trackLocation.js @@ -3,6 +3,7 @@ import { TRACK_MOVE } from "~/misc/devicePrefs"; import { createLogger } from "~/lib/logger"; import { BACKGROUND_SCOPES } from "~/lib/logger/scopes"; import jwtDecode from "jwt-decode"; +import { initEmulatorMode } from "./emulatorService"; import { getAuthState, @@ -271,4 +272,7 @@ export default async function trackLocation() { // Check for pending records after a short delay to ensure everything is initialized setTimeout(checkPendingRecords, 5000); + + // Initialize emulator mode if previously enabled + initEmulatorMode(); } diff --git a/src/scenes/Developer/index.js b/src/scenes/Developer/index.js index 8499c95..1737f87 100644 --- a/src/scenes/Developer/index.js +++ b/src/scenes/Developer/index.js @@ -1,6 +1,7 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { View, StyleSheet, ScrollView } from "react-native"; import * as Sentry from "@sentry/react-native"; +import BackgroundGeolocation from "react-native-background-geolocation"; import { Button, Card, @@ -12,6 +13,11 @@ import { import { createStyles } from "~/theme"; import env, { setStaging } from "~/env"; import { authActions } from "~/stores"; +import { + getEmulatorModeState, + toggleEmulatorMode as toggleEmulatorModeService, + initEmulatorMode, +} from "~/location/emulatorService"; const reset = async () => { await authActions.logout(); @@ -29,7 +35,50 @@ const Section = ({ title, children }) => { export default function Developer() { const styles = useStyles(); + const { colors } = useTheme(); const [isStaging, setIsStaging] = useState(env.IS_STAGING); + const [emulatorMode, setEmulatorMode] = useState(false); + const [syncStatus, setSyncStatus] = useState(null); // null, 'syncing', 'success', 'error' + const [syncResult, setSyncResult] = useState(""); + + // Initialize emulator mode when component mounts + useEffect(() => { + // Initialize the emulator service + initEmulatorMode(); + + // Set the initial state based on the global service + setEmulatorMode(getEmulatorModeState()); + }, []); + + // Handle toggling emulator mode + const handleEmulatorModeToggle = async (enabled) => { + const newState = await toggleEmulatorModeService(enabled); + setEmulatorMode(newState); + }; + + // Function to trigger geolocation sync + const triggerGeolocSync = async () => { + try { + setSyncStatus("syncing"); + setSyncResult(""); + + // Get the count of pending records first + const count = await BackgroundGeolocation.getCount(); + + // Perform the sync + const records = await BackgroundGeolocation.sync(); + + const result = `Synced ${ + records?.length || 0 + } records (${count} pending)`; + setSyncResult(result); + setSyncStatus("success"); + } catch (error) { + console.error("Geolocation sync failed:", error); + setSyncResult(`Sync failed: ${error.message}`); + setSyncStatus("error"); + } + }; const triggerNullError = () => { try { // Wrap the null error in try-catch @@ -101,6 +150,13 @@ export default function Developer() { }} /> + + Emulator Mode + +
@@ -150,6 +206,35 @@ export default function Developer() { +
+ + + {syncStatus && syncResult && ( + + {syncResult} + + )} +
+ + +