Skip to content

Commit

Permalink
Feature/theme (#12)
Browse files Browse the repository at this point in the history
* integrated dark/light theme using react context and useState hooks
  • Loading branch information
justinkx committed Oct 11, 2019
1 parent 487a4a7 commit 534bb8d
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 61 deletions.
4 changes: 4 additions & 0 deletions App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@ import { Provider } from 'react-redux';
import { Navigation } from './navigation';
import NavigationService from './utils/navigationService';

import { ThemeContextProvider } from './theme/themeProvider';

const App = () => {
return (

<Provider store={store}>
<ThemeContextProvider>
<Navigation
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
</ThemeContextProvider>
</Provider>
);
};
Expand Down
2 changes: 1 addition & 1 deletion App/components/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export const Content = ({style,children}) => {
return (
<KeyboardAwareScrollView
automaticallyAdjustContentInsets={false}
style={{flex: 1}}
showsVerticalScrollIndicator
contentContainerStyle={[{
padding: 15,
height:'100%',
flexDirection: 'column'
},style]}>
{children}
Expand Down
2 changes: 1 addition & 1 deletion App/components/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const Footer = ({ style, children }) => {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent'
backgroundColor: 'white'
}, style]}>
{children}
</View>
Expand Down
1 change: 0 additions & 1 deletion App/components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { View,StatusBar } from 'react-native';
import { BarStyle } from '../theme/global';

export const Header = ({style,statusbarColor,barStyle = BarStyle ,children}) => {
console.log(children)
return (
<View style={[{
height: 48,
Expand Down
16 changes: 12 additions & 4 deletions App/containers/about/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import {
HeaderRight,
HeaderLeft, Button
} from '../../components/index';
import { wrapTheme } from '../../theme/themeProvider';

export default class AboutScreen extends PureComponent {

class About extends PureComponent {
constructor(props) {
super(props)
}
render() {
const { theme } = this.props;
const images = [
'https://cdn.dribbble.com/users/4103091/screenshots/7353178/media/6d1a3a06961c0dcfd513ffe241636472.png',
'https://cdn.dribbble.com/users/4103091/screenshots/7154300/media/7839c89716a90284e0c52595efb61dd5.jpg',
Expand All @@ -30,15 +34,17 @@ export default class AboutScreen extends PureComponent {
</HeaderBody>
<HeaderRight />
</Header>
<Content>
<Content style={{
backgroundColor: theme.backgroundColor
}}>
<View style={{
flexDirection: 'column',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 30,
}}>
<Text style={style.title}>About Screen</Text>
<Text style={[style.title,{color: theme.color}]}>About Screen</Text>

<Button
style={{ backgroundColor: 'green' }}
Expand All @@ -63,6 +69,8 @@ export default class AboutScreen extends PureComponent {
}
}

export default AboutScreen = wrapTheme(About);

const style = StyleSheet.create({
title: {
fontSize: 18,
Expand Down
6 changes: 5 additions & 1 deletion App/containers/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import NavigationService from '../../utils/navigationService';
import storageService from '../../utils/storageService';
import { connect } from 'react-redux';
import * as loginActions from './actions';
import { wrapTheme } from '../../theme/themeProvider';

class LoginScreen extends PureComponent {
class Login extends PureComponent {
constructor(props) {
super(props)
}
render() {
const { theme } = this.props;
return (
<View
style={{
flex: 1,
padding: 30,
backgroundColor: theme.backgroundColor,
justifyContent: 'space-evenly',
alignItems: 'center',
}}>
Expand Down Expand Up @@ -44,6 +47,7 @@ const mapDispatchToProps = dispatch => {
};
};

const LoginScreen = wrapTheme(Login)
export default connect(
mapStateToProps,
mapDispatchToProps
Expand Down
94 changes: 64 additions & 30 deletions App/containers/sideMenu/index.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,90 @@
import React, {PureComponent} from 'react';
import SafeAreaView from 'react-native-safe-area-view';
import { DrawerNavigatorItems } from 'react-navigation-drawer';
import { StyleSheet, Text } from 'react-native';
import React, { PureComponent } from 'react';
import { StyleSheet, View, Text, Switch } from 'react-native';
import {
Container, Header, Content, HeaderBody,
HeaderRight,Footer,
HeaderRight, Footer,
HeaderLeft, Button
} from '../../components/index';
import { GlobalStyle } from '../../theme/global';
import store from '../../store/configureStore';
import * as loginTypes from '../login/types';
import { wrapTheme } from '../../theme/themeProvider';
import storageService from '../../utils/storageService';


class Sidemenu extends PureComponent {

export class SidemenuScreen extends PureComponent {
constructor(props) {
super(props)
console.log(props)

this.state = {
isDarkMode: this.props.theme.key === 'Dark Mode' ? true : false
}
}

onLogOut() {
store.dispatch({
type: loginTypes.LOGOUT
});
}
onThemeChange() {
this.props.setTheme(this.state.isDarkMode ? 'Light Mode' : 'Dark Mode');
storageService.setThemeId(this.state.isDarkMode ? 'Light Mode' : 'Dark Mode');
this.setState((prev) => ({ isDarkMode: !prev.isDarkMode }))
}
render() {
return (
<Container>
<Header
barStyle="dark-content"
statusbarColor="white">
<HeaderLeft />
<HeaderBody>
<Text style={[GlobalStyle.headerTitle]}>Boilerplate</Text>
</HeaderBody>
<HeaderRight />
</Header>
<Content>
</Content>
<Footer>
<Button
onClick={()=> this.onLogOut()}
style={{backgroundColor: 'red'}}>
<Text style={{color:'white'}}>Logout</Text>
</Button>
</Footer>
</Container>
)
const { isDarkMode } = this.state;
const { theme } = this.props;
return (
<Container>
<Header
barStyle="dark-content"
statusbarColor="white">
<HeaderLeft />
<HeaderBody>
<Text style={[GlobalStyle.headerTitle]}>Boilerplate</Text>
</HeaderBody>
<HeaderRight />
</Header>
<Content style={{
backgroundColor: theme.backgroundColor
}}>
<Text style={{
marginBottom: 40,
color: theme.color,
fontWeight: 'bold'
}}>Switch Theme</Text>
<View style={styles.switchContainer}>
<Text style={{ color: theme.color,
fontSize: isDarkMode ? 14 : 16,
fontWeight: isDarkMode ? '100' : 'bold' }}>{'Light Mode'}</Text>
<Switch
onValueChange={() => this.onThemeChange()}
value={isDarkMode} />
<Text style={{ color: theme.color,
fontSize: isDarkMode ? 16 : 14,
fontWeight: isDarkMode ? 'bold' : '100' }}>{'Dark Mode'}</Text>
</View>
</Content>
<Footer>
<Button
onClick={() => this.onLogOut()}
style={{ backgroundColor: 'red' }}>
<Text style={{ color: 'white' }}>Logout</Text>
</Button>
</Footer>
</Container>
)
}
}

export const SidemenuScreen = wrapTheme(Sidemenu);
const styles = StyleSheet.create({
container: {
flex: 1,
},
switchContainer: {
flexDirection: 'row',
justifyContent: 'space-evenly',
alignItems: 'center',
}
});
40 changes: 37 additions & 3 deletions App/theme/themeProvider.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
import THEMES from './themes.json';
import React, { useContext, useState, useEffect } from 'react';

export const themes = THEMES;
export const theme = THEMES[1];
export const ThemeContext = React.createContext();

import storageService from '../utils/storageService';


import Themes from './themes.json';



export const ThemeContextProvider = ({ children }) => {
const [themeID, setThemeID] = useState();

useEffect(() => {
(async () => {
const storedThemeID = await storageService.getThemeId();
if (storedThemeID) setThemeID(storedThemeID);
else setThemeID(Themes[0].key);
})();
}, []);
return (
<ThemeContext.Provider value={{ themeID, setThemeID }}>
{!!themeID ? children : null}
</ThemeContext.Provider>
);
};

export function wrapTheme(Component) {
return props => {
const { themeID, setThemeID } = useContext(ThemeContext);
const getTheme = themeID => Themes.find(theme => theme.key === themeID);
const setTheme = themeID => setThemeID(themeID);

return <Component {...props} themes={Themes} theme={getTheme(themeID)}
setTheme={setTheme}/>;
};
}
22 changes: 6 additions & 16 deletions App/theme/themes.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
[
{
"key": "MAASTRICHT BLUE",
"backgroundColor": "#011627",
"color": "#ffffff"
"key": "Light Mode",
"backgroundColor": "white",
"color": "black"
},
{
"key": "MAXIMUM BLUE GREEN",
"backgroundColor": "#2EC4B6",
"color": "#ffffff"
},
{
"key": "ROSE MADDER",
"backgroundColor": "#E71D36",
"color": "#ffffff"
},
{
"key": "BRIGHT YELLOW (CRAYOLA)",
"backgroundColor": "#FF9F1C",
"color": "#1F2D3D"
"key": "Dark Mode",
"backgroundColor": "#24292e",
"color": "white"
}
]
17 changes: 16 additions & 1 deletion App/utils/storageService.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,27 @@ const getApiKey = async () => {
}
};

const setThemeId = id =>{
AsyncStorage.setItem('theme_id', id);
}

const getThemeId = async () => {
try {
const themeId = await AsyncStorage.getItem('theme_id');
return themeId;
} catch (error) {
return error;
}
};

const clearApiKey = async () => {
await AsyncStorage.removeItem('api_key');
}

export default {
setApiKey,
getApiKey,
clearApiKey
clearApiKey,
setThemeId,
getThemeId
};
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<br />

<div align="center"><strong>Start Your Next React Native Project In Seconds</strong></div>
<div align="center">A highly scalable, redux state management, sagas middleware integrated setup focus on performance and best practices</div>
<div align="center">A highly scalable, redux state management, sagas middleware, Dark/Light theme mode integrated setup focus on performance and best practices</div>

<br />

Expand Down Expand Up @@ -33,7 +33,7 @@

# React Native Boilerplate

React Native boilerplate using react-native-cli with react-navigation, redux state management, redux-saga middleware and auth flow mechanism with drawer-tab navigation layout.
React Native boilerplate using react-native-cli with react-navigation, redux state management, redux-saga middleware and auth flow mechanism with drawer-tab navigation layout. Dark & Light theme switch mechanism using react context and useState Hooks.


## Prerequisites
Expand Down Expand Up @@ -83,7 +83,7 @@ react-native run <android/ios>
- redux
- react-redux
- redux-saga

- React Hooks

### React-Native Debugger
![React Native Debugger](https://user-images.githubusercontent.com/3001525/29451479-6621bf1a-83c8-11e7-8ebb-b4e98b1af91c.png)
Expand Down

0 comments on commit 534bb8d

Please sign in to comment.