feat: Implement RSSI service for iOS and Web platforms

- Added IosRssiService to handle synthetic RSSI data for iOS.
- Created WebRssiService to simulate RSSI scanning on the web.
- Defined shared types for WifiNetwork and RssiService in rssi.service.ts.
- Introduced simulation service to generate synthetic sensing data.
- Implemented WebSocket service for real-time data handling with reconnection logic.
- Established Zustand stores for managing application state related to MAT and pose data.
- Developed theme context and utility functions for consistent styling and formatting.
- Added type definitions for various application entities including API responses and sensing data.
- Created utility functions for color mapping and URL validation.
- Configured TypeScript settings for the mobile application.
This commit is contained in:
ruv
2026-03-02 10:30:33 -05:00
parent 02192b0232
commit fdc7142dfa
131 changed files with 24090 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
import { useEffect } from 'react';
import { wsService } from '@/services/ws.service';
import { usePoseStore } from '@/stores/poseStore';
export interface UsePoseStreamResult {
connectionStatus: ReturnType<typeof usePoseStore.getState>['connectionStatus'];
lastFrame: ReturnType<typeof usePoseStore.getState>['lastFrame'];
isSimulated: boolean;
}
export function usePoseStream(): UsePoseStreamResult {
const connectionStatus = usePoseStore((state) => state.connectionStatus);
const lastFrame = usePoseStore((state) => state.lastFrame);
const isSimulated = usePoseStore((state) => state.isSimulated);
useEffect(() => {
const unsubscribe = wsService.subscribe((frame) => {
usePoseStore.getState().handleFrame(frame);
});
return () => {
unsubscribe();
};
}, []);
return { connectionStatus, lastFrame, isSimulated };
}
+31
View File
@@ -0,0 +1,31 @@
import { useEffect, useState } from 'react';
import { rssiService, type WifiNetwork } from '@/services/rssi.service';
import { useSettingsStore } from '@/stores/settingsStore';
export function useRssiScanner(): { networks: WifiNetwork[]; isScanning: boolean } {
const enabled = useSettingsStore((state) => state.rssiScanEnabled);
const [networks, setNetworks] = useState<WifiNetwork[]>([]);
const [isScanning, setIsScanning] = useState(false);
useEffect(() => {
if (!enabled) {
rssiService.stopScanning();
setIsScanning(false);
return;
}
const unsubscribe = rssiService.subscribe((result) => {
setNetworks(result);
});
rssiService.startScanning(2000);
setIsScanning(true);
return () => {
unsubscribe();
rssiService.stopScanning();
setIsScanning(false);
};
}, [enabled]);
return { networks, isScanning };
}
@@ -0,0 +1,52 @@
import { useEffect, useState } from 'react';
import { apiService } from '@/services/api.service';
interface ServerReachability {
reachable: boolean;
latencyMs: number | null;
}
const POLL_MS = 10000;
export function useServerReachability(): ServerReachability {
const [state, setState] = useState<ServerReachability>({
reachable: false,
latencyMs: null,
});
useEffect(() => {
let active = true;
const check = async () => {
const started = Date.now();
try {
await apiService.getStatus();
if (!active) {
return;
}
setState({
reachable: true,
latencyMs: Date.now() - started,
});
} catch {
if (!active) {
return;
}
setState({
reachable: false,
latencyMs: null,
});
}
};
void check();
const timer = setInterval(check, POLL_MS);
return () => {
active = false;
clearInterval(timer);
};
}, []);
return state;
}
+4
View File
@@ -0,0 +1,4 @@
import { useContext } from 'react';
import { ThemeContext, ThemeContextValue } from '../theme/ThemeContext';
export const useTheme = (): ThemeContextValue => useContext(ThemeContext);