feat: add theme system with light/dark mode support

- Add ThemeStore (Zustand) for reactive theme switching
- Add Themes.tsx with THEMES object (defaultLight, defaultDark)
- Add Settings screen with theme switcher and logout button
- Add BaseButton component for reusable themed buttons
- Migrate all components from static currentTheme to useThemeStore()
- Add shadowColor to theme (iOS only, Android uses elevation)
- All text elements now use theme colors (textPrimary, textSecondary, etc.)
- Update tab navigation to include Settings tab
- Move logout from Header to Settings screen
This commit is contained in:
2026-01-24 16:57:33 +01:00
parent 1dbca79edd
commit 43d40b46d7
23 changed files with 450 additions and 236 deletions

View File

@@ -72,24 +72,26 @@ src/
│ ├── login.tsx # Login screen
│ ├── register.tsx # Registration screen
│ ├── (tabs)/ # Tab navigation group
│ │ ├── _layout.tsx # Tab bar configuration
│ │ ├── _layout.tsx # Tab bar configuration (themed)
│ │ ├── chat.tsx # Chat screen (AI conversation)
│ │ ── calendar.tsx # Calendar overview
│ │ ── calendar.tsx # Calendar overview
│ │ └── settings.tsx # Settings screen (theme switcher, logout)
│ ├── event/
│ │ └── [id].tsx # Event detail screen (dynamic route)
│ └── note/
│ └── [id].tsx # Note editor for event (dynamic route)
├── components/
│ ├── BaseBackground.tsx # Common screen wrapper
│ ├── Header.tsx # Header component with logout button
│ ├── AuthButton.tsx # Reusable button for auth screens (with shadow)
│ ├── BaseBackground.tsx # Common screen wrapper (themed)
│ ├── BaseButton.tsx # Reusable button component (themed, supports children)
│ ├── Header.tsx # Header component (themed)
│ ├── AuthButton.tsx # Reusable button for auth screens (themed, with shadow)
│ ├── ChatBubble.tsx # Reusable chat bubble component (used by ChatMessage & TypingIndicator)
│ ├── TypingIndicator.tsx # Animated typing indicator (. .. ...) shown while waiting for AI response
│ ├── EventCardBase.tsx # Shared event card layout with icons (used by EventCard & ProposedEventCard)
│ ├── EventCard.tsx # Calendar event card (uses EventCardBase + edit/delete buttons)
│ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal
│ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal (skeleton)
│ └── ProposedEventCard.tsx # Chat event proposal (uses EventCardBase + confirm/reject buttons)
├── Themes.tsx # Centralized color/theme definitions
├── Themes.tsx # Theme definitions: THEMES object with defaultLight/defaultDark, Theme type
├── logging/
│ ├── index.ts # Re-exports
│ └── logger.ts # react-native-logs config (apiLogger, storeLogger)
@@ -104,10 +106,47 @@ src/
├── AuthStore.ts # user, isAuthenticated, isLoading, login(), logout(), loadStoredUser()
│ # Uses expo-secure-store (native) / localStorage (web)
├── ChatStore.ts # messages[], isWaitingForResponse, addMessage(), addMessages(), updateMessage(), clearMessages(), setWaitingForResponse(), chatMessageToMessageData()
── EventsStore.ts # events[], setEvents(), addEvent(), updateEvent(), deleteEvent()
── EventsStore.ts # events[], setEvents(), addEvent(), updateEvent(), deleteEvent()
└── ThemeStore.ts # theme, setTheme() - reactive theme switching with Zustand
```
**Routing:** Tab-based navigation with Chat and Calendar as main screens. Auth screens (login, register) outside tabs. Dynamic routes for event detail and note editing.
**Routing:** Tab-based navigation with Chat, Calendar, and Settings as main screens. Auth screens (login, register) outside tabs. Dynamic routes for event detail and note editing.
### Theme System
The app supports multiple themes (light/dark) via a reactive Zustand store.
**Theme Structure (`Themes.tsx`):**
```typescript
export type Theme = {
chatBot, primeFg, primeBg, secondaryBg, messageBorderBg, placeholderBg,
calenderBg, confirmButton, rejectButton, disabledButton, buttonText,
textPrimary, textSecondary, textMuted, eventIndicator, borderPrimary, shadowColor
};
export const THEMES = {
defaultLight: { ... },
defaultDark: { ... }
} as const satisfies Record<string, Theme>;
```
**Usage in Components:**
```typescript
import { useThemeStore } from "../stores/ThemeStore";
const MyComponent = () => {
const { theme } = useThemeStore();
return <View style={{ backgroundColor: theme.primeBg }} />;
};
```
**Theme Switching:**
```typescript
const { setTheme } = useThemeStore();
setTheme("defaultDark"); // or "defaultLight"
```
**Note:** `shadowColor` only works on iOS. Android uses `elevation` with system-defined shadow colors.
### Backend Architecture (apps/server)
@@ -376,10 +415,16 @@ NODE_ENV=development # development = pretty logs, production = JSON
- `ApiClient`: Automatically injects X-User-Id header for authenticated requests
- Login screen: Supports email OR userName login
- Register screen: Email validation, checks for existing email/userName
- `AuthButton`: Reusable button component with shadow effect
- `Header`: Contains logout button on all screens
- `AuthButton`: Reusable button component with themed shadow
- `Header`: Themed header component (logout moved to Settings)
- `index.tsx`: Auth redirect - checks stored user on app start
- Tab navigation (Chat, Calendar) implemented with basic UI
- **Theme system fully implemented:**
- `ThemeStore`: Zustand store with theme state and setTheme()
- `Themes.tsx`: THEMES object with defaultLight/defaultDark variants
- All components use `useThemeStore()` for reactive theme colors
- Settings screen with theme switcher (light/dark)
- `BaseButton`: Reusable themed button component
- Tab navigation (Chat, Calendar, Settings) implemented with themed UI
- Calendar screen fully functional:
- Month navigation with grid display and Ionicons (chevron-back/forward)
- MonthSelector dropdown with infinite scroll (dynamically loads months, lazy-loaded when modal opens, cleared on close for memory efficiency)
@@ -404,9 +449,10 @@ NODE_ENV=development # development = pretty logs, production = JSON
- `EventCardBase`: Shared base component with event layout (header, date/time/recurring icons, description) - used by both EventCard and ProposedEventCard
- `EventCard`: Uses EventCardBase + edit/delete buttons for calendar display
- `ProposedEventCard`: Uses EventCardBase + confirm/reject buttons for chat proposals (supports create/update/delete actions)
- `Themes.tsx`: Centralized color definitions including textPrimary, borderPrimary, eventIndicator, secondaryBg
- `Themes.tsx`: Theme definitions with THEMES object (defaultLight, defaultDark) including all color tokens (textPrimary, borderPrimary, eventIndicator, secondaryBg, shadowColor, etc.)
- `EventsStore`: Zustand store with setEvents(), addEvent(), updateEvent(), deleteEvent() - stores ExpandedEvent[]
- `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages(), isWaitingForResponse/setWaitingForResponse() for typing indicator - loads from server on mount and persists across tab switches
- `ThemeStore`: Zustand store with theme/setTheme() for reactive theme switching across all components
- `ChatBubble`: Reusable chat bubble component with Tailwind styling, used by ChatMessage and TypingIndicator
- `TypingIndicator`: Animated typing indicator component showing `. → .. → ...` loop while waiting for AI response
- Event Detail and Note screens exist as skeletons