Add Theme Picker.

Moved Provider into App.js. Add AppContainer to store theme in redux
store. Add reducers and actions for theme, SET_THEME.
This commit is contained in:
Ceda EI 2020-03-17 19:25:03 +05:30
parent 635dbbf709
commit 0103e6abff
8 changed files with 260 additions and 46 deletions

122
App.js
View File

@ -1,20 +1,22 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, { useState } from "react"; import React, { useState } from "react";
import PropTypes from "prop-types";
import { import {
Appbar, Appbar,
BottomNavigation, BottomNavigation,
Text, Text,
Card, Card,
Dialog,
Portal,
Button,
Provider,
RadioButton,
TouchableRipple
} from "react-native-paper"; } from "react-native-paper";
import { View, StatusBar, StyleSheet } from "react-native";
import SubjectsContainer from "./containers/SubjectsContainer"; import SubjectsContainer from "./containers/SubjectsContainer";
import themes from "./themes";
function Dummy() { function Dummy() {
@ -28,7 +30,32 @@ function Dummy() {
); );
} }
const App = () => { function ThemePicker({ onPress, selectionIdx }) {
return (
<>
{themes.map((t, idx) => (
<TouchableRipple
onPress={() => onPress(t, idx)}
key={idx}
>
<View style={styles.radio}>
<RadioButton
status={selectionIdx === idx ? "checked": "unchecked"}
/>
<Text>{t.name}</Text>
</View>
</TouchableRipple>
))}
</>
);
}
ThemePicker.propTypes = {
onPress: PropTypes.func,
selectionIdx: PropTypes.number,
};
const App = ({ theme, setTheme }) => {
const [ pane, setPane ] = useState({ const [ pane, setPane ] = useState({
index: 0, index: 0,
routes: [ routes: [
@ -38,30 +65,81 @@ const App = () => {
{ key: "subjects", title: "Subjects", icon: "book-open" }, { key: "subjects", title: "Subjects", icon: "book-open" },
], ],
}); });
const [ showDialog, setShowDialog ] = useState(false);
const [ newTheme, setNewTheme ] = useState(themes.findIndex(
i => JSON.stringify(i.theme) === JSON.stringify(theme)
));
const renderScene = BottomNavigation.SceneMap({ const renderScene = BottomNavigation.SceneMap({
add: Dummy, add: Dummy,
statistics: Dummy, statistics: Dummy,
timetable: Dummy, timetable: Dummy,
subjects: SubjectsContainer, subjects: SubjectsContainer,
}); });
function dialogSelection(theme, idx) {
setNewTheme(idx);
setTheme(theme.theme);
}
return ( return (
<> <>
<Appbar.Header> <StatusBar
<Appbar.Content backgroundColor={
title="Sonzai" theme.dark && theme.mode === "adaptive" ?
/> theme.colors.surface :
</Appbar.Header> theme.colors.primary
<BottomNavigation }
navigationState={pane}
onIndexChange={(index) => setPane({
index,
routes: pane.routes
})}
renderScene={renderScene}
shifting={true}
/> />
<Provider theme={theme}>
<Appbar.Header>
<Appbar.Content
title="Sonzai"
/>
<Appbar.Action
icon="brush"
onPress={() => setShowDialog(true)}
/>
</Appbar.Header>
<BottomNavigation
navigationState={pane}
onIndexChange={(index) => setPane({
index,
routes: pane.routes
})}
renderScene={renderScene}
shifting={true}
/>
<Portal>
<Dialog
visible={showDialog}
onDismiss={() => setShowDialog(false)}>
<Dialog.Title>Theme</Dialog.Title>
<Dialog.Content>
<ThemePicker
onPress={dialogSelection}
selectionIdx={newTheme}
/>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={() => setShowDialog(false)}>Close</Button>
</Dialog.Actions>
</Dialog>
</Portal>
</Provider>
</> </>
); );
}; };
App.propTypes = {
theme: PropTypes.object,
setTheme: PropTypes.func,
};
const styles = StyleSheet.create({
radio: {
flexDirection: "row",
alignItems: "center",
margin: 5
}
});
export default App; export default App;

19
AppContainer.js Normal file
View File

@ -0,0 +1,19 @@
import { connect } from "react-redux";
import App from "./App";
import { setTheme } from "./actions";
const mapStateToProps = state => {
return {
theme: state.theme,
};
};
const mapDispatchToProps = dispatch => {
return {
setTheme: theme => dispatch(setTheme(theme))
};
};
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
export default AppContainer;

View File

@ -13,3 +13,10 @@ export function removeSubject(id) {
}; };
} }
export function setTheme(theme) {
return {
type: "SET_THEME",
theme: theme,
};
}

View File

