chahinebrini 594a43cbf9 feat(theme): Dark Theme — global color-system + Wave 1 screens
Theme-switch in Settings (System/Light/Dark) jetzt App-weit wirksam für die
Core-Screens. Wave 2 dokumentiert (siehe unten).

Color-System:
- lib/theme.ts: refactored zu colors.light + colors.dark (gleiche keys)
  Light: bg #fff, surface #fafafa, surfaceElevated #f5f5f5, border #e5e5e5,
         text #0a0a0a, textMuted #737373
  Dark:  bg #000, surface #1c1c1e, surfaceElevated #2c2c2e, border #38383a,
         text #fff, textMuted #8e8e93
  brandOrange unverändert #007AFF (iOS system blue)
  success/error variieren (light: #16a34a/#dc2626, dark: #30d158/#ff453a)
- legacy `colors` export bleibt als Light-Fallback für nicht-migrierte Files
- new `useColors()` hook → liest aktiven scheme aus useThemeStore

stores/theme.ts:
- Appearance.addChangeListener für live System-Theme-Updates (User schaltet
  iOS Dark/Light → App reagiert sofort ohne Reload)

Wave 1 — migrated Files (Core Screens):
- app/_layout.tsx + app/(app)/_layout.tsx + app/(app)/index.tsx (root + home)
- app/settings.tsx (full theme-aware inkl. TrueSheet)
- app/profile/index.tsx (bg + dividers)
- app/devices.tsx (bg, surface, border, icons)
- app/lyra.tsx (chat container, backdrop, bubbles, ThinkingDots, LoadingPulse)
- components/AppHeader (Nativewind classes ersetzt durch theme-aware Styles)
- components/header/HeaderDropdownMenu
- components/profile/* (ProfileHeader, StatsBar, StreakSection, UrgeStatsCard,
  ApprovedDomainsList, DemographicsAccordion)

Wave 2 (TODOs für separate Session):
- app/urge.tsx (~20 hardcoded colors, größter Screen)
- app/room.tsx, app/dm.tsx, app/(app)/chat.tsx, app/(app)/mail.tsx, app/(app)/coach.tsx
- app/games.tsx, app/profile/[userId].tsx
- Nativewind classes in PostCard, ComposeCard, PostCardSkeleton, NotificationsDropdown

StatusBar style dynamisch synchronisiert (light bei dark-mode, dark bei light).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:15:55 +02:00

48 lines
1.3 KiB
TypeScript

import { create } from 'zustand';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Appearance } from 'react-native';
export type ThemeMode = 'system' | 'light' | 'dark';
const STORAGE_KEY = '@rebreak/theme';
function resolveColorScheme(mode: ThemeMode): 'light' | 'dark' {
if (mode === 'system') {
return Appearance.getColorScheme() === 'dark' ? 'dark' : 'light';
}
return mode;
}
type ThemeState = {
mode: ThemeMode;
colorScheme: 'light' | 'dark';
setMode: (mode: ThemeMode) => Promise<void>;
init: () => Promise<void>;
};
export const useThemeStore = create<ThemeState>((set, get) => {
// Listen for OS-level theme changes and update store when mode === 'system'.
Appearance.addChangeListener(() => {
if (get().mode === 'system') {
set({ colorScheme: resolveColorScheme('system') });
}
});
return {
mode: 'system',
colorScheme: 'light',
init: async () => {
const stored = await AsyncStorage.getItem(STORAGE_KEY);
const mode: ThemeMode =
stored === 'light' || stored === 'dark' || stored === 'system' ? stored : 'system';
set({ mode, colorScheme: resolveColorScheme(mode) });
},
setMode: async (mode) => {
await AsyncStorage.setItem(STORAGE_KEY, mode);
set({ mode, colorScheme: resolveColorScheme(mode) });
},
};
});