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
+74
View File
@@ -0,0 +1,74 @@
import React, { PropsWithChildren, useEffect, useState } from 'react';
import { useColorScheme } from 'react-native';
import { colors } from './colors';
import { spacing } from './spacing';
import { typography } from './typography';
export type ThemeMode = 'light' | 'dark' | 'system';
export type ThemeContextValue = {
colors: typeof colors;
typography: typeof typography;
spacing: typeof spacing;
isDark: boolean;
};
const fallbackThemeValue: ThemeContextValue = {
colors,
typography,
spacing,
isDark: true,
};
export const ThemeContext = React.createContext<ThemeContextValue>(fallbackThemeValue);
const isValidThemeMode = (value: unknown): value is ThemeMode => {
return value === 'light' || value === 'dark' || value === 'system';
};
const readThemeFromSettings = async (): Promise<ThemeMode> => {
try {
const settingsStore = (await import('../stores/settingsStore')) as Record<string, unknown>;
const stateAccessors = [settingsStore.useSettingsStore, (settingsStore as { useStore?: unknown }).useStore].filter(
(candidate): candidate is { getState: () => { theme?: unknown } } =>
typeof candidate === 'function' &&
typeof (candidate as { getState?: unknown }).getState === 'function',
);
for (const accessor of stateAccessors) {
const state = accessor.getState?.() as { theme?: unknown } | undefined;
const candidateTheme = state?.theme;
if (isValidThemeMode(candidateTheme)) {
return candidateTheme;
}
}
} catch {
// No-op if store is unavailable during bootstrap.
}
return 'system';
};
export const ThemeProvider = ({ children }: PropsWithChildren<object>) => {
const [themeMode, setThemeMode] = useState<ThemeMode>('system');
const systemScheme = useColorScheme() ?? 'light';
useEffect(() => {
void readThemeFromSettings().then(setThemeMode);
}, []);
const isDark = themeMode === 'dark' || (themeMode === 'system' && systemScheme === 'dark');
return (
<ThemeContext.Provider
value={{
colors,
typography,
spacing,
isDark,
}}
>
{children}
</ThemeContext.Provider>
);
};
+22
View File
@@ -0,0 +1,22 @@
export const colors = {
bg: '#0A0E1A',
surface: '#111827',
surfaceAlt: '#1A2233',
accent: '#32B8C6',
accentDim: '#1A6B73',
danger: '#FF4757',
warn: '#FFA502',
success: '#2ED573',
textPrimary: '#E2E8F0',
textSecondary: '#94A3B8',
muted: '#475569',
border: '#1E293B',
connected: '#2ED573',
simulated: '#FFA502',
disconnected: '#FF4757',
signalLow: '#3B82F6',
signalMid: '#10B981',
signalHigh: '#EF4444',
};
export type ColorKey = keyof typeof colors;
+4
View File
@@ -0,0 +1,4 @@
export * from './colors';
export * from './spacing';
export * from './typography';
export * from './ThemeContext';
+10
View File
@@ -0,0 +1,10 @@
export const spacing = {
xs: 4,
sm: 8,
md: 12,
lg: 16,
xl: 20,
xxl: 24,
xxxl: 32,
huge: 48,
};
+26
View File
@@ -0,0 +1,26 @@
import { Platform } from 'react-native';
export const typography = {
displayXl: { fontSize: 48, fontWeight: '700', letterSpacing: -1 },
displayLg: { fontSize: 32, fontWeight: '700', letterSpacing: -0.5 },
displayMd: { fontSize: 24, fontWeight: '600' },
labelLg: {
fontSize: 16,
fontWeight: '600',
letterSpacing: 0.5,
textTransform: 'uppercase',
},
labelMd: {
fontSize: 12,
fontWeight: '600',
letterSpacing: 1,
textTransform: 'uppercase',
},
bodyLg: { fontSize: 16, fontWeight: '400' },
bodyMd: { fontSize: 14, fontWeight: '400' },
bodySm: { fontSize: 12, fontWeight: '400' },
mono: {
fontFamily: Platform.OS === 'ios' ? 'Courier New' : 'monospace',
fontSize: 13,
},
};