Reference Source

src/components/views/bikedetails-view.js

import React, { Component } from 'react';
import { StyleSheet, Text, View, Button, PixelRatio, TouchableOpacity, Image, Alert, ScrollView, FlatList, Dimensions } from 'react-native';
import { Icon } from 'react-native-elements';
import ImagePicker from 'react-native-image-picker';
import { HeaderBackButton } from 'react-navigation';
import { TextInput } from 'react-native-paper';

import { styles, text, bikedetails_styles } from './stylesheets/bikedetails-styles';

import BaseView from './view';
import SafeArea from './helpers/safearea';
import HandleBack from './helpers/handleback';
import ImageCarousel from './helpers/imagecarousel';
import BikeDetailsPresenter from '../presenters/bikedetails-presenter';
import TimeUtil from '../../util/timeutility';

/**
 * Class for the bike details view to display information about a bike.
 */
class BikeDetailsView extends BaseView {
	state = {
		data: [],
		photoEntries: [],
		rawData: []
	}

	/**
	 * Set the navigation options, change the header to handle a back button.
	 *
	 * @return {Object} Navigation option
	 */
	static navigationOptions = ({navigation, transitioning}) => {
		const { params = {} } = navigation.state;
		const back = params._onBack ? params._onBack : () => 'default';
		return {
			headerLeft: (<HeaderBackButton disabled={transitioning} onPress={()=>{back()}}/>),
			title: navigation.getParam('title', 'Bike Details')
		}
	}

	/**
	 * Creates an instance of the add bike view
	 *
	 * @constructor
	 * @param {Object} props - Component properties
	 */
	constructor(props) {
		super(props);
		this.BikeDetP = new BikeDetailsPresenter(this);
	}

	/**
	 * Navigates to a certain screen with parameters.
	 * 
	 * @param {string} screen - The screen name to navigate to. Name must be in navigation.js
	 */
	navigate = (screen) => {
		this.props.navigation.navigate({
			routeName: screen,
			params: {
				rawData: this.state.rawData,
				from: 'BikeDetails'
			},
			key: screen + TimeUtil.getDateTime()
		});

	};

	/**
	 * Handles the report found button clicked.
	 */
	_handleClick() {
		this.navigate("ReportFound");
	}

	/**
     * Handles the confirm found button clicked
     */
    _handleClickToConfirm(){
        this.BikeDetP.confirmFound(this.state.rawData,this.alertConfirmCallback);
    }

    /**
     * Handles the reject button clicked
     */
    _handleClickToReject(){
        this.BikeDetP.rejectFound(this.state.rawData,this.alertRejectionCallback);
    }

    /**
     * Function for decision confirm
     */
    decisionConfirm = () => {
    	Alert.alert(
			"Are you sure you want to confirm your bike found?",
			"",
			[
				{ text: "No", onPress: () => {}, style: "cancel" },
				{ text: "Yes", onPress: () => this._handleClickToConfirm() },
			],
			{ cancelable: false },
		);
    }

    /**
     * Function for decision reject
     */
    decisionReject = () => {
    	Alert.alert(
			"Are you sure you want to reject the found bike report?",
			"",
			[
				{ text: "No", onPress: () => {}, style: "cancel" },
				{ text: "Yes", onPress: () => this._handleClickToReject() },
			],
			{ cancelable: false },
		);
    }

    /**
     * Confirm callback on success or failure trying to confirm.
     *
     * @param {Boolean} success - If the found bike confirmation was accepted or not
     */
    alertConfirmCallback = (success) => {
		this.refreshState();
		if (success) {
			Alert.alert(
				"Congratulations, you have found your bike!",
				"",
				[
					{ text: "Ok", onPress: () => this.props.navigation.navigate('Home'), style: "ok" },
				],
				{ cancelable: false },
			);
		} else {
			Alert.alert(
				"Fail to confirm.",
				"Please try again.",
				[
					{ text: "Ok", onPress: () => {}, style: "ok" },
				],
				{ cancelable: false },
			);
		}
	}

	/**
     * Reject callback on success or failure trying to reject.
     *
     * @param {Boolean} success - If the found bike rejection was accepted or not
     */
    alertRejectionCallback = (success) => {
		this.refreshState();
		if (success) {
			Alert.alert(
				"Bike found report rejected.",
				"",
				[
					{ text: "Ok", onPress: () => this.props.navigation.navigate('Home'), style: "ok" },
				],
				{ cancelable: false },
			);
		} else {
			Alert.alert(
				"Fail to confirm.",
				"Please try again.",
				[
					{ text: "Ok", onPress: () => {}, style: "ok" },
				],
				{ cancelable: false },
			);
		}
    }

