Commit 474d2c3a authored by Wietse Kuipers's avatar Wietse Kuipers
Browse files

Merge branch 'feature/calendar-style' into 'master'

Add styling for calendar

See merge request !23
parents 5db69856 8052bdbc
image: kkarczmarczyk/node-yarn:latest
stages:
- build
- test
cache:
......@@ -12,13 +11,8 @@ cache:
- .yarn
yarn install:
stage: build
stage: test
script:
- yarn config set cache-folder .yarn
- yarn install
jest:
stage: test
script:
- rm -rf .yarn
- yarn lint
import React from 'react';
import { Text, View, ListView } from 'react-native';
import React, { Component } from 'react';
import { Text, View, SectionList } from 'react-native';
import { connect } from 'react-redux';
import Moment from 'moment';
import 'moment/locale/nl';
import * as actions from '../actions/calendar';
import EventCard from './EventCard';
const Calendar = (props) => {
if (!props.calendarFetched) {
props.retrieveCalendar(props.token);
return (
<View>
<Text>
No calendar retrieved!
</Text>
</View>
);
import styles from './style/calendar';
/* eslint no-param-reassign: ["error", { "props": false }]*/
const addEventToSection = (sections, date, event) => {
const day = date.date();
const month = date.month();
if (!(month in sections)) {
sections[month] = {
key: date.format('MMMM'),
data: {},
};
}
if (!(day in sections[month].data)) {
sections[month].data[day] = {
dayNumber: day,
dayOfWeek: date.format('dd'),
events: [],
};
}
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
const dataSource = ds.cloneWithRows(props.eventList);
sections[month].data[day].events.push(event);
};
/**
* Takes a list of events and groups them by month and day.
* Any event that spans multiple days will be split into separate events.
* The list of sections is sorted at the end.
*/
const eventListToSections = (eventList) => {
const sections = {};
for (let i = 0; i < eventList.length; i += 1) {
const start = Moment(eventList[i].start);
const end = Moment(eventList[i].end);
const daySpan = end.diff([
start.year(), start.month(), start.date(),
], 'days') + 1;
if (daySpan === 1) {
addEventToSection(sections, start, eventList[i]);
} else {
// Add start day
addEventToSection(sections, start, {
...eventList[i],
title: `${eventList[i].title} (dag 1/${daySpan})`,
end: null,
});
// Add all intermediate days
for (let j = 2; j < daySpan; j += 1) {
addEventToSection(sections, start.add(1, 'days'), {
...eventList[i],
start: null,
end: null,
title: `${eventList[i].title} (dag ${j}/${daySpan})`,
});
}
// Add end day
addEventToSection(sections, end, {
...eventList[i],
title: `${eventList[i].title} (dag ${daySpan}/${daySpan})`,
start: null,
});
}
}
return Object.keys(sections).sort((a, b) => a - b).map((month) => {
sections[month].data = Object.keys(sections[month].data).sort((a, b) => a - b).map((day) => {
sections[month].data[day].events.sort((a, b) => {
if (a.start == null && b.start == null) {
return 0;
} else if (a.start == null) {
return -1;
} else if (b.start == null) {
return 1;
}
return Moment(a.start).diff(Moment(b.start));
},
);
return sections[month].data[day];
});
return sections[month];
});
};
const renderItem = (item) => {
const { dayNumber, dayOfWeek, events } = item.item;
return (
<View>
<ListView
dataSource={dataSource}
renderRow={rowData => <EventCard event={rowData} />}
enableEmptySections
/>
<View style={styles.day} >
<View style={styles.dateInfo} >
<Text style={styles.dayNumber} >{dayNumber}</Text>
<Text style={styles.dayOfWeek}>{dayOfWeek}</Text>
</View>
<View style={styles.eventList} >
{events.map(
event => <EventCard event={event} key={`${event.pk}:${event.title}`} />,
)}
</View>
</View>
);
};
class Calendar extends Component {
constructor(props) {
super(props);
this.state = {
refreshing: false,
};
}
componentDidMount() {
Moment.locale('nl');
this.handleRefresh();
}
handleRefresh = () => {
this.setState({ refreshing: true });
this.props.retrieveCalendar(this.props.token)
.then(() => this.setState({ refreshing: false }));
};
render() {
if (!this.props.calendarFetched) {
return (
<View>
<Text>
No calendar retrieved!
</Text>
</View>
);
}
return (
<View>
<SectionList
style={styles.sectionList}
renderItem={renderItem}
renderSectionHeader={
itemHeader => <Text style={styles.sectionHeader}>{itemHeader.section.key}</Text>
}
sections={eventListToSections(this.props.eventList)}
keyExtractor={item => item.dayNumber}
stickySectionHeadersEnabled
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
/>
</View>
);
}
}
Calendar.propTypes = {
eventList: React.PropTypes.arrayOf(React.PropTypes.shape({
pk: React.PropTypes.number,
title: React.PropTypes.string,
description: React.PropTypes.string,
start: React.PropTypes.string,
end: React.PropTypes.string,
location: React.PropTypes.string,
price: React.PropTypes.string,
registered: React.PropTypes.bool,
})).isRequired,
calendarFetched: React.PropTypes.bool.isRequired,
retrieveCalendar: React.PropTypes.func.isRequired,
......
import React from 'react';
import { View, Text, Button } from 'react-native';
import { View, Text, TouchableHighlight } from 'react-native';
import { connect } from 'react-redux';
import Moment from 'moment';
import 'moment/locale/nl';
import * as actions from '../actions/events';
import styles from './style/eventCard';
const EventCard = (props) => {
const date = new Date(props.event.start).toISOString().substring(0, 10);
return (
<View>
<Text style={styles.boldText}>{props.event.title}</Text>
<Text>{date}</Text>
<Text style={styles.italicText}>{props.event.description}</Text>
<Text>-----------------------------------------</Text>
<Button title="Openen" onPress={() => props.loadEvent(props.event.pk, props.token)} />
</View>
);
const getEventInfo = (event) => {
Moment.locale('nl');
if (event.start === null && event.end === null) {
return event.location;
} else if (event.start === null) {
return `Tot ${Moment(event.end).format('HH:mm')} | ${event.location}`;
} else if (event.end === null) {
return `Vanaf ${Moment(event.start).format('HH:mm')} | ${event.location}`;
}
return `${Moment(event.start).format('HH:mm')} - ${Moment(event.end).format('HH:mm')} | ${event.location}`;
};
const EventCard = props => (
<TouchableHighlight
onPress={() => props.loadEvent(props.event.pk, props.token)}
style={styles.button}
>
<View style={[styles.card, props.event.registered ? styles.registered : styles.unregistered]}>
<Text style={styles.eventTitle}>{props.event.title}</Text>
<Text style={styles.eventInfo}>{getEventInfo(props.event)}</Text>
</View>
</TouchableHighlight>
);
EventCard.propTypes = {
event: React.PropTypes.shape({
title: React.PropTypes.string,
description: React.PropTypes.string,
start: React.PropTypes.string,
end: React.PropTypes.string,
location: React.PropTypes.string,
price: React.PropTypes.string,
pk: React.PropTypes.number,
registered: React.PropTypes.bool,
}).isRequired,
loadEvent: React.PropTypes.func.isRequired,
token: React.PropTypes.string.isRequired,
......
import { StyleSheet, Dimensions } from 'react-native';
import { TOTAL_BAR_HEIGHT } from './navigator';
import { colors } from '../../style';
const styles = StyleSheet.create({
day: {
flex: 1,
flexDirection: 'row',
},
dateInfo: {
flexDirection: 'column',
alignItems: 'flex-start',
flex: 1,
paddingLeft: 16,
paddingRight: 16,
},
dayNumber: {
fontSize: 28,
},
dayOfWeek: {
fontSize: 16,
},
eventList: {
flex: 7,
},
sectionList: {
backgroundColor: colors.background,
height: Dimensions.get('window').height - TOTAL_BAR_HEIGHT,
},
sectionHeader: {
backgroundColor: colors.background,
fontFamily: 'sans-serif-medium',
fontSize: 20,
color: colors.textColour,
paddingTop: 12,
paddingBottom: 12,
paddingLeft: 16,
},
});
export default styles;
import { StyleSheet } from 'react-native';
import { colors } from '../../style';
const styles = StyleSheet.create({
italicText: {
fontStyle: 'italic',
button: {
marginTop: 8,
marginBottom: 8,
marginLeft: 16,
marginRight: 16,
borderRadius: 2,
},
card: {
padding: 16,
borderRadius: 2,
},
registered: {
backgroundColor: colors.magenta,
},
unregistered: {
backgroundColor: colors.gray,
},
eventTitle: {
fontFamily: 'sans-serif-medium',
color: colors.white,
},
boldText: {
fontWeight: 'bold',
eventInfo: {
fontFamily: 'sans-serif-medium',
color: colors.white,
opacity: 0.8,
},
});
......
......@@ -5,6 +5,8 @@ import { colors } from '../../style';
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
export const TOTAL_BAR_HEIGHT = APPBAR_HEIGHT + 20;
const styles = StyleSheet.create({
statusBar: {
height: STATUSBAR_HEIGHT,
......
......@@ -4,12 +4,13 @@ export const colors = {
magenta: '#E62272',
darkMagenta: '#C2185B',
white: '#FFFFFF',
background: '#FAFAFA',
black: '#000000',
lightGray: '#BBBBBB',
gray: '#616161',
textColour: '#313131',
darkGrey: '#373737',
dividerGrey: 'rgba(0, 0, 0, 0.12)',
background: '#FAFAFA',
transparent: 'transparent',
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment