Files
Timmy-time-dashboard/mobile-app/lib/theme-provider.tsx
Alexander Whitestone 5e60a6453b feat: wire mobile app to real Timmy backend via JSON REST API (#73)
Add /api/chat, /api/upload, and /api/chat/history endpoints to the
FastAPI dashboard so the Expo mobile app talks directly to Timmy's
brain (Ollama) instead of a non-existent Node.js server.

Backend:
- New src/dashboard/routes/chat_api.py with 4 endpoints
- Mount /uploads/ for serving chat attachments
- Same context injection and session management as HTMX chat

Mobile app fixes:
- Point API base URL at port 8000 (FastAPI) instead of 3000
- Create lib/_core/theme.ts (was referenced but never created)
- Fix shared/types.ts (remove broken drizzle/errors re-exports)
- Remove broken server/chat.ts and 1,235-line template README
- Clean package.json (remove express, mysql2, drizzle, tRPC deps)
- Remove debug console.log from theme-provider

Tests: 13 new tests covering all API endpoints (all passing).

https://claude.ai/code/session_01XqErDoh2rVsPY8oTj21Lz2

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-26 23:58:53 -05:00

78 lines
2.5 KiB
TypeScript

import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { Appearance, View, useColorScheme as useSystemColorScheme } from "react-native";
import { colorScheme as nativewindColorScheme, vars } from "nativewind";
import { SchemeColors, type ColorScheme } from "@/constants/theme";
type ThemeContextValue = {
colorScheme: ColorScheme;
setColorScheme: (scheme: ColorScheme) => void;
};
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const systemScheme = useSystemColorScheme() ?? "light";
const [colorScheme, setColorSchemeState] = useState<ColorScheme>(systemScheme);
const applyScheme = useCallback((scheme: ColorScheme) => {
nativewindColorScheme.set(scheme);
Appearance.setColorScheme?.(scheme);
if (typeof document !== "undefined") {
const root = document.documentElement;
root.dataset.theme = scheme;
root.classList.toggle("dark", scheme === "dark");
const palette = SchemeColors[scheme];
Object.entries(palette).forEach(([token, value]) => {
root.style.setProperty(`--color-${token}`, value);
});
}
}, []);
const setColorScheme = useCallback((scheme: ColorScheme) => {
setColorSchemeState(scheme);
applyScheme(scheme);
}, [applyScheme]);
useEffect(() => {
applyScheme(colorScheme);
}, [applyScheme, colorScheme]);
const themeVariables = useMemo(
() =>
vars({
"color-primary": SchemeColors[colorScheme].primary,
"color-background": SchemeColors[colorScheme].background,
"color-surface": SchemeColors[colorScheme].surface,
"color-foreground": SchemeColors[colorScheme].foreground,
"color-muted": SchemeColors[colorScheme].muted,
"color-border": SchemeColors[colorScheme].border,
"color-success": SchemeColors[colorScheme].success,
"color-warning": SchemeColors[colorScheme].warning,
"color-error": SchemeColors[colorScheme].error,
}),
[colorScheme],
);
const value = useMemo(
() => ({
colorScheme,
setColorScheme,
}),
[colorScheme, setColorScheme],
);
return (
<ThemeContext.Provider value={value}>
<View style={[{ flex: 1 }, themeVariables]}>{children}</View>
</ThemeContext.Provider>
);
}
export function useThemeContext(): ThemeContextValue {
const ctx = useContext(ThemeContext);
if (!ctx) {
throw new Error("useThemeContext must be used within ThemeProvider");
}
return ctx;
}