Styled app

This commit is contained in:
Federico Kereki
2018-09-05 23:21:48 -03:00
parent 8b35af8bc5
commit c4f6b0b629
18 changed files with 570 additions and 3 deletions
+17
View File
@@ -0,0 +1,17 @@
/* @flow */
import React from "react";
import { Provider } from "react-redux";
import { store } from "./src/adaptiveApp/store";
import { Main } from "./src/adaptiveApp/main";
export default class App extends React.PureComponent<> {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}
+3 -3
View File
@@ -3,14 +3,14 @@
import React from "react";
import { Provider } from "react-redux";
import { store } from "./src/adaptiveApp/store";
import { Main } from "./src/adaptiveApp/main";
import { store } from "./src/regionsStyledApp/store";
import { ConnectedMain } from "./src/regionsStyledApp/main.connected";
export default class App extends React.PureComponent<> {
render() {
return (
<Provider store={store}>
<Main />
<ConnectedMain />
</Provider>
);
}
+17
View File
@@ -0,0 +1,17 @@
/* @flow */
import React from "react";
import { Provider } from "react-redux";
import { store } from "./src/regionsStyledApp/store";
import { ConnectedMain } from "./src/regionsStyledApp/main.connected";
export default class App extends React.PureComponent<> {
render() {
return (
<Provider store={store}>
<ConnectedMain />
</Provider>
);
}
}
@@ -0,0 +1,71 @@
/* @flow */
import React from "react";
import PropTypes from "prop-types";
import { View, Text, Picker } from "react-native";
import type { deviceDataType } from "./device";
export class CountrySelect extends React.PureComponent<{
deviceData: deviceDataType,
loading: boolean,
currentCountry: string,
list: Array<object>,
onSelect: string => void,
getCountries: () => void
}> {
static propTypes = {
deviceData: PropTypes.object.isRequired, // deviceDataType,
loading: PropTypes.bool.isRequired,
currentCountry: PropTypes.string.isRequired,
list: PropTypes.arrayOf(PropTypes.object).isRequired,
onSelect: PropTypes.func.isRequired,
getCountries: PropTypes.func.isRequired
};
componentDidMount() {
if (this.props.list.length === 0) {
this.props.getCountries();
}
}
onSelect = value => this.props.onSelect(value);
render() {
if (this.props.loading) {
return (
<View>
<Text>Loading countries...</Text>
</View>
);
} else {
const sortedCountries = [...this.props.list].sort(
(a, b) => (a.countryName < b.countryName ? -1 : 1)
);
return (
<View>
<Text>Country:</Text>
<Picker
onValueChange={this.onSelect}
prompt="Country"
selectedValue={this.props.currentCountry || "TV"}
>
<Picker.Item
key={"00"}
label={"Select a country:"}
value={""}
/>
{sortedCountries.map(x => (
<Picker.Item
key={x.countryCode}
label={x.countryName}
value={x.countryCode}
/>
))}
</Picker>
</View>
);
}
}
}
@@ -0,0 +1,23 @@
/* @flow */
import { connect } from "react-redux";
import { CountrySelect } from "./countrySelect.component";
import { getCountries, getRegions } from "./world.actions";
const getProps = state => ({
deviceData: state.deviceData,
list: state.countries,
currentCountry: state.currentCountry,
loading: state.loadingCountries
});
const getDispatch = dispatch => ({
getCountries: () => dispatch(getCountries()),
onSelect: c => dispatch(getRegions(c))
});
export const ConnectedCountrySelect = connect(
getProps,
getDispatch
)(CountrySelect);
+25
View File
@@ -0,0 +1,25 @@
/* @flow */
import { Dimensions } from "react-native";
export type deviceDataType = {
isTablet: boolean,
isPortrait: boolean,
height: number,
width: number,
scale: number,
fontScale: number
};
export const getDeviceData = (): deviceDataType => {
const { height, width, scale, fontScale } = Dimensions.get("screen");
return {
isTablet: Math.max(height, width) / Math.min(height, width) <= 1.6,
isPortrait: height > width,
height,
width,
scale,
fontScale
};
};
@@ -0,0 +1,21 @@
/* @flow */
import React from "react";
import PropTypes from "prop-types";
import { View } from "react-native";
class DeviceHandler extends React.PureComponent<{
setDevice: () => any
}> {
static propTypes = {
setDevice: PropTypes.func.isRequired
};
onLayoutHandler = () => this.props.setDevice();
render() {
return <View hidden onLayout={this.onLayoutHandler} />;
}
}
export { DeviceHandler };
@@ -0,0 +1,15 @@
/* @flow */
import { connect } from "react-redux";
import { DeviceHandler } from "./deviceHandler.component";
import { setDevice } from "./world.actions";
const getDispatch = dispatch => ({
setDevice: () => dispatch(setDevice())
});
export const ConnectedDeviceHandler = connect(
null,
getDispatch
)(DeviceHandler);
+11
View File
@@ -0,0 +1,11 @@
/* @flow */
import { ConnectedCountrySelect } from "./countrySelect.connected.js";
import { ConnectedRegionsTable } from "./regionsTable.connected.js";
import { ConnectedDeviceHandler } from "./deviceHandler.connected";
export {
ConnectedCountrySelect,
ConnectedRegionsTable,
ConnectedDeviceHandler
};
@@ -0,0 +1,57 @@
/* @flow */
import React from "react";
import { View, StatusBar } from "react-native";
import {
ConnectedCountrySelect,
ConnectedRegionsTable,
ConnectedDeviceHandler
} from ".";
import type { deviceDataType } from "./device";
/* eslint-disable react-native/no-inline-styles */
export class Main extends React.PureComponent<{
deviceData: deviceDataType
}> {
render() {
if (this.props.deviceData.isPortrait) {
return (
<View style={{ flex: 1 }}>
<StatusBar hidden />
<ConnectedDeviceHandler />
<View style={{ flex: 1, flexDirection: "column" }}>
<View>
<ConnectedCountrySelect />
</View>
<View style={{ flex: 1 }}>
<ConnectedRegionsTable />
</View>
</View>
</View>
);
} else {
return (
<View style={{ flex: 1 }}>
<StatusBar hidden />
<ConnectedDeviceHandler />
<View style={{ flex: 1, flexDirection: "row" }}>
<View
style={{
flex: 1,
flexDirection: "column",
justifyContent: "center"
}}
>
<ConnectedCountrySelect />
</View>
<View style={{ flex: 1 }}>
<ConnectedRegionsTable />
</View>
</View>
</View>
);
}
}
}
@@ -0,0 +1,11 @@
/* @flow */
import { connect } from "react-redux";
import { Main } from "./main.component";
const getProps = state => ({
deviceData: state.deviceData
});
export const ConnectedMain = connect(getProps)(Main);
@@ -0,0 +1,55 @@
/* @flow */
import React from "react";
import PropTypes from "prop-types";
import { View, ScrollView, Text, StyleSheet } from "react-native";
import type { deviceDataType } from "./device";
import { lowColor, fullSizeStyle } from "./styleConstants";
const ownStyle = StyleSheet.create({
grayish: {
backgroundColor: lowColor
}
});
export class RegionsTable extends React.PureComponent<{
deviceData: deviceDataType,
list: Array<{
regionCode: string,
regionName: string
}>
}> {
static propTypes = {
deviceData: PropTypes.object.isRequired,
list: PropTypes.arrayOf(PropTypes.object).isRequired
};
static defaultProps = {
list: []
};
render() {
if (this.props.list.length === 0) {
return (
<View style={ownStyle.fullSize}>
<Text>No regions.</Text>
</View>
);
} else {
const ordered = [...this.props.list].sort(
(a, b) => (a.regionName < b.regionName ? -1 : 1)
);
return (
<ScrollView style={[fullSizeStyle, ownStyle.grayish]}>
{ordered.map(x => (
<View key={`${x.countryCode}-${x.regionCode}`}>
<Text>{x.regionName}</Text>
</View>
))}
</ScrollView>
);
}
}
}
@@ -0,0 +1,13 @@
/* @flow */
import { connect } from "react-redux";
import { RegionsTable } from "./regionsTable.component";
const getProps = state => ({
deviceData: state.deviceData,
list: state.regions,
loading: state.loadingRegions
});
export const ConnectedRegionsTable = connect(getProps)(RegionsTable);
@@ -0,0 +1,9 @@
/* @flow */
import axios from "axios";
export const getCountriesAPI = () =>
axios.get(`http://192.168.1.200:8080/countries`);
export const getRegionsAPI = country =>
axios.get(`http://192.168.1.200:8080/regions/${country}`);
+8
View File
@@ -0,0 +1,8 @@
/* @flow */
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { reducer } from "./world.reducer";
export const store = createStore(reducer, applyMiddleware(thunk));
@@ -0,0 +1,11 @@
/* @flow */
import { StyleSheet } from "react-native";
export const styles = StyleSheet.create({
fullSize: {
flex: 1
}
});
export const lowColor = "lightgray";
@@ -0,0 +1,122 @@
/* @flow */
import { getCountriesAPI, getRegionsAPI } from "./serviceApi";
import { getDeviceData } from "./device";
// Device layout action
export const DEVICE_DATA = "device:data";
export type deviceDataAction = {
type: string,
deviceData: any // deviceDataType
};
export const setDevice = (deviceData?: object) =>
({
type: DEVICE_DATA,
deviceData: deviceData || getDeviceData()
}: deviceDataAction);
// Countries actions
export const COUNTRIES_REQUEST = "countries:request";
export const COUNTRIES_SUCCESS = "countries:success";
export const COUNTRIES_FAILURE = "countries:failure";
export type CountriesAction = {
type: string,
country?: string,
listOfCountries?: [object]
};
export const countriesRequest = () =>
({
type: COUNTRIES_REQUEST
}: CountriesActions);
export const countriesSuccess = (listOfCountries: []) =>
({
type: COUNTRIES_SUCCESS,
listOfCountries
}: CountriesActions);
export const countriesFailure = () =>
({
type: COUNTRIES_FAILURE
}: CountriesActions);
// Regions actions
export const REGIONS_REQUEST = "regions:request";
export const REGIONS_SUCCESS = "regions:success";
export const REGIONS_FAILURE = "regions:failure";
export type RegionsAction = {
type: string,
listOfRegions?: [object]
};
export const regionsRequest = (country: string) =>
({
type: REGIONS_REQUEST,
country
}: RegionsActions);
export const regionsSuccess = (listOfRegions: [{}]) =>
({
type: REGIONS_SUCCESS,
listOfRegions
}: RegionsActions);
export const regionsFailure = () =>
({
type: REGIONS_FAILURE
}: RegionsActions);
// Complex Actions:
export const getCountries = () => async dispatch => {
try {
dispatch(countriesRequest());
const result = await getCountriesAPI();
dispatch(countriesSuccess(result.data));
} catch (e) {
dispatch(countriesFailure());
}
};
export const getRegions = (country: string) => async dispatch => {
if (country) {
try {
dispatch(regionsRequest(country));
const result = await getRegionsAPI(country);
dispatch(regionsSuccess(result.data));
} catch (e) {
dispatch(regionsFailure());
}
} else {
dispatch(regionsFailure());
}
};
export const getRegions2 = (country: string) => async (
dispatch,
getState
) => {
if (country === getState().currentCountry) {
console.log("Hey! You are getting the same country as before!");
}
if (country) {
try {
dispatch(regionsRequest(country));
const result = await getRegionsAPI(country);
dispatch(regionsSuccess(result.data));
} catch (e) {
dispatch(regionsFailure());
}
} else {
dispatch(regionsFailure());
}
};
@@ -0,0 +1,81 @@
/* @flow */
import {
DEVICE_DATA,
COUNTRIES_REQUEST,
COUNTRIES_SUCCESS,
COUNTRIES_FAILURE,
REGIONS_REQUEST,
REGIONS_SUCCESS,
REGIONS_FAILURE
} from "./world.actions";
import { getDeviceData } from "./device";
import type { CountriesAction, RegionsAction } from "./world.actions";
export const reducer = (
state: object = {
// initial state
deviceData: getDeviceData(),
loadingCountries: false,
currentCountry: "",
countries: [],
loadingRegions: false,
regions: []
},
action: CountriesAction | RegionsAction
) => {
switch (action.type) {
case DEVICE_DATA:
return {
...state,
deviceData: action.deviceData
};
case COUNTRIES_REQUEST:
return {
...state,
loadingCountries: true,
countries: []
};
case COUNTRIES_SUCCESS:
return {
...state,
loadingCountries: false,
countries: action.listOfCountries
};
case COUNTRIES_FAILURE:
return {
...state,
loadingCountries: false,
countries: []
};
case REGIONS_REQUEST:
return {
...state,
loadingRegions: true,
currentCountry: action.country,
regions: []
};
case REGIONS_SUCCESS:
return {
...state,
loadingRegions: false,
regions: action.listOfRegions
};
case REGIONS_FAILURE:
return {
...state,
loadingRegions: false,
regions: []
};
default:
return state;
}
};