diff --git a/app/settings/theme.tsx b/app/settings/theme.tsx
index 79feb75..aa1ab13 100644
--- a/app/settings/theme.tsx
+++ b/app/settings/theme.tsx
@@ -5,7 +5,6 @@ import {
StyleSheet,
ScrollView,
TouchableOpacity,
- Switch,
ActivityIndicator,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
@@ -43,7 +42,7 @@ function ColorSettingRow({ label, color, onPress, colors }: ColorSettingRowProps
export default function ThemeSettingsScreen() {
const router = useRouter();
- const { isDark, toggleTheme, colors, reloadCustomColors } = useTheme();
+ const { isDark, themeMode, setThemeMode, colors, reloadCustomColors } = useTheme();
const { showToast } = useToast();
const { t } = useTranslation();
const [colorPickerVisible, setColorPickerVisible] = useState(false);
@@ -89,12 +88,26 @@ export default function ThemeSettingsScreen() {
-
+
+ {(['auto', 'light', 'dark'] as const).map((mode) => {
+ const selected = themeMode === mode;
+ return (
+ setThemeMode(mode)}
+ activeOpacity={0.7}
+ >
+
+ {t(`screens.settings.themeMode${mode.charAt(0).toUpperCase()}${mode.slice(1)}`)}
+
+
+ );
+ })}
+
@@ -387,6 +400,21 @@ const styles = StyleSheet.create({
fontSize: 12,
marginTop: 2,
},
+ themeModeContainer: {
+ flexDirection: 'row',
+ borderWidth: 1,
+ borderRadius: borderRadius.small,
+ overflow: 'hidden',
+ },
+ themeModeButton: {
+ paddingHorizontal: spacing.sm,
+ paddingVertical: spacing.xs,
+ },
+ themeModeText: {
+ ...typography.caption,
+ fontWeight: '600',
+ textTransform: 'capitalize',
+ },
separator: {
height: 1,
marginHorizontal: spacing.md,
@@ -408,4 +436,3 @@ const styles = StyleSheet.create({
borderWidth: 2,
},
});
-
diff --git a/constants/changelog.ts b/constants/changelog.ts
index 4706030..1302e6d 100644
--- a/constants/changelog.ts
+++ b/constants/changelog.ts
@@ -11,6 +11,14 @@ export interface ChangelogRelease {
}
export const CHANGELOG: ChangelogRelease[] = [
+ {
+ version: '3.2.2',
+ date: '2026-05-29',
+ changes: [
+ 'Added an Auto theme mode option that follows the iOS system light/dark appearance',
+ 'Theme settings now include Auto, Light, and Dark mode choices',
+ ],
+ },
{
version: '3.2.1',
date: '2026-05-29',
diff --git a/context/ThemeContext.tsx b/context/ThemeContext.tsx
index 96e18cb..75c9a00 100644
--- a/context/ThemeContext.tsx
+++ b/context/ThemeContext.tsx
@@ -1,4 +1,5 @@
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
+import { useColorScheme } from 'react-native';
import { storageService } from '@/services/storage';
import { colorThemeManager, ColorTheme } from '@/services/color-theme-manager';
@@ -28,7 +29,9 @@ export interface ThemeColors {
interface ThemeContextType {
isDark: boolean;
- toggleTheme: () => void;
+ themeMode: 'auto' | 'light' | 'dark';
+ setThemeMode: (mode: 'auto' | 'light' | 'dark') => Promise;
+ toggleTheme: () => Promise;
reloadCustomColors: () => Promise;
colors: ThemeColors;
}
@@ -109,9 +112,11 @@ const trueBlackColors = {
const ThemeContext = createContext(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
- const [isDark, setIsDark] = useState(true); // Default to dark theme
+ const systemColorScheme = useColorScheme();
+ const [themeMode, setThemeModeState] = useState<'auto' | 'light' | 'dark'>('dark');
const [isLoading, setIsLoading] = useState(true);
const [customColors, setCustomColors] = useState(null);
+ const isDark = themeMode === 'auto' ? systemColorScheme !== 'light' : themeMode === 'dark';
useEffect(() => {
loadThemePreference();
@@ -128,7 +133,11 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
const preferences = await storageService.getPreferences();
const savedTheme = preferences.theme;
if (savedTheme !== undefined) {
- setIsDark(savedTheme === true || savedTheme === 'dark');
+ if (savedTheme === 'auto' || savedTheme === 'light' || savedTheme === 'dark') {
+ setThemeModeState(savedTheme);
+ } else {
+ setThemeModeState(savedTheme === true ? 'dark' : 'light');
+ }
}
// If no saved preference, default to dark (already set)
} catch (error) {
@@ -147,23 +156,27 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
}
};
- const toggleTheme = async () => {
- const newTheme = !isDark;
- setIsDark(newTheme);
+ const setThemeMode = async (mode: 'auto' | 'light' | 'dark') => {
+ setThemeModeState(mode);
try {
const preferences = await storageService.getPreferences();
await storageService.savePreferences({
...preferences,
- theme: newTheme ? 'dark' : 'light',
+ theme: mode,
});
- // Reload custom colors for new theme
- const custom = await colorThemeManager.getCustomColors(newTheme);
+ const nextIsDark = mode === 'auto' ? systemColorScheme !== 'light' : mode === 'dark';
+ const custom = await colorThemeManager.getCustomColors(nextIsDark);
setCustomColors(custom);
} catch (error) {
// Ignore theme saving errors
}
};
+ const toggleTheme = async () => {
+ const newMode = isDark ? 'light' : 'dark';
+ await setThemeMode(newMode);
+ };
+
const baseColors = isDark ? darkColors : lightColors;
const colors = colorThemeManager.mergeColors(baseColors, customColors) as typeof baseColors;
@@ -172,6 +185,8 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
return (