diff --git a/app/ui/components/floatingActionButton/FloatingActionButton.js b/app/ui/components/floatingActionButton/FloatingActionButton.js new file mode 100644 index 0000000..d72307b --- /dev/null +++ b/app/ui/components/floatingActionButton/FloatingActionButton.js @@ -0,0 +1,71 @@ +import React from 'react'; +import { FloatingAction } from 'react-native-floating-action'; +import Icon from 'react-native-vector-icons/MaterialIcons'; +import PropTypes from 'prop-types'; +import { Text, View } from 'react-native'; +import Colors from '../../style/Colors'; +import styles from './style/FloatingActionButton'; + +const renderText = (text) => ( + + {text} + +); + +const renderIcon = (iconName) => { + if (iconName) { + return ( + + + + ); + } + return null; +}; + +const renderItem = (item) => ( + + {renderText(item.text)} + {renderIcon(item.iconName)} + +); + +const FloatingActionButton = (props) => { + const actions = props.actions.map((action, index) => ({ + text: action.text, + icon: , + name: index.toString(), + color: Colors.magenta, + buttonSize: 46, + textColor: Colors.white, + textBackground: Colors.magenta, + render: () => renderItem(action), + })); + + const onPress = (index) => { + props.actions[index].onPress(); + }; + + return ( + } + /> + ); +}; + +FloatingActionButton.propTypes = { + iconName: PropTypes.string.isRequired, + actions: PropTypes.arrayOf( + PropTypes.shape({ + iconName: PropTypes.string, + text: PropTypes.string, + onPress: PropTypes.func.isRequired, + }) + ).isRequired, +}; + +export default FloatingActionButton; diff --git a/app/ui/components/floatingActionButton/style/FloatingActionButton.js b/app/ui/components/floatingActionButton/style/FloatingActionButton.js new file mode 100644 index 0000000..1717d0e --- /dev/null +++ b/app/ui/components/floatingActionButton/style/FloatingActionButton.js @@ -0,0 +1,34 @@ +import Colors from '../../../style/Colors'; +import StyleSheet from '../../../style/StyleSheet'; + +const styles = StyleSheet.create({ + floatingActionButtonItem: { + flexDirection: 'row', + alignItems: 'center', + }, + floatingActionButtonItemIcon: { + backgroundColor: Colors.magenta, + width: 46, + height: 46, + marginLeft: 14, + borderRadius: 23, + justifyContent: 'center', + alignItems: 'center', + elevation: 5, + }, + floatingActionButtonItemTextWrapper: { + backgroundColor: Colors.magenta, + paddingHorizontal: 10, + elevation: 5, + borderRadius: 6, + height: 28, + flexDirection: 'row', + alignItems: 'center', + }, + floatingActionButtonItemText: { + color: Colors.white, + fontSize: 16, + }, +}); + +export default styles; diff --git a/app/ui/screens/admin/AdminScreen.js b/app/ui/screens/admin/AdminScreen.js index 332b4aa..7ca16d6 100644 --- a/app/ui/screens/admin/AdminScreen.js +++ b/app/ui/screens/admin/AdminScreen.js @@ -1,15 +1,6 @@ import React, { Component } from 'react'; -import { - FlatList, - RefreshControl, - Switch, - Text, - TouchableHighlight, - View, -} from 'react-native'; +import { FlatList, RefreshControl, Switch, Text, View } from 'react-native'; import PropTypes from 'prop-types'; -import Icon from 'react-native-vector-icons/MaterialIcons'; -import Snackbar from 'react-native-snackbar'; import unorm from 'unorm'; import styles from './style/AdminScreen'; @@ -17,15 +8,21 @@ import Colors from '../../style/Colors'; import SearchHeader from '../../components/searchHeader/SearchHeaderConnector'; import Button from '../../components/button/Button'; +import FloatingActionButton from '../../components/floatingActionButton/FloatingActionButton'; class AdminScreen extends Component { + actions = this.props.filterTypes.map((filterType) => ({ + text: filterType.label, + onPress: () => this.updateFilter(filterType.checkItem), + })); + constructor(props) { super(props); this.state = { items: {}, searchKey: '', - currentFilter: 0, + currentFilter: props.filterTypes[0].checkItem, }; for (let i = 0; i < this.props.items.length; i += 1) { @@ -76,12 +73,11 @@ class AdminScreen extends Component { }; applyFilter = (keys) => { - const { filterTypes } = this.props; const { currentFilter } = this.state; return keys .filter(this.containsSearchKey) - .filter((pk) => filterTypes[currentFilter].checkItem(this.state.items[pk])); + .filter((pk) => currentFilter(this.state.items[pk])); }; cleanSearchTerm = (term) => @@ -104,14 +100,10 @@ class AdminScreen extends Component { return 0; }; - updateFilter = () => { + updateFilter = (newFilter) => { const { currentFilter } = this.state; - const { filterTypes } = this.props; - - const newFilter = (currentFilter + 1) % filterTypes.length; if (newFilter !== currentFilter) { - Snackbar.show({ text: filterTypes[newFilter].label }); this.setState({ currentFilter: newFilter }); } }; @@ -183,11 +175,7 @@ class AdminScreen extends Component { ); const filterButton = this.props.filterTypes.length > 1 && ( - - - - - + ); if (keys.length === 0) { diff --git a/app/ui/screens/admin/style/AdminScreen.js b/app/ui/screens/admin/style/AdminScreen.js index 4fc4649..a205e0a 100644 --- a/app/ui/screens/admin/style/AdminScreen.js +++ b/app/ui/screens/admin/style/AdminScreen.js @@ -63,29 +63,6 @@ const styles = StyleSheet.create({ selected: { backgroundColor: Colors.magenta, }, - filterButton: { - position: 'absolute', - bottom: 16, - right: 16, - borderRadius: 28, - overflow: 'hidden', - backgroundColor: Colors.grey, - android: { - elevation: 4, - }, - ios: { - borderColor: Colors.lightGray, - borderStyle: 'solid', - borderWidth: 0.5, - }, - }, - filterButtonWrapper: { - width: 56, - height: 56, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: Colors.magenta, - }, noResultsMessage: { fontSize: 18, padding: 16, diff --git a/app/ui/screens/events/CalendarScreen.js b/app/ui/screens/events/CalendarScreen.js index 8eb3f03..1b345d2 100644 --- a/app/ui/screens/events/CalendarScreen.js +++ b/app/ui/screens/events/CalendarScreen.js @@ -9,6 +9,13 @@ import ErrorScreen from '../../components/errorScreen/ErrorScreen'; import styles from './style/CalendarScreen'; import SearchHeader from '../../components/searchHeader/SearchHeaderConnector'; import DismissKeyboardView from '../../components/dismissKeyboardView/DismissKeyboardView'; +import FloatingActionButton from '../../components/floatingActionButton/FloatingActionButton'; + +const filters = { + all: () => true, + registered: (item) => item.registered, + open: (item) => item.registration_allowed, +}; /* eslint no-param-reassign: ["error", { "props": false }] */ const addEventToSection = (sections, date, event) => { @@ -119,6 +126,31 @@ const renderItem = (item) => { }; class CalendarScreen extends Component { + actions = [ + { + text: 'All', + iconName: 'reorder', + onPress: () => this.updateFilter(filters.all), + }, + { + text: 'Your registrations', + iconName: 'account-circle', + onPress: () => this.updateFilter(filters.registered), + }, + { + text: 'Open registrations', + iconName: 'event-available', + onPress: () => this.updateFilter(filters.open), + }, + ]; + + constructor(props) { + super(props); + this.state = { + activeFilter: filters.all, + }; + } + componentDidMount() { const { keywords } = this.props; this.props.events(keywords); @@ -136,7 +168,22 @@ class CalendarScreen extends Component { }, 500); }; + filteredEvents = () => { + const { activeFilter } = this.state; + const { eventList } = this.props; + + return eventList.filter(activeFilter); + }; + + updateFilter = (filter) => { + this.setState({ + activeFilter: filter, + }); + }; + render() { + const items = this.filteredEvents(); + const header = ( ( {itemHeader.section.key} )} - sections={eventListToSections(this.props.eventList)} + sections={eventListToSections(items)} keyExtractor={(item) => item.dayNumber} stickySectionHeadersEnabled onRefresh={this.handleRefresh} @@ -199,6 +246,7 @@ class CalendarScreen extends Component { {content} + ); } @@ -217,6 +265,7 @@ CalendarScreen.propTypes = { start: PropTypes.string, end: PropTypes.string, location: PropTypes.string, + registration_allowed: PropTypes.bool.isRequired, price: PropTypes.string, registered: PropTypes.bool, pizza: PropTypes.bool, diff --git a/app/ui/screens/events/EventAdminScreen.js b/app/ui/screens/events/EventAdminScreen.js index 24e6c6f..5e7483a 100644 --- a/app/ui/screens/events/EventAdminScreen.js +++ b/app/ui/screens/events/EventAdminScreen.js @@ -50,15 +50,15 @@ class EventAdminScreen extends Component { const filterTypes = [ { - label: 'Disabled filter', + label: 'Show all', checkItem: () => true, }, { - label: 'Filtering on payment', + label: 'Hide paid', checkItem: (item) => item.select.value === PAYMENT_TYPES.NONE, }, { - label: 'Filtering on presence', + label: 'Hide present', checkItem: (item) => !item.checkbox, }, ]; diff --git a/app/ui/screens/pizza/PizzaAdminScreen.js b/app/ui/screens/pizza/PizzaAdminScreen.js index b369aa1..bce51d9 100644 --- a/app/ui/screens/pizza/PizzaAdminScreen.js +++ b/app/ui/screens/pizza/PizzaAdminScreen.js @@ -48,11 +48,11 @@ class PizzaAdminScreen extends Component { const filterTypes = [ { - label: 'Disabled filter', + label: 'Show all', checkItem: () => true, }, { - label: 'Filtering on payment', + label: 'Hide paid', checkItem: (item) => item.select.value === PAYMENT_TYPES.NONE, }, ]; diff --git a/package.json b/package.json index 900b5cb..691defa 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "/__tests__/setup.js" ], "transformIgnorePatterns": [ - "node_modules/(?!(react-native|@react-native-community|react-navigation|react-navigation-drawer|react-navigation-stack|@sentry|react-native-gesture-handler|react-native-vector-icons|react-native-linear-gradient|@react-native-firebase|react-native-image-crop-picker|react-native-reanimated|react-native-iphone-x-helper|react-native-render-html|react-native-webview|react-native-share|react-native-image-zoom-viewer|react-native-image-pan-zoom|react-native-add-calendar-event)/)" + "node_modules/(?!(react-native|@react-native-community|react-navigation|react-navigation-drawer|react-navigation-stack|@sentry|react-native-gesture-handler|react-native-vector-icons|react-native-linear-gradient|@react-native-firebase|react-native-image-crop-picker|react-native-reanimated|react-native-iphone-x-helper|react-native-render-html|react-native-webview|react-native-share|react-native-image-zoom-viewer|react-native-image-pan-zoom|react-native-add-calendar-event|react-native-floating-action)/)" ] }, "prettier": { @@ -67,6 +67,7 @@ "react-native-cli": "^2.0.1", "react-native-device-info": "^7.0.2", "react-native-dotenv": "^2.4.2", + "react-native-floating-action": "^1.21.0", "react-native-fs": "^2.16.6", "react-native-gesture-handler": "^1.8.0", "react-native-image-crop-picker": "^0.35.1", @@ -75,8 +76,8 @@ "react-native-reanimated": "^1.13.1", "react-native-render-html": "^4.2.4", "react-native-safe-area-context": "^3.1.8", - "react-native-share": "^4.0.4", "react-native-screens": "^2.12.0", + "react-native-share": "^4.0.4", "react-native-snackbar": "^2.2.3", "react-native-vector-icons": "^7.1.0", "react-native-webview": "^10.10.0", diff --git a/yarn.lock b/yarn.lock index 1bb7cfa..e6f5dc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7467,6 +7467,11 @@ react-native-dotenv@^2.4.2: dependencies: dotenv "^8.0.0" +react-native-floating-action@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/react-native-floating-action/-/react-native-floating-action-1.21.0.tgz#cb1e8349fec5b9aed06c8053c07372822c64b22f" + integrity sha512-ozd/iaJunrEYc4eGUWkP+4UI8yoIL0nQsToI3hnJRste/3LPhBkyu0eG3RTjc6aFY+q++JpRmgO469Ck8jJOwg== + react-native-fs@^2.16.6: version "2.16.6" resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.16.6.tgz#2901789a43210a35a0ef0a098019bbef3af395fd"