@ -67,6 +67,8 @@ Subjects.propTypes = {
const style = StyleSheet.create({ const style = StyleSheet.create({
card: { card: {
marginTop: 12, marginTop: 12,
marginLeft: 10,
marginRight: 10,
}, },
text: { text: {
marginTop: 12, marginTop: 12,

View File

@ -1,40 +1,18 @@
import React from "react"; import React from "react";
import {AppRegistry} from "react-native"; import {AppRegistry} from "react-native";
import App from "./App"; import AppContainer from "./AppContainer";
import {name as appName} from "./app.json"; import {name as appName} from "./app.json";
import { DarkTheme, Provider } from "react-native-paper";
import { Provider as ReduxProvider } from "react-redux"; import { Provider as ReduxProvider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react"; import { PersistGate } from "redux-persist/integration/react";
import configureStore from "./configureStore"; import configureStore from "./configureStore";
const { store, persistor } = configureStore(); const { store, persistor } = configureStore();
const theme = {
...DarkTheme,
mode: "exact",
colors: {
primary: "#ededed",
accent: "#1a237e",
backdrop: "rgba(0, 0, 0, 0.5)",
background: "#000000",
disabled: "rgba(255, 255, 255, 0.38)",
error: "#CF6679",
notification: "#ff80ab",
onBackground: "#FFFFFF",
onSurface: "#FFFFFF",
placeholder: "rgba(255, 255, 255, 0.54)",
surface: "#121212",
text: "#ffffff"
}
};
export default function Main() { export default function Main() {
return ( return (
<ReduxProvider store={store}> <ReduxProvider store={store}>
<PersistGate persistor={persistor}> <PersistGate persistor={persistor}>
<Provider theme={theme}> <AppContainer />
<App />
</Provider>
</PersistGate> </PersistGate>
</ReduxProvider> </ReduxProvider>
); );

View File

@ -3,11 +3,13 @@ import { combineReducers } from "redux";
import timetable from "./timetable"; import timetable from "./timetable";
import classes from "./classes"; import classes from "./classes";
import subjects from "./subjects"; import subjects from "./subjects";
import theme from "./theme";
const rootReducer = combineReducers({ const rootReducer = combineReducers({
timetable, timetable,
classes, classes,
subjects, subjects,
theme,
}); });
export default rootReducer; export default rootReducer;

9
reducers/theme.js Normal file
View File

@ -0,0 +1,9 @@
import themes from "../themes";
export default function theme(state, action) {
if (typeof state === "undefined")
return themes[0].theme;
if (action.type === "SET_THEME") {
return action.theme;
}
return state;
}

119
themes.js Normal file
View File

@ -0,0 +1,119 @@
import { DefaultTheme, DarkTheme } from "react-native-paper";
export default [
{
"name": "Dark: Pink and Gray",
"theme": {
...DarkTheme,
mode: "exact",
colors: {
primary: "#ff1744",
accent: "#e0e0e0",
backdrop: "rgba(0, 0, 0, 0.5)",
background: "#000000",
disabled: "rgba(255, 255, 255, 0.38)",
error: "#CF6679",
notification: "#ff80ab",
onBackground: "#FFFFFF",
onSurface: "#FFFFFF",
placeholder: "rgba(255, 255, 255, 0.54)",
surface: "#121212",
text: "#ffffff"
}
}
},
{
"name": "Dark: Pastel Colors",
"theme": {
...DarkTheme,
mode: "exact",
colors: {
primary: "#ef9a9a",
accent: "#fce4ec",
backdrop: "rgba(0, 0, 0, 0.5)",
background: "#000000",
disabled: "rgba(255, 255, 255, 0.38)",
error: "#CF6679",
notification: "#ff80ab",
onBackground: "#FFFFFF",
onSurface: "#FFFFFF",
placeholder: "rgba(255, 255, 255, 0.54)",
surface: "#121212",
text: "#ffffff"
}
}
},
{
"name": "Dark: Pastel Colors 2",
"theme": {
...DarkTheme,
mode: "exact",
colors: {
primary: "#e0f7fa",
accent: "#ffebee",
backdrop: "rgba(0, 0, 0, 0.5)",
background: "#000000",
disabled: "rgba(255, 255, 255, 0.38)",
error: "#CF6679",
notification: "#ff80ab",
onBackground: "#FFFFFF",
onSurface: "#FFFFFF",
placeholder: "rgba(255, 255, 255, 0.54)",
surface: "#121212",
text: "#ffffff"
}
}
},
{
"name": "Absolute Dark: Pink and Gray",
"theme": {
...DarkTheme,
colors: {
...DarkTheme.colors,
primary: "#ff1744",
accent: "#e0e0e0",
}
}
},
{
"name": "Absolute Dark: Purple and Gray",
"theme": DarkTheme
},
{
"name": "Light: Pink and Gray",
"theme": {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: "#ff1744",
accent: "#e0e0e0",
}
}
},
{
"name": "Light: Pastel Colors",
"theme": {
...DefaultTheme,
mode: "exact",
colors: {
...DefaultTheme.colors,
primary: "#ef9a9a",
accent: "#fce4ec",
}
}
},
{
"name": "Light: Pastel Colors 2",
"theme": {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: "#ffcccb",
accent: "#fce4ec",
}
}
},
{
"name": "Light: Purple and Green",
"theme": DefaultTheme
},
];