Commit e00c479c authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg
Browse files

Merge branch 'refactor-dispatch-props' into 'master'

Refactor some stuff

Closes #96, #95, and #94

See merge request !234
parents 40a59f7d 23d03d29
......@@ -8,8 +8,8 @@ const mapStateToProps = state => ({
status: state.calendar.status,
});
const mapDispatchToProps = dispatch => ({
refresh: () => dispatch(calendarActions.refresh()),
});
const mapDispatchToProps = {
refresh: calendarActions.refresh,
};
export default connect(mapStateToProps, mapDispatchToProps)(CalendarScreen);
......@@ -13,7 +13,7 @@ import Colors from '../../style/Colors';
import StandardHeader from '../../components/standardHeader/StandardHeader';
import LoadingScreen from '../../components/loadingScreen/LoadingScreen';
import ErrorScreen from '../../components/errorScreen/ErrorScreen';
import SearchHeader from '../../components/searchHeader/SearchHeaderContainer';
import SearchHeader from '../../components/searchHeader/SearchHeaderConnector';
import Button from '../../components/button/Button';
const PAYMENT_TYPES = {
......
......@@ -10,12 +10,10 @@ const mapStateToProps = state => ({
loading: state.event.loading,
});
const mapDispatchToProps = dispatch => ({
refresh: pk => dispatch(eventActions.event(pk, false)),
updateRegistration: (pk, present, payment) => dispatch(
eventActions.updateRegistration(pk, present, payment),
),
goBack: () => dispatch(navigationActions.goBack()),
});
const mapDispatchToProps = {
refresh: pk => eventActions.event(pk, false),
updateRegistration: eventActions.updateRegistration,
goBack: navigationActions.goBack,
};
export default connect(mapStateToProps, mapDispatchToProps)(EventAdminScreen);
......@@ -4,7 +4,6 @@ import {
Alert,
FlatList,
Image,
Linking,
RefreshControl,
ScrollView,
Text,
......@@ -19,7 +18,7 @@ import Share from 'react-native-share';
import Icon from 'react-native-vector-icons/MaterialIcons';
import styles, { memberSize } from './style/EventScreen';
import MemberView from '../../components/memberView/MemberViewContainer';
import MemberView from '../../components/memberView/MemberViewConnector';
import LoadingScreen from '../../components/loadingScreen/LoadingScreen';
import ErrorScreen from '../../components/errorScreen/ErrorScreen';
import Colors from '../../style/Colors';
......@@ -34,8 +33,8 @@ class EventScreen extends Component {
const cancelDeadlineDate = new Date(data.cancel_deadline);
let message = t('Are you sure you want to cancel your registration?');
if (data.cancel_deadline !== null && cancelDeadlineDate <= new Date()) {
message = t('The deadline has passed, are you sure you want to cancel your '
+ 'registration and pay the full costs of €{{ fine }}? You will not be able to undo this!',
message = t('The deadline has passed, are you sure you want to cancel your \
registration and pay the full costs of €{{ fine }}? You will not be able to undo this!',
{ fine: data.fine });
}
return Alert.alert(
......@@ -256,8 +255,8 @@ class EventScreen extends Component {
text += ' ';
}
text += t(
'Cancellation isn\'t possible anymore without having to pay the full '
+ 'costs of €{{fine}}. Also note that you will be unable to re-register.',
'Cancellation isn\'t possible anymore without having to pay the full \
costs of €{{fine}}. Also note that you will be unable to re-register.',
{ fine: data.fine },
);
}
......@@ -274,7 +273,7 @@ class EventScreen extends Component {
eventActions = () => {
const {
data, register, t,
data, register, t, openUrl,
} = this.props;
const {
......@@ -293,7 +292,7 @@ class EventScreen extends Component {
{t('By registering, you confirm that you have read the')}
<Text
style={styles.termsUrl}
onPress={() => Linking.openURL(termsAndConditionsUrl)}
onPress={() => openUrl(termsAndConditionsUrl)}
>
{' '}
{t('terms and conditions')}
......@@ -380,7 +379,7 @@ class EventScreen extends Component {
render() {
const {
status, loading, openMaps, data, t, openAdmin,
status, loading, openMaps, data, t, openAdmin, openUrl,
} = this.props;
const shareButton = (
......@@ -471,7 +470,7 @@ class EventScreen extends Component {
<View style={styles.divider} />
<HTML
html={data.description}
onLinkPress={(event, href) => Linking.openURL(href)}
onLinkPress={(event, href) => openUrl(href)}
baseFontStyle={fontStyles}
tagsStyles={{
a: linkStyles,
......@@ -553,6 +552,7 @@ EventScreen.propTypes = {
openMaps: PropTypes.func.isRequired,
retrievePizzaInfo: PropTypes.func.isRequired,
openAdmin: PropTypes.func.isRequired,
openUrl: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
......
import { Linking, Platform } from 'react-native';
import { Platform } from 'react-native';
import { connect } from 'react-redux';
import * as pizzaActions from '../../../actions/pizza';
import * as registrationActions from '../../../actions/registration';
import * as eventActions from '../../../actions/event';
import EventScreen from './EventScreen';
import * as navigationActions from '../../../actions/navigation';
const mapStateToProps = state => ({
data: state.event.data,
......@@ -12,14 +13,15 @@ const mapStateToProps = state => ({
loading: state.event.loading,
});
const mapDispatchToProps = dispatch => ({
refresh: pk => dispatch(eventActions.event(pk)),
register: event => dispatch(registrationActions.register(event)),
cancel: registration => dispatch(registrationActions.cancel(registration)),
fields: registration => dispatch(registrationActions.retrieveFields(registration)),
openMaps: location => Linking.openURL(`https://maps.${Platform.OS === 'ios' ? 'apple' : 'google'}.com/maps?daddr=${location}`),
retrievePizzaInfo: () => dispatch(pizzaActions.retrievePizzaInfo()),
openAdmin: () => (dispatch(eventActions.admin())),
});
const mapDispatchToProps = {
refresh: eventActions.event,
register: registrationActions.register,
cancel: registrationActions.cancel,
fields: registrationActions.retrieveFields,
openMaps: location => this.openUrl(`https://maps.${Platform.OS === 'ios' ? 'apple' : 'google'}.com/maps?daddr=${location}`),
openUrl: navigationActions.openWebsite,
retrievePizzaInfo: pizzaActions.retrievePizzaInfo,
openAdmin: eventActions.admin,
};
export default connect(mapStateToProps, mapDispatchToProps)(EventScreen);
......@@ -2,7 +2,6 @@ import React, { Component } from 'react';
import {
ActivityIndicator,
KeyboardAvoidingView,
Linking,
Modal,
ScrollView,
Switch,
......@@ -90,11 +89,15 @@ class RegistrationScreen extends Component {
};
render() {
if (this.props.status === 'failure') {
return <ErrorScreen message={this.props.t('Sorry! We couldn\'t load any data.')} />;
const {
status, fields, openUrl, update, t, registration,
} = this.props;
if (status === 'failure') {
return <ErrorScreen message={t('Sorry! We couldn\'t load any data.')} />;
}
const keys = Object.keys(this.props.fields);
const keys = Object.keys(fields);
const linkStyles = {
color: Colors.magenta,
......@@ -111,7 +114,7 @@ class RegistrationScreen extends Component {
keyboardShouldPersistTaps="handled"
>
<Modal
visible={this.props.status === 'loading'}
visible={status === 'loading'}
transparent
onRequestClose={() => ({})}
>
......@@ -124,7 +127,7 @@ class RegistrationScreen extends Component {
</View>
</Modal>
{keys.map((key) => {
const field = this.props.fields[key];
const field = fields[key];
const validity = this.getFieldValidity(key);
if (field.type === 'boolean') {
return (
......@@ -145,7 +148,7 @@ class RegistrationScreen extends Component {
</View>
<HTML
html={field.description}
onLinkPress={(event, href) => Linking.openURL(href)}
onLinkPress={(event, href) => openUrl(href)}
baseFontStyle={styles.description}
tagsStyles={{
a: linkStyles,
......@@ -166,7 +169,7 @@ class RegistrationScreen extends Component {
</Text>
<HTML
html={field.description}
onLinkPress={(event, href) => Linking.openURL(href)}
onLinkPress={(event, href) => openUrl(href)}
baseFontStyle={styles.description}
tagsStyles={{
a: linkStyles,
......@@ -190,12 +193,12 @@ class RegistrationScreen extends Component {
}
return <View />;
})}
{this.props.status !== 'loading' && (
{status !== 'loading' && (
<View style={styles.buttonView}>
<Button
title={this.props.t('Save')}
title={t('Save')}
color={Colors.magenta}
onPress={() => this.props.update(this.props.registration, this.state)}
onPress={() => update(registration, this.state)}
disabled={!this.isFormValid()}
/>
</View>
......@@ -211,6 +214,7 @@ RegistrationScreen.propTypes = {
fields: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
status: PropTypes.string.isRequired,
update: PropTypes.func.isRequired,
openUrl: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
......
import { connect } from 'react-redux';
import * as registrationActions from '../../../actions/registration';
import RegistrationScreen from './RegistrationScreen';
import * as navigationActions from '../../../actions/navigation';
const mapStateToProps = state => ({
registration: state.registration.registration,
......@@ -8,8 +9,9 @@ const mapStateToProps = state => ({
status: state.registration.status,
});
const mapDispatchToProps = dispatch => ({
update: (registration, fields) => dispatch(registrationActions.update(registration, fields)),
});
const mapDispatchToProps = {
update: registrationActions.update,
openUrl: navigationActions.openWebsite,
};
export default connect(mapStateToProps, mapDispatchToProps)(RegistrationScreen);
......@@ -5,7 +5,6 @@ import {
Image,
KeyboardAvoidingView,
LayoutAnimation,
Linking,
Text,
TextInput,
View,
......@@ -42,7 +41,9 @@ class LoginScreen extends Component {
render() {
configureNextAnimation();
const { login, t, status } = this.props;
const {
login, t, status, openUrl,
} = this.props;
let content = (
<View>
......@@ -74,10 +75,10 @@ class LoginScreen extends Component {
textStyle={styles.loginButtonText}
underlayColor={Colors.white}
/>
<Text style={styles.linkText} onPress={() => Linking.openURL(`${url}/password_reset/`)}>
<Text style={styles.linkText} onPress={() => openUrl(`${url}/password_reset/`)}>
{t('Forgot password?')}
</Text>
<Text style={styles.linkText} onPress={() => Linking.openURL(`${url}/registration/`)}>
<Text style={styles.linkText} onPress={() => openUrl(`${url}/registration/`)}>
{t('Become a member')}
</Text>
</View>
......@@ -114,6 +115,7 @@ class LoginScreen extends Component {
LoginScreen.propTypes = {
login: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
openUrl: PropTypes.func.isRequired,
status: PropTypes.string.isRequired,
};
......
......@@ -2,13 +2,15 @@ import { connect } from 'react-redux';
import { Keyboard } from 'react-native';
import * as actions from '../../../actions/session';
import LoginScreen from './LoginScreen';
import * as navigationActions from '../../../actions/navigation';
const mapStateToProps = state => state.session;
const mapDispatchToProps = dispatch => ({
const mapDispatchToProps = {
login: (username, password) => {
Keyboard.dismiss();
dispatch(actions.signIn(username, password));
return actions.signIn(username, password);
},
});
openUrl: navigationActions.openWebsite,
};
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
......@@ -3,10 +3,10 @@ import { FlatList, View } from 'react-native';
import { translate } from 'react-i18next';
import PropTypes from 'prop-types';
import MemberView from '../../components/memberView/MemberViewContainer';
import MemberView from '../../components/memberView/MemberViewConnector';
import LoadingScreen from '../../components/loadingScreen/LoadingScreen';
import ErrorScreen from '../../components/errorScreen/ErrorScreen';
import SearchHeader from '../../components/searchHeader/SearchHeaderContainer';
import SearchHeader from '../../components/searchHeader/SearchHeaderConnector';
import styles, { memberSize } from './style/MemberList';
import DismissKeyboardView from '../../components/dismissKeyboardView/DismissKeyboardView';
......
......@@ -10,9 +10,9 @@ const mapStateToProps = state => ({
searchKey: state.members.searchKey,
});
const mapDispatchToProps = dispatch => ({
loadMembers: (keywords = '') => dispatch(memberActions.members(keywords)),
loadMoreMembers: url => dispatch(memberActions.more(url)),
});
const mapDispatchToProps = {
loadMembers: (keywords = '') => memberActions.members(keywords),
loadMoreMembers: memberActions.more,
};
export default connect(mapStateToProps, mapDispatchToProps)(MemberList);
import { connect } from 'react-redux';
import { cancelOrder, orderPizza, retrievePizzaInfo } from '../../../actions/pizza';
import * as pizzaActions from '../../../actions/pizza';
import PizzaScreen from './PizzaScreen';
const mapStateToProps = state => ({
......@@ -11,10 +11,10 @@ const mapStateToProps = state => ({
pizzaList: state.pizza.pizzaList,
});
const mapDispatchToProps = dispatch => ({
loadPizzas: () => dispatch(retrievePizzaInfo()),
cancelPizza: () => dispatch(cancelOrder()),
orderPizza: (pk, hasOrder) => dispatch(orderPizza(pk, hasOrder)),
});
const mapDispatchToProps = {
loadPizzas: pizzaActions.retrievePizzaInfo,
cancelPizza: pizzaActions.cancelOrder,
orderPizza: pizzaActions.orderPizza,
};
export default connect(mapStateToProps, mapDispatchToProps)(PizzaScreen);
import PropTypes from 'prop-types';
import React from 'react';
import { translate } from 'react-i18next';
import { Text, View } from 'react-native';
import Moment from 'moment';
import CardSection from '../../components/cardSection/CardSection';
import styles from './style/Profile';
const AchievementSection = ({ profile, t, type }) => {
const memberships = profile[type];
const sectionHeader = type === 'societies' ? t('Societies') : t('Achievements for Thalia');
if (memberships.length) {
return (
<CardSection
sectionHeader={sectionHeader}
>
{memberships.map((achievement, i) => (
<View style={[styles.item, i !== 0 && styles.borderTop]} key={achievement.name}>
<Text style={styles.description}>
{achievement.name}
</Text>
{achievement.periods && achievement.periods.map((period) => {
let start = Moment(period.since);
start = start.isSame(Moment([1970, 1, 1]), 'year') ? '?' : start.format('D MMMM YYYY');
const end = period.until ? Moment(period.until).format('D MMMM YYYY') : t('today');
let text = '';
if (period.role) {
text = `${period.role}: `;
} else if (period.chair) {
text = `${t('Chair')}: `;
}
text += `${start} - ${end}`;
return (
<Text style={styles.data} key={period.since}>
{text}
</Text>
);
})}
</View>
))}
</CardSection>
);
}
return <View />;
};
AchievementSection.propTypes = {
profile: PropTypes.shape({
achievements: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
earliest: PropTypes.string,
periods: PropTypes.arrayOf(PropTypes.shape({
chair: PropTypes.bool.isRequired,
until: PropTypes.string,
since: PropTypes.string.isRequired,
role: PropTypes.string,
})),
})).isRequired,
societies: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
earliest: PropTypes.string,
periods: PropTypes.arrayOf(PropTypes.shape({
chair: PropTypes.bool.isRequired,
until: PropTypes.string,
since: PropTypes.string.isRequired,
role: PropTypes.string,
})),
})).isRequired,
}).isRequired,
t: PropTypes.func.isRequired,
type: PropTypes.string.isRequired,
};
export default translate('screens/profile/AchievementSection')(AchievementSection);
import PropTypes from 'prop-types';
import React from 'react';
import { translate } from 'react-i18next';
import { Text } from 'react-native';
import CardSection from '../../components/cardSection/CardSection';
import styles from './style/Profile';
const DescriptionSection = (
{ profile: { display_name: name, profile_description: description }, t },
) => (
<CardSection
sectionHeader={`${t('About')} ${name}`}
>
<Text
style={[
styles.data,
styles.item,
styles.profileText,
!description && styles.italics]}
>
{description || t('This member has not written a description yet.')}
</Text>
</CardSection>
);
DescriptionSection.propTypes = {
profile: PropTypes.shape({
display_name: PropTypes.string.isRequired,
profile_description: PropTypes.string,
}).isRequired,
t: PropTypes.func.isRequired,
};
export default translate('screens/profile/DescriptionSection')(DescriptionSection);
import PropTypes from 'prop-types';
import React from 'react';
import { translate } from 'react-i18next';
import { Text, View } from 'react-native';
import Moment from 'moment';
import CardSection from '../../components/cardSection/CardSection';
import styles from './style/Profile';
const PersonalInfoSection = ({ profile, t, openUrl }) => {
const profileInfo = {
starting_year: {
title: t('Cohort'),
display: x => x,
},
programme: {
title: t('Study programme'),
display: x => (x === 'computingscience' ? t('Computing science') : t('Information sciences')),
},
website: {
title: t('Website'),
display: x => x,
},
birthday: {
title: t('Birthday'),
display: x => Moment(x).format('D MMMM YYYY'),
},
};
const profileData = Object.keys(profileInfo).map((key) => {
if (profile[key]) {
return {
title: profileInfo[key].title,
value: profileInfo[key].display(profile[key]),
};
}
return null;
}).filter(n => n);
if (profileData) {
return (
<CardSection sectionHeader={t('Personal information')}>
{profileData.map((item, i) => (
<View style={[styles.item, i !== 0 && styles.borderTop]} key={item.title}>
<Text style={styles.description}>
{item.title}
</Text>
<Text
style={item.title === 'Website' ? [styles.data, styles.url] : styles.data}
onPress={item.title === 'Website' ? () => openUrl(`${item.value}`) : null}
>
{item.value}
</Text>
</View>
))}
</CardSection>
);
}
return <View />;
};
PersonalInfoSection.propTypes = {
profile: PropTypes.shape({
birthday: PropTypes.string,
starting_year: PropTypes.number,
programme: PropTypes.string,
website: PropTypes.string,
membership_type: PropTypes.string,
}).isRequired,
t: PropTypes.func.isRequired,
openUrl: PropTypes.func.isRequired,
};
export default translate('screens/profile/PersonalInfoSection')(PersonalInfoSection);
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {
Animated,
ImageBackground,
Linking,
Platform,
ScrollView,
StatusBar,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { translate } from 'react-i18next';
import Icon from 'react-native-vector-icons/MaterialIcons';
import LinearGradient from 'react-native-linear-gradient';
import Moment from 'moment';
import StandardHeader from '../../components/standardHeader/StandardHeader';
import LoadingScreen from '../../components/loadingScreen/LoadingScreen';
import Icon from 'react-native-vector-icons/MaterialIcons';
import ErrorScreen from '../../components/errorScreen/ErrorScreen';
import LoadingScreen from '../../components/loadingScreen/LoadingScreen';