diff --git a/app/screens/FindMachine.js b/app/screens/FindMachine.js index d4d2ca55..50710a85 100644 --- a/app/screens/FindMachine.js +++ b/app/screens/FindMachine.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useContext, useEffect, useState } from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import { @@ -10,7 +10,9 @@ import { StyleSheet, TextInput, View, + TouchableOpacity, } from "react-native"; +import { useNavigation, useRoute } from "@react-navigation/native"; import { ThemeContext } from "../theme-context"; import { KeyboardAwareScrollView } from "react-native-keyboard-controller"; import MaterialIcons from "@expo/vector-icons/MaterialIcons"; @@ -30,8 +32,9 @@ import { WarningButton, } from "../components"; import Checkbox from "expo-checkbox"; - import { alphaSortNameObj } from "../utils/utilityFunctions"; +import { FontAwesome6 } from "@expo/vector-icons"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; let deviceWidth = Dimensions.get("window").width; @@ -46,89 +49,83 @@ const getDisplayText = (machine, theme) => ( ); -class MultiSelectRow extends React.PureComponent { - static contextType = ThemeContext; - - _onPress = () => { - this.props.onPressItem(this.props.machine); - }; - - render() { - const { index, machine, selected } = this.props; - const theme = this.context.theme; - const backgroundColor = index % 2 === 0 ? theme.base1 : theme.base2; - - return ( - [ - { - display: "flex", - flexDirection: "row", - alignItems: "center", - padding: 8, - justifyContent: "space-between", - }, - pressed - ? { backgroundColor: theme.base4, opacity: 0.8 } - : { backgroundColor, opacity: 1 }, - ]} - > - - {getDisplayText(machine, theme)} - - {selected ? ( - - ) : null} - - ); - } -} - -MultiSelectRow.propTypes = { - onPressItem: PropTypes.func, - machine: PropTypes.object, - selected: PropTypes.bool, - index: PropTypes.number, +const MultiSelectRow = ({ index, machine, selected, onPressItem }) => { + const { theme } = useContext(ThemeContext); + const backgroundColor = index % 2 === 0 ? theme.base1 : theme.base2; + + const _onPress = () => onPressItem(machine); + + return ( + [ + { + display: "flex", + flexDirection: "row", + alignItems: "center", + padding: 8, + justifyContent: "space-between", + }, + pressed + ? { backgroundColor: theme.base4, opacity: 0.8 } + : { backgroundColor, opacity: 1 }, + ]} + > + + {getDisplayText(machine, theme)} + + {selected ? ( + + ) : null} + + ); }; -class FindMachine extends React.PureComponent { - constructor(props) { - super(props); - const sortedMachines = alphaSortNameObj(props.machines.machines); - - this.state = { - machines: sortedMachines, - allMachines: sortedMachines, - query: "", - showModal: false, - machine: {}, - condition: "", - machineList: props.location.machineList, - machinesInView: false, - ic_enabled: undefined, - }; - } - - componentDidMount() { - this.props.navigation.setOptions({ - title: this.props.route.params?.machineFilter +function FindMachine(props) { + const { + location, + machines, + mapLocations, + addMachineToLocation, + setMachineFilter, + } = props; + + const navigation = useNavigation(); + const route = useRoute(); + const { theme } = useContext(ThemeContext); + const insets = useSafeAreaInsets(); + + const sortedMachines = alphaSortNameObj(machines.machines); + const [list, setList] = useState(sortedMachines); + const [allMachines] = useState(sortedMachines); + const [query, setQuery] = useState(""); + const [showModal, setShowModal] = useState(false); + const [machine, setMachine] = useState({}); + const [condition, setCondition] = useState(""); + const [machineList, setMachineList] = useState(location.machineList || []); + const [machinesInView, setMachinesInView] = useState(false); + const [ic_enabled, setIcEnabled] = useState(undefined); + const [refresh, setRefresh] = useState(false); + + useEffect(() => { + setMachineList(location.machineList || []); + }, [location.machineList]); + + useEffect(() => { + navigation.setOptions({ + title: route.params?.machineFilter ? "Select Machine to Filter" - : `Select Machine${this.props.route.params?.multiSelect ? "s" : ""}`, + : `Select Machine${route.params?.multiSelect ? "s" : ""}`, headerRight: () => - this.props.route.params?.showDone ? ( - this.props.navigation.goBack(null)}> + route.params?.showDone ? ( + navigation.goBack(null)}> {({ pressed }) => ( - + ) : null, }); - } - - componentDidUpdate() { - this.props.navigation.setOptions({ - headerRight: () => - this.props.route.params?.showDone ? ( - this.props.navigation.goBack(null)}> - {({ pressed }) => ( - - - - )} - - ) : null, - }); - } - - static contextType = ThemeContext; - - handleSearch = (query, machinesInView) => { - const formattedQuery = query.toLowerCase(); - - if (machinesInView) { - const machinesInView = this.props.mapLocations.reduce((machines, loc) => { - loc.machine_ids && - loc.machine_ids.map((machineId) => { - if (machines.indexOf(machineId) === -1) machines.push(machineId); - }); - - return machines; + }, [ + navigation, + route.params?.machineFilter, + route.params?.multiSelect, + route.params?.showDone, + ]); + + useEffect(() => { + navigation.setParams?.({ showDone: (machineList?.length || 0) > 0 }); + }, [machineList, navigation]); + + const toArray = (maybeArrayOrObject) => + Array.isArray(maybeArrayOrObject) + ? maybeArrayOrObject + : Object.values(maybeArrayOrObject || {}); + + const handleSearch = (q, wantInView) => { + const formatted = q.toLowerCase(); + + if (wantInView) { + const idsInView = toArray(mapLocations).reduce((ids, loc) => { + (loc?.machine_ids || []).forEach((id) => { + if (!ids.includes(id)) ids.push(id); + }); + return ids; }, []); - const curMachines = this.state.allMachines.filter( - (mach) => machinesInView.indexOf(mach.id) > -1, - ); - const machines = curMachines.filter((m) => - m.name.toLowerCase().includes(formattedQuery), + const cur = allMachines.filter((m) => idsInView.includes(m.id)); + const filtered = cur.filter((m) => + m.name.toLowerCase().includes(formatted), ); - this.setState({ query, machines }); + setQuery(q); + setList(filtered); } else { - const machines = this.state.allMachines.filter((m) => - m.name.toLowerCase().includes(formattedQuery), + const filtered = allMachines.filter((m) => + m.name.toLowerCase().includes(formatted), ); - this.setState({ query, machines }); + setQuery(q); + setList(filtered); } }; - handleClear = () => { - this.setState({ query: "" }); + const handleClear = () => { + setQuery(""); + setList(allMachines); }; - toggleViewMachinesInMapArea = (idx) => { - if (idx === 0 && !!this.state.machinesInView) { - this.handleSearch(this.state.query, false); - this.setState({ machinesInView: false }); - } else if (idx === 1 && !!!this.state.machinesInView) { - this.handleSearch(this.state.query, true); - this.setState({ machinesInView: true }); + const toggleViewMachinesInMapArea = (idx) => { + if (idx === 0 && machinesInView) { + handleSearch(query, false); + setMachinesInView(false); + } else if (idx === 1 && !machinesInView) { + handleSearch(query, true); + setMachinesInView(true); } }; - setSelected = (machine) => { - if (this.props.route.params?.machineFilter) { - this.props.setMachineFilter(machine); - this.props.navigation.goBack(); + const setSelected = (m) => { + if (route.params?.machineFilter) { + setMachineFilter(m); + navigation.goBack(); } else { - this.setState({ - showModal: true, - machine, - }); + setShowModal(true); + setMachine(m); } }; - addMachine = () => { - this.props.addMachineToLocation( - this.state.machine, - this.state.condition, - this.state.ic_enabled, - ); - this.setState({ showModal: false }); - this.props.navigation.goBack(); + const addMachineAndClose = () => { + addMachineToLocation(machine, condition, ic_enabled); + setShowModal(false); + navigation.goBack(); }; - cancelAddMachine = () => { - this.setState({ - showModal: false, - machine: {}, - condition: "", - }); + const returnToMachineSelection = () => { + setShowModal(false); + setMachine({}); + setCondition(""); + }; + + const returnToLocationDetails = () => { + setShowModal(false); + setMachine({}); + setCondition(""); + navigation.goBack(); + }; + + const onPressMultiSelect = (m) => { + const selected = machineList.find((x) => x.id === m.id); + if (selected) { + removeMachineFromList(m); + } else { + addMachineToList(m); + } + setRefresh((r) => !r); }; - renderRow = ({ item, index }) => { - const theme = this.context.theme; + const keyExtractor = (m) => `${m.id}`; + + const onIcEnabledPressed = (val) => { + if (ic_enabled === val) setIcEnabled(undefined); + else setIcEnabled(val); + }; + + const multiSelect = !!route.params?.multiSelect; + const isFiltering = !!route.params?.machineFilter; + const selectedIdx = machinesInView ? 1 : 0; + const s = getStyles(theme); + const keyboardDismissProp = + Platform.OS === "ios" + ? { keyboardDismissMode: "on-drag" } + : { onScrollBeginDrag: Keyboard.dismiss }; + + const { opdb_img, opdb_img_height, opdb_img_width } = machine; + const opdb_resized = (opdb_img_width || 0) - (deviceWidth - 48); + const opdb_img_height_calc = + (deviceWidth - 48) * ((opdb_img_height || 0) / (opdb_img_width || 1)); + const opdbImgHeight = + opdb_resized > 0 ? opdb_img_height_calc : opdb_img_height; + const opdbImgWidth = opdb_resized > 0 ? deviceWidth - 48 : opdb_img_width; + + const renderRow = ({ item, index }) => { const backgroundColor = index % 2 === 0 ? theme.base1 : theme.base2; return ( - this.setSelected(item)}> + setSelected(item)}> {({ pressed }) => ( ( + const renderMultiSelectRow = ({ item, index }) => ( m.id === item.id)} + onPressItem={onPressMultiSelect} + selected={!!(location.machineList || []).find((m) => m.id === item.id)} index={index} /> ); - onPressMultiSelect = (machine) => { - const selected = !!this.props.location.machineList.find( - (m) => m.id === machine.id, - ); - - if (selected) { - this.props.removeMachineFromList(machine); - this.setState({ - refresh: !this.state.refresh, - }); - } else { - this.props.addMachineToList(machine); - this.setState({ - refresh: !this.state.refresh, - }); - } - }; - - keyExtractor = (machine) => `${machine.id}`; - - onIcEnabledPressed = (ic_enabled) => { - const prevState = this.state.ic_enabled; - // uncheck if pressing the currently checked box - if ( - (!!prevState && !!ic_enabled) || - (prevState === false && ic_enabled === false) - ) { - return this.setState({ ic_enabled: undefined }); - } - - this.setState({ ic_enabled }); - }; - - UNSAFE_componentWillReceiveProps(props) { - if ( - this.props.location.machineList.length === 0 && - props.location.machineList.length > 0 - ) - this.props.navigation.setParams({ showDone: true }); - - if ( - this.props.location.machineList.length > 0 && - props.location.machineList.length === 0 - ) - this.props.navigation.setParams({ showDone: false }); - } - - render() { - const { machineList = [] } = this.props.location; - const multiSelect = - (this.props.route.params && this.props.route.params["multiSelect"]) || - false; - const isFiltering = this.props.route.params?.machineFilter; - const selectedIdx = this.state.machinesInView ? 1 : 0; - const theme = this.context.theme; - const s = getStyles(theme); - const keyboardDismissProp = - Platform.OS === "ios" - ? { keyboardDismissMode: "on-drag" } - : { onScrollBeginDrag: Keyboard.dismiss }; - const { opdb_img, opdb_img_height, opdb_img_width } = this.state.machine; - const opdb_resized = opdb_img_width - (deviceWidth - 48); - const opdb_img_height_calc = - (deviceWidth - 48) * (opdb_img_height / opdb_img_width); - const opdbImgHeight = - opdb_resized > 0 ? opdb_img_height_calc : opdb_img_height; - const opdbImgWidth = opdb_resized > 0 ? deviceWidth - 48 : opdb_img_width; - - return ( - <> - {}} - transparent={false} - statusBarTranslucent={true} - navigationBarTranslucent={true} - > - - - + return ( + <> + {}} + transparent={false} + statusBarTranslucent + navigationBarTranslucent + > + + + + + returnToMachineSelection()} + style={s.backButton} + activeOpacity={0.5} + > + + - Add{" "} - - {this.state.machine.name} - {" "} - to{" "} - - {this.props.location.location.name} - + Add {machine.name} to{" "} + {location.name} - {!!opdb_img && ( - - )} - this.setState({ condition })} - textAlignVertical="top" - underlineColorAndroid="transparent" + + + {!!opdb_img && ( + - {this.state.machine.ic_eligible && ( - - - Does machine have Stern Insider Connected enabled? - - - this.onIcEnabledPressed(true)} - color={theme.purple} - style={s.checkStyle} - /> - - Yes - - this.onIcEnabledPressed(false)} - color={theme.purple} - style={s.checkStyle} - /> - No - + )} + + + + {machine.ic_eligible && ( + + + Does machine have Stern Insider Connected enabled? + + + onIcEnabledPressed(true)} + color={theme.purple} + style={s.checkStyle} + /> + Yes + onIcEnabledPressed(false)} + color={theme.purple} + style={s.checkStyle} + /> + No - )} - - - - - - - - - - - this.handleSearch(query, this.state.machinesInView) - } - value={this.state.query} - style={s.inputStyle} - autoCorrect={false} + + )} + + + + + + + + + + + + handleSearch(q, machinesInView)} + value={query} + style={s.inputStyle} + autoCorrect={false} + /> + + {query.length > 0 && ( + + - - {this.state.query.length > 0 && ( - - - + + )} + + + {isFiltering ? ( + + + + ) : null} + + {multiSelect ? ( + + {machineList.length === 0 ? ( + 0 machines selected + ) : ( + + + {`${machineList.length} machine${machineList.length > 1 ? "s" : ""} selected`} + + )} - {isFiltering ? ( - - - - ) : null} - {multiSelect ? ( - - {machineList.length === 0 ? ( - 0 machines selected - ) : ( - - {`${ - machineList.length - } machine${machineList.length > 1 ? "s" : ""} selected`} - - )} - - ) : null} - - - ); - } + ) : null} + + + + ); } const getStyles = (theme) => @@ -530,13 +488,24 @@ const getStyles = (theme) => fontSize: 18, fontFamily: "Nunito-Regular", }, - + headerContainer: { + flexDirection: "row", + alignItems: "center", + position: "relative", + justifyContent: "center", + }, + backButton: { + position: "absolute", + left: 0, + top: 0, + bottom: 0, + justifyContent: "center", + }, textInput: { backgroundColor: theme.white, borderColor: theme.theme == "dark" ? theme.base4 : theme.indigo4, borderWidth: 1, marginHorizontal: 30, - marginTop: 5, marginBottom: 10, borderRadius: 10, fontFamily: "Nunito-Regular", @@ -546,7 +515,7 @@ const getStyles = (theme) => verticalAlign: { flexDirection: "column", justifyContent: "top", - marginTop: 80, + marginTop: 60, marginBottom: 40, }, multiSelect: { @@ -605,7 +574,7 @@ const getStyles = (theme) => modalTitle: { textAlign: "center", marginHorizontal: 40, - marginBottom: 15, + marginVertical: 20, fontSize: 18, fontFamily: "Nunito-Regular", }, @@ -648,6 +617,7 @@ const mapStateToProps = ({ location, machines, locations }) => ({ machines, mapLocations: locations.mapLocations || {}, }); + const mapDispatchToProps = (dispatch) => ({ addMachineToLocation: (machine, condition, ic_enabled) => dispatch(addMachineToLocation(machine, condition, ic_enabled)), @@ -655,4 +625,5 @@ const mapDispatchToProps = (dispatch) => ({ removeMachineFromList: (machine) => dispatch(removeMachineFromList(machine)), setMachineFilter: (machine) => dispatch(setMachineFilter(machine)), }); + export default connect(mapStateToProps, mapDispatchToProps)(FindMachine);