From b8fbcfef69ff47f8ff9eb40cea206ef8df356c0e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:55:30 +0000 Subject: [PATCH 1/3] Initial plan From 2fa1ab002f51d6202ea7938d0b8cc42c6350eeb7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:03:55 +0000 Subject: [PATCH 2/3] feat: Add light and dark theme support Co-authored-by: pushpender-singh-ap <73298854+pushpender-singh-ap@users.noreply.github.com> --- App.js | 17 ++-- src/Navigation/BottomNavigation.js | 8 +- src/Redux/Reducers/index.js | 6 ++ src/Screens/Settings/index.js | 125 +++++++++++++++++++++++++++-- src/Theme/ThemeContext.js | 76 ++++++++++++++++++ src/Theme/colors.js | 120 +++++++++++++++++++++++++++ src/Theme/index.js | 1 + 7 files changed, 337 insertions(+), 16 deletions(-) create mode 100644 src/Theme/ThemeContext.js create mode 100644 src/Theme/colors.js create mode 100644 src/Theme/index.js diff --git a/App.js b/App.js index dc67a6a..f2d598a 100644 --- a/App.js +++ b/App.js @@ -13,6 +13,7 @@ import {CONFIGCAT_SDK_KEY_TEST, CONFIGCAT_SDK_KEY_PROD} from '@env'; import {BannerProvider} from './src/Components/UIComp/AnimeAdBanner/BannerContext'; import crashlytics from '@react-native-firebase/crashlytics'; import analytics from '@react-native-firebase/analytics'; +import {ThemeProvider} from './src/Theme'; /** * The main App component that sets up the root of the application. @@ -54,13 +55,15 @@ const App = () => { sdkKey={__DEV__ ? CONFIGCAT_SDK_KEY_TEST : CONFIGCAT_SDK_KEY_PROD}> } persistor={persistor}> - - - - - - - + + + + + + + + + diff --git a/src/Navigation/BottomNavigation.js b/src/Navigation/BottomNavigation.js index 71a3122..d3427bc 100644 --- a/src/Navigation/BottomNavigation.js +++ b/src/Navigation/BottomNavigation.js @@ -14,6 +14,7 @@ import {View, StyleSheet} from 'react-native'; import {useFeatureFlag} from 'configcat-react'; import LinkListScreen from '../InkNest-Externals/Screens/Webview/LinkListScreen'; import FloatingDonationButton from '../InkNest-Externals/Donation/FloatingDonationButton'; +import {useTheme} from '../Theme'; const BottomTab = createBottomTabNavigator(); @@ -78,6 +79,7 @@ const TabBarIcon = props => { */ export function BottomNavigation() { const {value: forIosValue} = useFeatureFlag('forIos', 'Default'); + const {colors} = useTheme(); return ( <> @@ -88,14 +90,14 @@ export function BottomNavigation() { style={[ StyleSheet.absoluteFill, { - backgroundColor: '#110918', + backgroundColor: colors.tabBar, }, ]} /> ), headerShown: false, - tabBarActiveTintColor: '#D2D2D6', - tabBarInactiveTintColor: '#6B666D', + tabBarActiveTintColor: colors.tabBarActive, + tabBarInactiveTintColor: colors.tabBarInactive, tabBarStyle: { paddingVertical: 4, }, diff --git a/src/Redux/Reducers/index.js b/src/Redux/Reducers/index.js index 4c73172..927eb0a 100644 --- a/src/Redux/Reducers/index.js +++ b/src/Redux/Reducers/index.js @@ -14,6 +14,7 @@ const initialState = { DownloadComic: {}, scrollPreference: 'horizontal', // Default scroll mode is horizontal hasRewardAdsShown: false, + themeMode: 'system', // Theme mode: 'light', 'dark', or 'system' }; /** @@ -200,6 +201,10 @@ const Reducers = createSlice({ // Update the flag indicating whether reward ads have been shown state.hasRewardAdsShown = action.payload; }, + setThemeMode: (state, action) => { + // Update the theme mode: 'light', 'dark', or 'system' + state.themeMode = action.payload; + }, }, }); @@ -224,5 +229,6 @@ export const { clearHistory, setScrollPreference, rewardAdsShown, + setThemeMode, } = Reducers.actions; export default Reducers.reducer; diff --git a/src/Screens/Settings/index.js b/src/Screens/Settings/index.js index a784a52..7e17653 100644 --- a/src/Screens/Settings/index.js +++ b/src/Screens/Settings/index.js @@ -27,23 +27,136 @@ import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import {NAVIGATION} from '../../Constants'; import Header from '../../Components/UIComp/Header'; import {useDispatch, useSelector} from 'react-redux'; -import {setScrollPreference} from '../../Redux/Reducers'; +import {setScrollPreference, setThemeMode} from '../../Redux/Reducers'; import DonationBanner from '../../InkNest-Externals/Donation/DonationBanner'; +import {useTheme} from '../../Theme'; + +/** + * Get the display label for the current theme mode + * @param {string} mode - The theme mode ('light', 'dark', or 'system') + * @returns {string} The display label + */ +const getThemeModeLabel = mode => { + switch (mode) { + case 'light': + return 'Light'; + case 'dark': + return 'Dark'; + case 'system': + default: + return 'System'; + } +}; + +/** + * Get the icon name for the current theme mode + * @param {string} mode - The theme mode ('light', 'dark', or 'system') + * @returns {string} The icon name + */ +const getThemeModeIcon = mode => { + switch (mode) { + case 'light': + return 'white-balance-sunny'; + case 'dark': + return 'moon-waning-crescent'; + case 'system': + default: + return 'theme-light-dark'; + } +}; export function Settings({navigation}) { const dispatch = useDispatch(); const scrollPreference = useSelector(state => state.data.scrollPreference); + const themeMode = useSelector(state => state.data.themeMode) || 'system'; + const {colors} = useTheme(); + + /** + * Cycle through theme modes: system -> light -> dark -> system + */ + const handleThemeToggle = () => { + analytics().logEvent('toggle_theme_mode', { + item: 'Theme Mode', + currentMode: themeMode, + }); + + let newMode; + switch (themeMode) { + case 'system': + newMode = 'light'; + break; + case 'light': + newMode = 'dark'; + break; + case 'dark': + default: + newMode = 'system'; + break; + } + + dispatch(setThemeMode(newMode)); + + Alert.alert( + 'Theme Changed', + `App theme is now set to ${getThemeModeLabel(newMode)}.`, + [{text: 'OK'}], + ); + }; return ( - -
+ +
+ + + + Theme + + + + {getThemeModeLabel(themeMode)} + + + + About us diff --git a/src/Theme/ThemeContext.js b/src/Theme/ThemeContext.js new file mode 100644 index 0000000..af8b239 --- /dev/null +++ b/src/Theme/ThemeContext.js @@ -0,0 +1,76 @@ +import React, {createContext, useContext, useMemo} from 'react'; +import {useSelector} from 'react-redux'; +import {useColorScheme} from 'react-native'; +import {lightColors, darkColors} from './colors'; + +/** + * @typedef {'light' | 'dark' | 'system'} ThemeMode + */ + +/** + * Theme context that provides the current theme colors and mode information. + */ +const ThemeContext = createContext({ + colors: darkColors, + isDark: true, + themeMode: 'system', +}); + +/** + * ThemeProvider component that wraps the application and provides theme context. + * It reads the theme preference from Redux store and the system color scheme + * to determine the current theme. + * + * @param {Object} props - Component props + * @param {React.ReactNode} props.children - Child components + * @returns {JSX.Element} The provider component wrapping children + */ +export function ThemeProvider({children}) { + const systemColorScheme = useColorScheme(); + const themeMode = useSelector(state => state.data.themeMode) || 'system'; + + const {colors, isDark} = useMemo(() => { + let isDarkTheme; + + if (themeMode === 'system') { + isDarkTheme = systemColorScheme === 'dark'; + } else { + isDarkTheme = themeMode === 'dark'; + } + + return { + colors: isDarkTheme ? darkColors : lightColors, + isDark: isDarkTheme, + }; + }, [themeMode, systemColorScheme]); + + const value = useMemo( + () => ({ + colors, + isDark, + themeMode, + }), + [colors, isDark, themeMode], + ); + + return ( + {children} + ); +} + +/** + * Custom hook to access the current theme context. + * Must be used within a ThemeProvider. + * + * @returns {{ colors: typeof darkColors, isDark: boolean, themeMode: string }} + * The current theme colors, dark mode flag, and theme mode setting + */ +export function useTheme() { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +} + +export {lightColors, darkColors}; diff --git a/src/Theme/colors.js b/src/Theme/colors.js new file mode 100644 index 0000000..110bc48 --- /dev/null +++ b/src/Theme/colors.js @@ -0,0 +1,120 @@ +/** + * Theme color definitions for Light and Dark modes. + * These colors are used throughout the application to maintain consistent theming. + */ + +export const lightColors = { + // Primary backgrounds + primary: '#FFFFFF', + secondary: '#F5F5F5', + background: '#FFFFFF', + surface: '#F8F8F8', + + // Text colors + text: '#000000', + textSecondary: '#666666', + textMuted: '#999999', + + // UI elements + card: '#FFFFFF', + cardBorder: '#E0E0E0', + border: '#E0E0E0', + divider: '#E0E0E0', + + // Navigation + tabBar: '#FFFFFF', + tabBarActive: '#5B67F1', + tabBarInactive: '#999999', + headerBackground: '#FFFFFF', + headerBorder: '#E0E0E0', + + // Buttons and interactive elements + buttonPrimary: '#5B67F1', + buttonPrimaryText: '#FFFFFF', + buttonSecondary: 'rgba(91, 103, 241, 0.1)', + buttonSecondaryText: '#5B67F1', + + // Status colors + success: '#4CAF50', + warning: '#FF9800', + error: '#F44336', + info: '#2196F3', + + // Accent colors + accent: '#5B67F1', + accentLight: 'rgba(91, 103, 241, 0.2)', + + // Overlay colors + overlay: 'rgba(0, 0, 0, 0.5)', + overlayLight: 'rgba(0, 0, 0, 0.1)', + + // Loading and skeleton + skeleton: '#E0E0E0', + loadingBackground: 'rgba(255, 255, 255, 0.9)', + + // Icon colors + icon: '#000000', + iconSecondary: '#666666', + + // Settings specific + settingsItem: '#FFFFFF', + settingsItemText: '#000000', +}; + +export const darkColors = { + // Primary backgrounds + primary: '#14142A', + secondary: '#1C1C3A', + background: '#14142A', + surface: '#1E1E3C', + + // Text colors + text: '#FFFFFF', + textSecondary: 'rgba(255, 255, 255, 0.7)', + textMuted: 'rgba(255, 255, 255, 0.5)', + + // UI elements + card: 'rgba(255, 255, 255, 0.05)', + cardBorder: 'rgba(255, 255, 255, 0.1)', + border: 'rgba(255, 255, 255, 0.1)', + divider: 'rgba(255, 255, 255, 0.1)', + + // Navigation + tabBar: '#110918', + tabBarActive: '#D2D2D6', + tabBarInactive: '#6B666D', + headerBackground: '#14142A', + headerBorder: 'rgba(255, 255, 255, 0.5)', + + // Buttons and interactive elements + buttonPrimary: '#5B67F1', + buttonPrimaryText: '#FFFFFF', + buttonSecondary: 'rgba(91, 103, 241, 0.3)', + buttonSecondaryText: '#FFFFFF', + + // Status colors + success: '#4CAF50', + warning: '#FF9800', + error: '#F44336', + info: '#2196F3', + + // Accent colors + accent: '#5B67F1', + accentLight: 'rgba(91, 103, 241, 0.2)', + + // Overlay colors + overlay: 'rgba(20, 20, 42, 0.8)', + overlayLight: 'rgba(255, 255, 255, 0.06)', + + // Loading and skeleton + skeleton: 'rgba(255, 255, 255, 0.1)', + loadingBackground: 'rgba(20, 20, 42, 0.9)', + + // Icon colors + icon: '#FFFFFF', + iconSecondary: 'rgba(255, 255, 255, 0.7)', + + // Settings specific + settingsItem: '#FFFFFF', + settingsItemText: '#000000', +}; diff --git a/src/Theme/index.js b/src/Theme/index.js new file mode 100644 index 0000000..16d31f9 --- /dev/null +++ b/src/Theme/index.js @@ -0,0 +1 @@ +export {ThemeProvider, useTheme, lightColors, darkColors} from './ThemeContext'; From 7051a7a92fdac48358805ca8f41885dbd8e0478d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:06:27 +0000 Subject: [PATCH 3/3] fix: Address code review feedback - use theme colors for links Co-authored-by: pushpender-singh-ap <73298854+pushpender-singh-ap@users.noreply.github.com> --- src/Screens/Settings/index.js | 4 ++-- src/Theme/colors.js | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Screens/Settings/index.js b/src/Screens/Settings/index.js index 7e17653..c085071 100644 --- a/src/Screens/Settings/index.js +++ b/src/Screens/Settings/index.js @@ -147,7 +147,7 @@ export function Settings({navigation}) { {getThemeModeLabel(themeMode)} @@ -344,7 +344,7 @@ export function Settings({navigation}) { {scrollPreference === 'horizontal' ? 'Horizontal' : 'Vertical'} diff --git a/src/Theme/colors.js b/src/Theme/colors.js index 110bc48..e222f70 100644 --- a/src/Theme/colors.js +++ b/src/Theme/colors.js @@ -57,8 +57,11 @@ export const lightColors = { iconSecondary: '#666666', // Settings specific - settingsItem: '#FFFFFF', + settingsItem: '#F5F5F5', settingsItemText: '#000000', + + // Link and action colors + link: '#007AFF', }; export const darkColors = { @@ -117,4 +120,7 @@ export const darkColors = { // Settings specific settingsItem: '#FFFFFF', settingsItemText: '#000000', + + // Link and action colors + link: '#007AFF', };