burntab
unknown
plain_text
2 years ago
25 kB
6
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