	/**
	 * Component mounted
	 */
	componentDidMount = () => {
		const { navigation } = this.props;
		// const data = navigation.getParam('data', 'NO-DATA');

		// item = this.sectionedMultiSelect._findItem(data.colour);
		// this.sectionedMultiSelect._toggleItem(item, false);
	}


	/**
	 * Component is about to mount, initialize the data.
	 * This function is called before componentDidMount
	 */
	componentWillMount = () => {
		this.props.navigation.setParams({
			_onBack: this._onBack,
		});

		const { navigation } = this.props;
		let data=[];
    	const id = navigation.getParam('id','NO-DATA');
		const fromPage = navigation.getParam('from', 'Home');
	    if (id ==='NO-DATA'){
		    data = navigation.getParam('data', 'NO-DATA');
		}else{
		    data = this.BikeDetP.getDataFromID(id);
		}

		const { formedData, thumbnail } = this.BikeDetP.translateData(data, fromPage);

		this.setState({
			rawData: data,
			from: fromPage,
			data: formedData,
			photoEntries: thumbnail
		});
	}

	/**
	 * Component will unmount after this method is called, do any clean up here
	 * Call viewUnmounting in base class so it can do any cleanup for the view before calling the presenter destroy method
	 */
	componentWillUnmount = () => {
		this.viewUnmounting(this.BikeDetP);
	}

	/**
	 * When the back button is clicked, check if the user was editing.
	 */
	_onBack = () => {
		this.setState({
			data: [],
			photoEntries: []
		});
		this.props.navigation.navigate(this.state.from);
	}

	/**
	 * Refreshes the state of the component so new data is fetched.
	 */
	refreshState = () => {
		this.setState({ 
			refresh: !this.state.refresh
		});
	}

	/**
	 * Renders a bike detail 
	 *
	 * @param {Object} item - The item of the bike
	 * @return {Component} A react-native component
	 */
	_renderItem = ({item}) => (
		<TextInput
			style={text.textInput}
			label={this._renderText(item.title)}
			value={item.text}
			multiline
			disabled/>
	);

	/**
	 * Renders the text of the label.
	 *
	 * @param {string} text - The text to render
	 * @return {Component} A react-native component
	 */
	_renderText = (text) => (
		<Text style={[{color: 'black'}]}>{text}</Text>
	);

	/**
	 * Extract the key from the item and index
	 */
	_keyExtractor = (item, index) => item.id;

	/**
	 * A callback function if there is a map open error.
	 */
	onMapOpenError = () => {
		Alert.alert(
			"Unable to Open Directions",
			"",
			[
				{ text: "Ok", onPress: () => {}, style: "ok" },
			],
			{ cancelable: false },
		);
	}

	/**
	 * Renders items to the screen
	 *
	 * @return {Component} 
	 */
	render() {
		const { width: windowWidth } = Dimensions.get('window');

		return (
				<HandleBack onBack={this._onBack}>
					<SafeArea/>
						<View style={styles.container}>
							<ScrollView contentContainerStyle={bikedetails_styles.contentContainer}>

							<ImageCarousel 
								photos={this.state.photoEntries} 
								selected={() => 'default'}/>


							{/* List of text inputs */}
							<FlatList
								style={bikedetails_styles.flatList}
								data={this.state.data}
								extraData={this.state}
								keyExtractor={this._keyExtractor}
								renderItem={this._renderItem}/>

							<View>
							<TouchableOpacity
								style={bikedetails_styles.touchableButtons}>
								<Button 
									title='Get Directions'
									onPress={() => {this.BikeDetP.goToDirectionsOnMap(this.state.rawData, this.onMapOpenError)}}/>
							</TouchableOpacity>
							</View>

							{
								this.state.rawData.stolen &&
								<View>
									<TouchableOpacity style={bikedetails_styles.touchableButtons}>
										<Button
											onPress={()=>this._handleClick()}
											title="Report Found"/>
									</TouchableOpacity>
								</View>
							}
							{
                                this.state.rawData.found &&
                            	<View style={{flexDirection: 'row', width: windowWidth, justifyContent: 'space-between', marginTop: 10}}>
	                            	<TouchableOpacity style={[bikedetails_styles.touchableButtons, {width: (windowWidth/2)-15, alignSelf: 'flex-start', marginRight: 5}]}>
	                            		<Button
	                            	  	  	onPress={()=>this.decisionConfirm()}
	                            			title="Confirm Found"/>
	                            	</TouchableOpacity>
	                            	<TouchableOpacity style={[bikedetails_styles.touchableButtons, {width: (windowWidth/2)-15, alignSelf: 'flex-end', marginLeft: 5}]}>
	                            		<Button
	                            	    	onPress={()=>this.decisionReject()}
		                            		title="Reject Found"/>
                            		</TouchableOpacity>
	                            </View>
                            	}

							</ScrollView>
						</View>
					<SafeArea/>
				</HandleBack>
		);
	}

}

export default BikeDetailsView;