mirror of
https://github.com/ruvnet/RuView
synced 2026-07-28 18:21:42 +00:00
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:
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './colors';
|
||||
export * from './spacing';
|
||||
export * from './typography';
|
||||
export * from './ThemeContext';
|
||||
@@ -0,0 +1,10 @@
|
||||
export const spacing = {
|
||||
xs: 4,
|
||||
sm: 8,
|
||||
md: 12,
|
||||
lg: 16,
|
||||
xl: 20,
|
||||
xxl: 24,
|
||||
xxxl: 32,
|
||||
huge: 48,
|
||||
};
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user