Add request logging mode, dark mode, Account hub, profile, vehicle CRUD, cards view
- Client: logRequests option + redacted request/response logging (setLogRequests to toggle at runtime); enabled via app extra.debugHttp for on-device 401 debugging - Dark mode (persisted) via ThemeProvider + React Navigation theme - New "Account" tab: Profile (view), Vehicles (full CRUD), Payment methods (view-only), About, dark-mode + request-logging toggles, sign out Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
65e9118806
commit
2f9f0f604a
11 changed files with 707 additions and 8 deletions
102
app/src/theme/ThemeContext.tsx
Normal file
102
app/src/theme/ThemeContext.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import {
|
||||
DarkTheme as NavDark,
|
||||
DefaultTheme as NavLight,
|
||||
type Theme as NavTheme,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
export type ThemeMode = 'light' | 'dark';
|
||||
|
||||
export interface Colors {
|
||||
bg: string;
|
||||
card: string;
|
||||
text: string;
|
||||
subtext: string;
|
||||
border: string;
|
||||
primary: string;
|
||||
danger: string;
|
||||
}
|
||||
|
||||
const light: Colors = {
|
||||
bg: '#ffffff',
|
||||
card: '#f4f4f4',
|
||||
text: '#111111',
|
||||
subtext: '#666666',
|
||||
border: '#e0e0e0',
|
||||
primary: '#1e6f5c',
|
||||
danger: '#c0392b',
|
||||
};
|
||||
|
||||
const dark: Colors = {
|
||||
bg: '#0e1513',
|
||||
card: '#1b2422',
|
||||
text: '#f2f2f2',
|
||||
subtext: '#9aa5a1',
|
||||
border: '#2c3a36',
|
||||
primary: '#3fbf9c',
|
||||
danger: '#ef6a5c',
|
||||
};
|
||||
|
||||
interface ThemeState {
|
||||
mode: ThemeMode;
|
||||
colors: Colors;
|
||||
navTheme: NavTheme;
|
||||
setMode: (m: ThemeMode) => void;
|
||||
toggle: () => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeState | null>(null);
|
||||
const STORAGE_KEY = 'ps_theme_mode';
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [mode, setModeState] = useState<ThemeMode>('light');
|
||||
|
||||
useEffect(() => {
|
||||
AsyncStorage.getItem(STORAGE_KEY).then((v) => {
|
||||
if (v === 'light' || v === 'dark') setModeState(v);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const setMode = (m: ThemeMode) => {
|
||||
setModeState(m);
|
||||
void AsyncStorage.setItem(STORAGE_KEY, m);
|
||||
};
|
||||
|
||||
const value = useMemo<ThemeState>(() => {
|
||||
const colors = mode === 'dark' ? dark : light;
|
||||
const base = mode === 'dark' ? NavDark : NavLight;
|
||||
const navTheme: NavTheme = {
|
||||
...base,
|
||||
colors: {
|
||||
...base.colors,
|
||||
background: colors.bg,
|
||||
card: colors.bg,
|
||||
text: colors.text,
|
||||
border: colors.border,
|
||||
primary: colors.primary,
|
||||
},
|
||||
};
|
||||
return {
|
||||
mode,
|
||||
colors,
|
||||
navTheme,
|
||||
setMode,
|
||||
toggle: () => setMode(mode === 'dark' ? 'light' : 'dark'),
|
||||
};
|
||||
}, [mode]);
|
||||
|
||||
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
||||
}
|
||||
|
||||
export function useTheme(): ThemeState {
|
||||
const ctx = useContext(ThemeContext);
|
||||
if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
|
||||
return ctx;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue