mirror of https://gitlab.com/ceda_ei/sonzai.git
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:
parent
635dbbf709
commit
0103e6abff
96
App.js
96
App.js
|
@ -1,20 +1,22 @@
|
|||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
Appbar,
|
||||
BottomNavigation,
|
||||
Text,
|
||||
Card,
|
||||
Dialog,
|
||||
Portal,
|
||||
Button,
|
||||
Provider,
|
||||
RadioButton,
|
||||
TouchableRipple
|
||||
} from "react-native-paper";
|
||||
|
||||
import { View, StatusBar, StyleSheet } from "react-native";
|
||||
|
||||
import SubjectsContainer from "./containers/SubjectsContainer";
|
||||
import themes from "./themes";
|
||||
|
||||
|
||||
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({
|
||||
index: 0,
|
||||
routes: [
|
||||
|
@ -38,18 +65,39 @@ const App = () => {
|
|||
{ 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({
|
||||
add: Dummy,
|
||||
statistics: Dummy,
|
||||
timetable: Dummy,
|
||||
subjects: SubjectsContainer,
|
||||
});
|
||||
|
||||
function dialogSelection(theme, idx) {
|
||||
setNewTheme(idx);
|
||||
setTheme(theme.theme);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<StatusBar
|
||||
backgroundColor={
|
||||
theme.dark && theme.mode === "adaptive" ?
|
||||
theme.colors.surface :
|
||||
theme.colors.primary
|
||||
}
|
||||
/>
|
||||
<Provider theme={theme}>
|
||||
<Appbar.Header>
|
||||
<Appbar.Content
|
||||
title="Sonzai"
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="brush"
|
||||
onPress={() => setShowDialog(true)}
|
||||
/>
|
||||
</Appbar.Header>
|
||||
<BottomNavigation
|
||||
navigationState={pane}
|
||||
|
@ -60,8 +108,38 @@ const App = () => {
|
|||
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;
|
||||
|
|
|
@ -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;
|
|
@ -13,3 +13,10 @@ export function removeSubject(id) {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
export function setTheme(theme) {
|
||||
return {
|
||||
type: "SET_THEME",
|
||||
theme: theme,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ Subjects.propTypes = {
|
|||
const style = StyleSheet.create({
|
||||
card: {
|
||||
marginTop: 12,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
},
|
||||
text: {
|
||||
marginTop: 12,
|
||||
|
|
26
index.js
26
index.js
|
@ -1,40 +1,18 @@
|
|||
import React from "react";
|
||||
import {AppRegistry} from "react-native";
|
||||
import App from "./App";
|
||||
import AppContainer from "./AppContainer";
|
||||
import {name as appName} from "./app.json";
|
||||
import { DarkTheme, Provider } from "react-native-paper";
|
||||
import { Provider as ReduxProvider } from "react-redux";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
|
||||
import configureStore from "./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() {
|
||||
return (
|
||||
<ReduxProvider store={store}>
|
||||
<PersistGate persistor={persistor}>
|
||||
<Provider theme={theme}>
|
||||
<App />
|
||||
</Provider>
|
||||
<AppContainer />
|
||||
</PersistGate>
|
||||
</ReduxProvider>
|
||||
);
|
||||
|
|
|
@ -3,11 +3,13 @@ import { combineReducers } from "redux";
|
|||
import timetable from "./timetable";
|
||||
import classes from "./classes";
|
||||
import subjects from "./subjects";
|
||||
import theme from "./theme";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
timetable,
|
||||
classes,
|
||||
subjects,
|
||||
theme,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
},
|
||||
];
|
Loading…
Reference in New Issue