burntab
unknown
plain_text
a year ago
25 kB
5
Indexable
import React, { useEffect, useRef, useState } from "react" import { useFocusEffect, useNavigation } from "@react-navigation/native" import { View, Platform, FlatList, TouchableOpacity, Text, StyleSheet, PermissionsAndroid, } from "react-native" import Geolocation from 'react-native-geolocation-service'; import MapView, { Polyline } from "react-native-maps" import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons"; import moment from "moment"; import haversine from 'haversine'; import HistoryIcon from '../../assets/Icons/stats-burn/history.svg' import InviteIcon from '../../assets/Icons/stats-burn/profile-add-yellow.svg' import LocationIcon from '../../assets/Icons/stats-burn/location-map.svg' import PauseIcon from '../../assets/Icons/stats-burn/pause.svg' import ResumeIcon from '../../assets/Icons/stats-burn/resume.svg' import RoutingIcon from '../../assets/Icons/stats-burn/routing.svg' import StopIcon from '../../assets/Icons/stats-burn/stop.svg' import { SheetModal } from "../common/SheetModal" import { StartButton } from "../common/StartButton" import { StatsItem } from "./StatsItem" import { StepsHistoryCard } from "../common/StepsHistoryCard" import { BurnTrackers } from "./BurnTrackers"; import { colors } from "../../theme" import { endBurnTracking, getBurnTrackingData, startBurnTracking } from "../../api"; import fonts from "../../assets/fonts" import routes from "../../routes/routes" import { storeCurrentTrackerData } from "../../redux/actions/stats"; import { useDispatch, useSelector } from "react-redux" import useTimer from "../../helpers/useTimer"; import { LatLngTypes, height, width } from "../../constants/constants" import getDirections from "../../utils/getDirections"; import useHardwareBackButton from "../../utils/backHandler"; export const BurnTab = () => { interface HistoryItem { id: number; date: string; stepsCount: number; increase: boolean; } interface RenderHistoryItemsProps { item: HistoryItem; } const { currentTracker } = useSelector(store => store?.stats) const historyData = currentTracker?.trackerData?.chart?.day const navigation = useNavigation() const LATITUDE = 23.2418; const LONGITUDE = 77.3797; const LATITUDE_DELTA = 0.02; const ASPECT_RATIO = width / height; const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; const dispatch = useDispatch() const mapRef = useRef(null); const { timer, handleStart, handlePause, handleResume, handleReset } = useTimer(0); const [historyModal, setHistoryModal] = useState<boolean>(false) const [inviteModal, setInviteModal] = useState<boolean>(false) const [lat, setLat] = useState<number>(23.2418); const [lon, setLon] = useState<number>(77.3797); const [isPause, setIsPause] = useState(false); const [startWatchPosition, setStartWatchPosition] = useState(false); const [userStartTime, setUserStartTime] = useState(''); const [isStart, setIsStart] = useState<boolean>(false); const [isMapLoaded, setIsMapLoaded] = useState(false); const [polyLineCoords, setPolyLineCoords] = useState<LatLngTypes[]>([ { latitude: LATITUDE, longitude: LONGITUDE }, ]); const [userInitialPosition, setUserInitialPosition] = useState<LatLngTypes>(); const [animatedCount, setShowAnimatedCount] = useState(false); const [timeLeft, setTimeLeft] = useState<number | null>(null); const [printTime, setPrintTime] = useState<number | string | null>(null); const [location, setLocation] = useState<{ speed: number; stepsWalked: number; latitude: number; longitude: number; distanceTravelled: number; prevLatLng: {}; }>({ speed: 0, stepsWalked: 0, latitude: LATITUDE, longitude: LONGITUDE, distanceTravelled: 0, prevLatLng: {}, }); // time format const formatTime = (time: any) => { const getSeconds = `0${time % 60}`.slice(-2); const minutes: any = `${Math.floor(time / 60)}`; const getMinutes = `0${minutes % 60}`.slice(-2); const getHours = `0${Math.floor(time / 3600)}`.slice(-2); return `${getMinutes} : ${getSeconds}`; }; // walk pause const onPause = async () => { setIsPause(true); handlePause(); setStartWatchPosition(false); }; // resume const handleResumeWalk = () => { setIsPause(false); handleResume(); setStartWatchPosition(true); }; // finish the walk const handleFinishWalk = async () => { handleReset(); let formData = new FormData() formData.append('lat', location?.latitude) formData.append('long', location?.longitude) formData.append('speed', location?.speed) formData.append('calories_burn', 0) formData.append('steps', location?.stepsWalked) formData.append('distance', location?.distanceTravelled) formData.append('polyline_coords', JSON.stringify(polyLineCoords)) endBurnTracking(formData).then(value => { if (value?.data) { let paramsObj = { current_date: moment().format('YYYY-MM-DD') } getBurnTrackingData(paramsObj).then(trackingData => { if (trackingData?.data) { const valuesData = trackingData?.data?.data?.steps_chart?.day?.map(entry => entry.value) const titlesData = trackingData?.data?.data?.steps_chart?.day?.map(entry => moment(entry.title).format('MMM-DD')) const modifiedTitles = [titlesData[0]] for (let i = 1; i < titlesData.length; i++) { const day = parseInt(titlesData[i].split("-")[1]); modifiedTitles.push(day); } dispatch(storeCurrentTrackerData({ titlesData: modifiedTitles, valuesData: valuesData, trackerType: 'steps', trackerData: { chart: { ...trackingData?.data?.data?.steps_chart }, data: { ...trackingData?.data?.data?.burn_tracking } } })) navigation.navigate(routes.trackerHistoryScreen, {origin:'burn-session',selectedTracker:'steps'}) } }) } }).catch(err => console.log(err)) setStartWatchPosition(false); }; const renderStatsItem = ({ item }) => ( <StatsItem title={item?.title} value={item?.value} unit={item?.unit} leading /> ) const renderHistoryCards: React.FC<RenderHistoryItemsProps> = ({ item }) => ( <StepsHistoryCard title={'Steps'} date={item?.title} stepsCount={item?.value} increase={item?.value > 0} timer={item?.time} icon={<MaterialCommunityIcons name="shoe-print" color={colors.purple} size={20} />} />) const InviteFriendButton = ({ onpress, requests, }) => { return ( <TouchableOpacity style={styles.inviteButtonStyles} onPress={onpress}> <View style={styles.notificationIndicator}> <Text style={styles.notificationCountText}>{requests}</Text> </View> <InviteIcon /> <Text style={{ ...styles.fs12ppReg666, color: colors.white }}>Invite Friend</Text> </TouchableOpacity> ) } const requestLocationPermission = async () => { if (Platform.OS === 'ios') { const auth = await Geolocation.requestAuthorization('whenInUse'); if (auth === 'granted') { return true; } return false; } if (Platform.OS === 'android') { try { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, { title: 'Location Access Required', message: 'App Require to Access your location', buttonPositive: 'OK', }, ); return granted === PermissionsAndroid.RESULTS.GRANTED; } catch (err) { return false; } } else { return true; } }; const calcUserDistance = (newLatLng: object) => haversine(userInitialPosition, newLatLng, { unit: 'km' }) || 0; // get current lat and long const getLocation = async () => { const isLocationPermited = await requestLocationPermission(); if (isLocationPermited) { Geolocation.getCurrentPosition( position => { const { latitude, longitude } = position.coords; const newCoordinate = { latitude, longitude, }; setLocation({ ...location, latitude, longitude, }); setUserInitialPosition(newCoordinate); setUserStartTime(moment()?.toString()); }, error => { }, { enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }, ); } }; useFocusEffect( React.useCallback(() => { getLocation(); }, []), ); // count animation useEffect(() => { if (timeLeft === 0) { setIsStart(true); handleStart(); setTimeLeft(null); setPrintTime(null); setStartWatchPosition(true); } else if (timeLeft === 1) { setPrintTime('GO'); } else { setPrintTime(timeLeft); } if (!timeLeft) return; const intervalId = setInterval(() => { setTimeLeft(timeLeft - 1); }, 1000); return () => clearInterval(intervalId); }, [timeLeft]); useEffect(() => { if (startWatchPosition) { Geolocation.getCurrentPosition(position => { const { latitude, longitude } = position.coords; // setUserInitialPosition({ latitude, longitude }); // for future use setPolyLineCoords([{ latitude, longitude }]); let dataObj = { lat: latitude, long: longitude } startBurnTracking(dataObj).then(val => {}) }); } else { // setUserInitialPosition({ latitude: LATITUDE, longitude: LONGITUDE }); } const watchId = Geolocation.watchPosition( async position => { const { latitude, longitude, speed } = position.coords; if (startWatchPosition && speed > 0) { try { const { coords, stepCount } = await getDirections({ startLoc: `${userInitialPosition?.latitude},${userInitialPosition?.longitude}`, destinationLoc: `${latitude},${longitude}`, }); setPolyLineCoords(prevState => [...prevState, ...coords]); const newCoordinate = { latitude, longitude, }; setLocation(prevState => ({ ...prevState, latitude, longitude, speed, stepsWalked: prevState.stepsWalked + stepCount, prevLatLng: newCoordinate, distanceTravelled: calcUserDistance(newCoordinate), })); } catch (error) { // add logic to log error message } } }, err => { // add logic to log error message }, { enableHighAccuracy: true, distanceFilter: 1, }, ); return () => { Geolocation.clearWatch(watchId); }; }, [startWatchPosition, dispatch]); //BURN TAB - STATS DATA\\ const mapStatsData = [ { id: 1, title: 'Steps', value: location?.stepsWalked }, { id: 2, title: 'Time', value: formatTime(timer) }, { id: 3, title: 'Speed', value: location?.speed?.toFixed(3), unit: 'Kmph' }, { id: 4, title: 'Distance', value: location?.distanceTravelled?.toFixed(3), unit: 'km' }, { id: 5, title: 'Calories', value: 0, // may come from watch apis unit: 'Kcal' }, { id: 6, title: 'Heart Rate', value: 0, // may come from watch apis unit: 'BPM' }, ] return ( <> {animatedCount && timeLeft !== null && ( <View style={styles.animatedCountContainer}> <View> <Text style={styles.animatedCountText}> {printTime === 'GO' ? printTime : `0${printTime}`} </Text> </View> </View> )} <MapView initialRegion={{ latitude: lat, longitude: lon, latitudeDelta: 0.0922, longitudeDelta: 0.0421, }} loadingEnabled onMapLoaded={() => setIsMapLoaded(true)} ref={mapRef} style={styles.mapStyles} showsUserLocation showsMyLocationButton={false} followsUserLocation region={{ latitude: location.latitude, longitude: location.longitude, latitudeDelta: LATITUDE_DELTA, longitudeDelta: LONGITUDE_DELTA, }}> <Polyline coordinates={polyLineCoords} strokeColor="#7e81e9" strokeWidth={4} /> </MapView> <TouchableOpacity style={styles.locationIcon} onPress={() => { mapRef.current.animateToRegion({ latitude: location?.latitude, longitude: location?.longitude, latitudeDelta: LATITUDE_DELTA, longitudeDelta: LONGITUDE_DELTA, }); }}> <LocationIcon /> </TouchableOpacity> <View style={styles.burnBottomContainer}> {/*INITIAL LAUNCH TABS*/} {!isStart ? <> <View style={styles.p16}> <BurnTrackers /> </View> <View style={{ ...styles.rowCenterApart, width: '100%', padding: 16, marginBottom: 10 }}> <TouchableOpacity style={{ ...styles.burnButtonStyles, backgroundColor: colors.white, gap: 4, }} onPress={() => setHistoryModal(true)}> <HistoryIcon /> <Text style={styles.fs12ppReg666}>History</Text> </TouchableOpacity> <StartButton onStart={() => { setShowAnimatedCount(true); setTimeLeft(5); // navigation.navigate(routes.challengeReportScreen) for reference }} style={{ position: 'absolute', }} /> <InviteFriendButton onpress={() => setInviteModal(true)} key={'invite-frnd-btn'} requests={1} /> </View> </> : <> <FlatList data={mapStatsData} renderItem={renderStatsItem} horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 8 }} keyExtractor={item => item?.id} /> <View style={{ ...styles.rowCenterApart, width: '75%', alignSelf: 'center', marginTop: 22, marginBottom: 12 }}> {!isPause ? <TouchableOpacity onPress={onPause} style={{ ...styles.burnTrackerActionButtons, backgroundColor: colors.mangoYellow }}> <PauseIcon /> <Text style={{ ...styles.fs14ppMed, color: colors.white }}>Pause</Text> </TouchableOpacity> : <TouchableOpacity onPress={handleResumeWalk} style={{ ...styles.burnTrackerActionButtons, backgroundColor: colors.orange }}> <ResumeIcon /> <Text style={{ ...styles.fs14ppMed, color: colors.white }}>Resume</Text> </TouchableOpacity>} <TouchableOpacity style={{ ... styles.burnTrackerActionButtons, backgroundColor: colors.green }} onPress={handleFinishWalk}> <StopIcon /> <Text style={{ ...styles.fs14ppMed, color: colors.white }}>Finish</Text> </TouchableOpacity> </View> </> } {/*INITIAL LAUNCH TABS*/} <View style={styles.newChallengeBanner}> <View style={styles.rowCenter}> <RoutingIcon /> <Text style={styles.newChallengeBannerText}>You have a New Run Challenge at 06:00 PM</Text> </View> <TouchableOpacity onPress={() => navigation.navigate(routes.runChallenge)}> <Text style={{ ...styles.fs12ppSb666, color: colors.white }}>Join Now</Text> </TouchableOpacity> </View> </View > {historyModal && <SheetModal visible={historyModal} setVisible={setHistoryModal} style={{ justifyContent: 'flex-end', margin: 0 }} key={'history-modal-sheet'}> <View style={styles.modalView}> <FlatList data={historyData} keyExtractor={item => item?.id} renderItem={renderHistoryCards} contentContainerStyle={{ gap: 12 }} style={{ marginVertical: 20, height: height - height * .3, }} showsVerticalScrollIndicator={false} /> </View> </SheetModal> } </> ) } const styles = StyleSheet.create({ animatedCountContainer: { alignItems: 'center', backgroundColor: 'rgba(52, 52, 52, 0.8)', bottom: 0, justifyContent: 'center', left: 0, position: 'absolute', right: 0, top: 0, width: '100%', zIndex: 999, }, animatedCountText: { color: colors.white, fontFamily: 'Poppins-SemiBold', fontSize: 46, }, burnBottomContainer: { marginTop: 'auto', position: 'absolute', width: width, bottom: 0, }, burnTrackerActionButtons: { padding: 10, paddingHorizontal: 20, borderRadius: 57, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 4 }, burnButtonStyles: { padding: 6, borderRadius: 70, paddingRight: 12, flexDirection: 'row', alignItems: 'center', }, fs12ppReg666: { fontSize: 12, fontFamily: fonts.PoppinsRegular, fontWeight: '400', color: colors.c666666 }, fs14ppMed: { fontSize: 14, fontFamily: fonts.PoppinsMedium, fontWeight: '500' }, fs12ppSb666: { fontSize: 12, fontFamily: fonts.PoppinsSemiBold, fontWeight: '600', color: colors.c666666 }, inviteButtonStyles: { padding: 6, borderRadius: 70, paddingRight: 12, flexDirection: 'row', alignItems: 'center', backgroundColor: colors.mangoYellow, gap: 4, position: 'relative', }, locationIcon: { position: 'absolute', right: 20, top: width - width * .50, }, mapStyles: { height: height - height * 0.325, width: '100%', position: 'relative' }, modalView: { backgroundColor: colors.white, padding: 16, borderTopLeftRadius: 24, borderTopRightRadius: 24, }, notificationIndicator: { width: 24, height: 24, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.cEC6E6E, borderRadius: 40, position: 'absolute', top: -10, right: 0, }, newChallengeBanner: { width: '100%', padding: 12, backgroundColor: colors.purple, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 'auto' }, newChallengeBannerText: { fontSize: 12, fontFamily: fonts.PoppinsMedium, color: colors.cf9f9f9 }, notificationCountText: { fontSize: 12, fontFamily: fonts.PoppinsSemiBold, color: colors.white }, p16: { padding: 16, }, rowCenter: { flexDirection: 'row', alignItems: 'center', }, rowCenterApart: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, })
Editor is loading...
Leave a Comment