diff --git a/App.tsx b/App.tsx
index 6a713c7..ff33968 100644
--- a/App.tsx
+++ b/App.tsx
@@ -2,17 +2,17 @@ import { useEffect } from 'react';
import { StatusBar } from 'expo-status-bar';
import * as Notifications from 'expo-notifications';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
+import { ThemeProvider } from './src/ThemeContext';
import { AppNavigator } from './src/navigation/AppNavigator';
import { requestNotificationPermissions } from './src/services/gps';
import { promptBatteryOptimizationIfNeeded } from './src/services/batteryOptimization';
-// Show notifications even when the app is in the foreground (iOS suppresses by default)
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
- shouldShowList: true,
- shouldPlaySound: true,
- shouldSetBadge: false,
+ shouldShowList: true,
+ shouldPlaySound: true,
+ shouldSetBadge: false,
}),
});
@@ -24,8 +24,10 @@ export default function App() {
return (
-
-
+
+
+
+
);
}
diff --git a/src/ThemeContext.tsx b/src/ThemeContext.tsx
new file mode 100644
index 0000000..8bf7ea8
--- /dev/null
+++ b/src/ThemeContext.tsx
@@ -0,0 +1,75 @@
+import React, { createContext, useContext, useEffect, useState } from 'react';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { PALETTES, FONT_SCALE, type PaletteKey, type FontSizeKey } from './theme';
+
+interface ThemeValue {
+ accent: string;
+ accentDim: string;
+ palette: PaletteKey;
+ setPalette: (p: PaletteKey) => void;
+ fontSize: FontSizeKey;
+ setFontSize: (s: FontSizeKey) => void;
+ boldLabels: boolean;
+ setBoldLabels: (b: boolean) => void;
+ scale: number;
+}
+
+const ThemeContext = createContext({
+ ...PALETTES.default,
+ palette: 'default',
+ setPalette: () => {},
+ fontSize: 'medium',
+ setFontSize: () => {},
+ boldLabels: false,
+ setBoldLabels: () => {},
+ scale: 1,
+});
+
+export function ThemeProvider({ children }: { children: React.ReactNode }) {
+ const [palette, setPaletteState] = useState('default');
+ const [fontSize, setFontSizeState] = useState('medium');
+ const [boldLabels, setBoldLabelsState] = useState(false);
+
+ useEffect(() => {
+ (async () => {
+ const [p, f, b] = await Promise.all([
+ AsyncStorage.getItem('themePalette'),
+ AsyncStorage.getItem('themeFontSize'),
+ AsyncStorage.getItem('themeBoldLabels'),
+ ]);
+ if (p && p in PALETTES) setPaletteState(p as PaletteKey);
+ if (f && f in FONT_SCALE) setFontSizeState(f as FontSizeKey);
+ if (b !== null) setBoldLabelsState(b === 'true');
+ })();
+ }, []);
+
+ function setPalette(p: PaletteKey) {
+ setPaletteState(p);
+ AsyncStorage.setItem('themePalette', p);
+ }
+ function setFontSize(f: FontSizeKey) {
+ setFontSizeState(f);
+ AsyncStorage.setItem('themeFontSize', f);
+ }
+ function setBoldLabels(b: boolean) {
+ setBoldLabelsState(b);
+ AsyncStorage.setItem('themeBoldLabels', String(b));
+ }
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useTheme() {
+ return useContext(ThemeContext);
+}
diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx
index 8375a48..3aaf05d 100644
--- a/src/navigation/AppNavigator.tsx
+++ b/src/navigation/AppNavigator.tsx
@@ -4,16 +4,17 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Text } from 'react-native';
-import { RecordingScreen } from '../screens/RecordingScreen';
-import { PostRecordingScreen } from '../screens/PostRecordingScreen';
-import { SensorPairingScreen } from '../screens/SensorPairingScreen';
+import { RecordingScreen } from '../screens/RecordingScreen';
+import { PostRecordingScreen } from '../screens/PostRecordingScreen';
+import { SensorPairingScreen } from '../screens/SensorPairingScreen';
import { SavedRecordingsScreen } from '../screens/SavedRecordingsScreen';
-import { SettingsScreen } from '../screens/SettingsScreen';
+import { SettingsScreen } from '../screens/SettingsScreen';
import { RootStackParamList, TabParamList } from '../types';
import { colors } from '../theme';
+import { useTheme } from '../ThemeContext';
const Stack = createNativeStackNavigator();
-const Tab = createBottomTabNavigator();
+const Tab = createBottomTabNavigator();
const TAB_ICONS: Record = {
Recording: '◉',
@@ -22,13 +23,14 @@ const TAB_ICONS: Record = {
};
function Tabs() {
+ const { accent } = useTheme();
return (
({
- headerStyle: { backgroundColor: colors.bg },
- headerTintColor: colors.text,
- tabBarStyle: { backgroundColor: colors.surface, borderTopColor: colors.border },
- tabBarActiveTintColor: colors.accent,
+ headerStyle: { backgroundColor: colors.bg },
+ headerTintColor: colors.text,
+ tabBarStyle: { backgroundColor: colors.surface, borderTopColor: colors.border },
+ tabBarActiveTintColor: accent,
tabBarInactiveTintColor: colors.textMuted,
tabBarIcon: ({ color }) => (
{TAB_ICONS[route.name]}
@@ -36,8 +38,8 @@ function Tabs() {
})}
>
-
-
+
+
);
}
@@ -47,14 +49,14 @@ export function AppNavigator() {
-
+
-
+
);
diff --git a/src/screens/RecordingScreen.tsx b/src/screens/RecordingScreen.tsx
index e207f83..7b2bb1b 100644
--- a/src/screens/RecordingScreen.tsx
+++ b/src/screens/RecordingScreen.tsx
@@ -9,20 +9,15 @@ import { useRecordingStore } from '../store/recording';
import { startGpsRecording, stopGpsRecording, requestLocationPermissions } from '../services/gps';
import { RootStackParamList } from '../types';
import { colors } from '../theme';
+import { useTheme } from '../ThemeContext';
const MAP_STYLE = 'https://tiles.openfreemap.org/styles/liberty';
-const trackLineStyle: LineLayerStyle = {
- lineColor: colors.accent,
- lineWidth: 3,
- lineJoin: 'round',
- lineCap: 'round',
-};
-
type Nav = NativeStackNavigationProp;
export function RecordingScreen() {
const nav = useNavigation