diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b2520a9..f3d5363 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1079,7 +1079,7 @@ PODS: - React-jsi (= 0.73.2) - React-logger (= 0.73.2) - React-perflogger (= 0.73.2) - - ReactNativePassioSDK (3.1.1-3): + - ReactNativePassioSDK (3.1.4): - React-Core - RNCAsyncStorage (1.21.0): - React-Core @@ -1403,7 +1403,7 @@ SPEC CHECKSUMS: React-runtimescheduler: df8945a656356ff10f58f65a70820478bfcf33ad React-utils: f5bc61e7ea3325c0732ae2d755f4441940163b85 ReactCommon: 45b5d4f784e869c44a6f5a8fad5b114ca8f78c53 - ReactNativePassioSDK: 058706b380c8a0de2c73fb11fa2589f4bae60463 + ReactNativePassioSDK: d1bc3d9c9d7b0b15389f333fbe257df82a7f3ac5 RNCAsyncStorage: 618d03a5f52fbccb3d7010076bc54712844c18ef RNCPicker: b7873ba797dc586bfaf3307d737cbdc620a9ff3e RNDateTimePicker: 7b38b71bcd7c4cfa1cb95f2dff9a4f1faed2dced diff --git a/example/package.json b/example/package.json index e218310..9df4844 100644 --- a/example/package.json +++ b/example/package.json @@ -18,7 +18,7 @@ "dependencies": { "@gorhom/bottom-sheet": "^4.6.1", "@notifee/react-native": "^7.8.2", - "@passiolife/nutritionai-react-native-sdk-v3": "3.1.1-3", + "@passiolife/nutritionai-react-native-sdk-v3": "3.1.4", "@react-native-async-storage/async-storage": "^1.21.0", "@react-native-community/datetimepicker": "^7.6.3", "@react-native-community/slider": "^4.5.0", diff --git a/example/yarn.lock b/example/yarn.lock index 4a289db..c4c0688 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -1444,10 +1444,10 @@ resolved "https://registry.yarnpkg.com/@notifee/react-native/-/react-native-7.8.2.tgz#72d3199ae830b4128ddaef3c1c2f11604759c9c4" integrity sha512-VG4IkWJIlOKqXwa3aExC3WFCVCGCC9BA55Ivg0SMRfEs+ruvYy/zlLANcrVGiPtgkUEryXDhA8SXx9+JcO8oLA== -"@passiolife/nutritionai-react-native-sdk-v3@3.1.1-3": - version "3.1.1-3" - resolved "https://npm.pkg.github.com/download/@passiolife/nutritionai-react-native-sdk-v3/3.1.1-3/3f405c758ccbf77ce27465ff69d48ab7a5b885d0#3f405c758ccbf77ce27465ff69d48ab7a5b885d0" - integrity sha512-zKsHmNpKfX8ugBdiEfau2vCvhWc615P3jdh/wEEtKVywR7EVol9Li/q+CUSXeCaIggJaHr61Hv3P5fV3TdxkCA== +"@passiolife/nutritionai-react-native-sdk-v3@3.1.4": + version "3.1.4" + resolved "https://npm.pkg.github.com/download/@passiolife/nutritionai-react-native-sdk-v3/3.1.4/3428561e2f5490df4bc3940301219e97876253e9#3428561e2f5490df4bc3940301219e97876253e9" + integrity sha512-hgDGR/tMzTfR0QFdg6RKXUZX8yVuTlUc73mFZwnXORJrWmZzoe9AxDwH0Dvj93VhNfilcgpFcp+x/kxS06O5Bg== "@pkgjs/parseargs@^0.11.0": version "0.11.0" diff --git a/package.json b/package.json index f158765..a74e53b 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@babel/runtime": "^7.23.8", "@gorhom/bottom-sheet": "^4.6.1", "@notifee/react-native": "^7.8.2", - "@passiolife/nutritionai-react-native-sdk-v3": "3.1.1-3", + "@passiolife/nutritionai-react-native-sdk-v3": "3.1.4", "@react-native-async-storage/async-storage": "^1.21.0", "@react-native-community/datetimepicker": "^7.6.3", "@react-native-community/slider": "^4.5.0", diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx index 11760c4..3e82c69 100644 --- a/src/components/charts/BarChart.tsx +++ b/src/components/charts/BarChart.tsx @@ -16,6 +16,7 @@ import { VictoryChart, VictoryTheme, VictoryAxis, + VictoryLine, } from 'victory-native'; import { Card } from '../cards'; @@ -39,18 +40,29 @@ export interface BarChartProps { title?: string; barChartContainerStyle?: StyleProp; barData: ChartData[]; + target?: number; } export const BarChart = ({ title = 'Calories', barChartContainerStyle, barData, + target, }: BarChartProps) => { const { calories, black } = useBranding(); const styles = barChartStyle(useBranding()); - const maxValue = Math.max(...barData.map((o) => o.value)); + let maxValue = Math.max(...barData.map((o) => o.value)); + if (target) { + if (maxValue < target) { + maxValue = Math.round(target + (target % 20)); + } else { + maxValue = maxValue > 0 ? Math.round(maxValue + 10) : 30; + } + } + + maxValue = maxValue > 0 ? maxValue : 30; return ( @@ -122,6 +134,25 @@ export const BarChart = ({ data: { fill: calories }, }} /> + + {/* Dotted Line for Target */} + {target && ( + + )} diff --git a/src/components/progressSliders/ProgressSlider.tsx b/src/components/progressSliders/ProgressSlider.tsx index 41e7855..f4c4056 100644 --- a/src/components/progressSliders/ProgressSlider.tsx +++ b/src/components/progressSliders/ProgressSlider.tsx @@ -6,6 +6,7 @@ interface Props { sliderValue: number; sliderMaxValue: number; step: number; + minimumValue?: number; minimumTrackTintColor: string; maximumTrackTintColor: string; thumbTintColor: string; @@ -20,6 +21,7 @@ export const ProgressSlider = (props: Props) => { minimumTrackTintColor, maximumTrackTintColor, thumbTintColor, + minimumValue = 0, onChangeSliderValue, sliderMaxValue, step, @@ -34,7 +36,7 @@ export const ProgressSlider = (props: Props) => { maximumTrackTintColor={maximumTrackTintColor} thumbTintColor={thumbTintColor} vertical={isVertical} - minimumValue={0} + minimumValue={minimumValue} step={step} maximumValue={sliderMaxValue} style={sliderStyle} diff --git a/src/components/svgs/scan.tsx b/src/components/svgs/scan.tsx index ebbc7ea..67d29e5 100644 --- a/src/components/svgs/scan.tsx +++ b/src/components/svgs/scan.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Dimensions } from 'react-native'; import { Path, Svg } from 'react-native-svg'; -const ScanSVG = ({ margin = 20 }: { margin?: number }) => { +const ScanSVG = ({ margin = 32 }: { margin?: number }) => { return ( { const styles = timeStampViewStyle(useBranding()); - const f = format ?? mode === 'time' ? 'HH:MM a' : 'MMM dd, yyyy'; + const f = format ?? mode === 'time' ? 'hh:mm a' : 'MMM dd, yyyy'; useImperativeHandle(ref, () => { return { @@ -68,7 +68,7 @@ export const TimeStampView = React.forwardRef( const timeStampViewStyle = ({ border, white }: Branding) => StyleSheet.create({ timeStampContainer: { - marginTop: scaleHeight(12), + marginTop: scaleHeight(6), marginBottom: scaleHeight(12), paddingHorizontal: 19, borderColor: border, diff --git a/src/hooks/useNutritionAdvisor.tsx b/src/hooks/useNutritionAdvisor.tsx index f32a728..0082ee6 100644 --- a/src/hooks/useNutritionAdvisor.tsx +++ b/src/hooks/useNutritionAdvisor.tsx @@ -54,17 +54,11 @@ export const useNutritionAdvisor = ({ key }: { key: string }) => { useEffect(() => { const initializeNutritionAdvisor = async () => { try { - const status = await NutritionAdvisor.configure(key); - if (status?.status === 'Success') { - const conversationResponse = - await NutritionAdvisor.initConversation(); - setConfigureStatus( - conversationResponse?.status === 'Success' ? 'Success' : 'Error' - ); - } else { - setConfigureStatus('Error'); - setSDKError(status?.message); - } + const conversationResponse = await NutritionAdvisor.initConversation(); + + setConfigureStatus( + conversationResponse?.status === 'Success' ? 'Success' : 'Error' + ); } catch (err) { setConfigureStatus('Error'); setSDKError('Error'); diff --git a/src/screens/advisor/useAdvisorScreen.ts b/src/screens/advisor/useAdvisorScreen.ts index 37458d2..caa5529 100644 --- a/src/screens/advisor/useAdvisorScreen.ts +++ b/src/screens/advisor/useAdvisorScreen.ts @@ -7,7 +7,7 @@ import { Keyboard } from 'react-native'; import { PassioSDK } from '@passiolife/nutritionai-react-native-sdk-v3'; import { ShowToast, - createFoodLogUsingPortionSize, + createFoodLogUsingWeightGram, getLogToDate, mealLabelByDate, } from '../../utils'; @@ -108,10 +108,11 @@ export const useAdvisorScreen = () => { for (const item of selected) { if (item.foodDataInfo) { const foodItem = await PassioSDK.fetchFoodItemForDataInfo( - item.foodDataInfo + item.foodDataInfo, + item.weightGrams ); if (foodItem) { - let foodLog = createFoodLogUsingPortionSize( + let foodLog = createFoodLogUsingWeightGram( foodItem, logToDate, meal, diff --git a/src/screens/advisor/view/message/records/MessageRecordItem.tsx b/src/screens/advisor/view/message/records/MessageRecordItem.tsx index 960f347..ff7267d 100644 --- a/src/screens/advisor/view/message/records/MessageRecordItem.tsx +++ b/src/screens/advisor/view/message/records/MessageRecordItem.tsx @@ -53,7 +53,7 @@ export const MessageRecordItem = (props: Props) => { {bottom} @@ -127,6 +127,11 @@ const styles = StyleSheet.create({ marginVertical: 2, marginRight: 10, }, + bottom: { + marginStart: 4, + marginVertical: 2, + marginRight: 10, + }, selectedAddIcon: { borderWidth: 1, borderColor: '#4F46E5', diff --git a/src/screens/advisor/view/message/records/MessageRecords.tsx b/src/screens/advisor/view/message/records/MessageRecords.tsx index 2483bdd..52074df 100644 --- a/src/screens/advisor/view/message/records/MessageRecords.tsx +++ b/src/screens/advisor/view/message/records/MessageRecords.tsx @@ -83,7 +83,7 @@ export const MessageRecords = ({ { if (response.isLogged) { return; diff --git a/src/screens/myFoods/MyFoodsScreen.tsx b/src/screens/myFoods/MyFoodsScreen.tsx index 0cbf5ac..f9676c7 100644 --- a/src/screens/myFoods/MyFoodsScreen.tsx +++ b/src/screens/myFoods/MyFoodsScreen.tsx @@ -20,6 +20,7 @@ export const MyFoodsScreen = () => { onCreateFoodPress, onEditorPress, onDeletePress, + onCreateNewRecipe, onLogPress, } = useMyFoodScreen(); @@ -58,13 +59,13 @@ export const MyFoodsScreen = () => { onPress={onCreateFoodPress} /> )} - {/* {tab !== 'Custom Foods' && ( + {tab !== 'Custom Foods' && ( - )} */} + )} ); }; diff --git a/src/screens/quick/QuickScanningScreen.tsx b/src/screens/quick/QuickScanningScreen.tsx index 57c7eb7..3053c7e 100644 --- a/src/screens/quick/QuickScanningScreen.tsx +++ b/src/screens/quick/QuickScanningScreen.tsx @@ -1,5 +1,11 @@ -import React, { useState } from 'react'; -import { Image, StyleSheet, TouchableOpacity, View } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { + Image, + Platform, + StyleSheet, + TouchableOpacity, + View, +} from 'react-native'; import { DetectionCameraView } from '@passiolife/nutritionai-react-native-sdk-v3/src/sdk/v2'; import type { MealLabel } from '../../models'; @@ -17,6 +23,11 @@ import { VisualFoodScan } from './mode/visual/VisualFoodScan'; import { useBranding } from '../../contexts'; import { NutritionFactScan } from './mode/nutritionFact/NutritionFactScan'; import { ShowToast } from '../../utils'; +import { ProgressSlider } from '../../components'; +import { + PassioCameraZoomLevel, + PassioSDK, +} from '@passiolife/nutritionai-react-native-sdk-v3'; export interface ScanningScreenProps { logToDate: Date | undefined; @@ -35,13 +46,25 @@ export const QuickScanningScreen = gestureHandlerRootHOC(() => { const [mode, setMode] = useState('Visual'); const navigation = useNavigation(); const branding = useBranding(); + const [level, setLevel] = useState(); + + useEffect(() => { + function init() { + setTimeout(() => { + PassioSDK.getMinMaxCameraZoomLevel().then((passioCameraZoomLevel) => { + setLevel(passioCameraZoomLevel); + }); + }, 300); + } + init(); + }, []); const renderMode = () => { return ( { ); }; + const renderZoomIndicator = () => { + return ( + + + + ); + }; return ( - + {!info && ( @@ -139,6 +180,8 @@ export const QuickScanningScreen = gestureHandlerRootHOC(() => { /> {info && setInfo(false)} />} {renderMode()} + + {level && renderZoomIndicator()} ); }); @@ -153,13 +196,21 @@ const styles = StyleSheet.create({ scanIcon: { position: 'absolute', top: 0, - bottom: 50, + bottom: Platform.OS === 'android' ? 100 : 80, right: 0, left: 0, marginHorizontal: 16, alignItems: 'center', justifyContent: 'center', }, + slider: { + position: 'absolute', + bottom: Platform.OS === 'android' ? 230 : 250, + left: 0, + marginHorizontal: 16, + right: 0, + justifyContent: 'center', + }, icons: { height: 24, width: 24, diff --git a/src/screens/takePicture/result/PictureLoggingResult.tsx b/src/screens/takePicture/result/PictureLoggingResult.tsx index 78af45c..ffe8a7f 100644 --- a/src/screens/takePicture/result/PictureLoggingResult.tsx +++ b/src/screens/takePicture/result/PictureLoggingResult.tsx @@ -116,7 +116,7 @@ export const PictureLoggingResult = ({ { onFoodSelect({ ...item, index: index }); }} diff --git a/src/screens/takePicture/result/PictureLoggingResultItemView.tsx b/src/screens/takePicture/result/PictureLoggingResultItemView.tsx index 1b6ef1b..d351318 100644 --- a/src/screens/takePicture/result/PictureLoggingResultItemView.tsx +++ b/src/screens/takePicture/result/PictureLoggingResultItemView.tsx @@ -36,7 +36,7 @@ export const PictureLoggingResultItemView = (props: Props) => { {bottom} @@ -93,6 +93,11 @@ const styles = StyleSheet.create({ marginVertical: 2, marginRight: 10, }, + bottom: { + marginStart: 16, + marginVertical: 2, + marginRight: 10, + }, selectedAddIcon: { borderWidth: 1, borderColor: '#4F46E5', diff --git a/src/screens/voiceLogging/useVoiceLoggingScreen.ts b/src/screens/voiceLogging/useVoiceLoggingScreen.ts index de4c88c..36c9dfa 100644 --- a/src/screens/voiceLogging/useVoiceLoggingScreen.ts +++ b/src/screens/voiceLogging/useVoiceLoggingScreen.ts @@ -10,7 +10,7 @@ import { import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ShowToast, - createFoodLogUsingPortionSize, + createFoodLogUsingWeightGram, getLogToDate, mealLabelByDate, } from '../../utils'; @@ -115,10 +115,11 @@ export function useVoiceLogging() { for (const item of selected) { if (item.advisorInfo.foodDataInfo) { const foodItem = await PassioSDK.fetchFoodItemForDataInfo( - item.advisorInfo.foodDataInfo + item.advisorInfo.foodDataInfo, + item.advisorInfo.weightGrams ); if (foodItem) { - let foodLog = createFoodLogUsingPortionSize( + let foodLog = createFoodLogUsingWeightGram( foodItem, logToDate, meal, diff --git a/src/screens/voiceLogging/views/VoiceLoggingResult.tsx b/src/screens/voiceLogging/views/VoiceLoggingResult.tsx index d365dbf..986bd8c 100644 --- a/src/screens/voiceLogging/views/VoiceLoggingResult.tsx +++ b/src/screens/voiceLogging/views/VoiceLoggingResult.tsx @@ -108,7 +108,7 @@ export const VoiceLoggingResult = React.forwardRef( item.advisorInfo.recognisedName } imageName={foodDataInfo?.iconID} - bottom={`${item.advisorInfo?.portionSize} | ${Math.round(calories)} cal`} + bottom={`${item.advisorInfo?.weightGrams} g | ${Math.round(calories)} cal`} onFoodLogSelect={() => { onFoodSelect(item); }} diff --git a/src/screens/voiceLogging/views/VoiceLoggingResultItemView.tsx b/src/screens/voiceLogging/views/VoiceLoggingResultItemView.tsx index 5df1641..ec3a81d 100644 --- a/src/screens/voiceLogging/views/VoiceLoggingResultItemView.tsx +++ b/src/screens/voiceLogging/views/VoiceLoggingResultItemView.tsx @@ -36,7 +36,7 @@ export const VoiceLoggingResultItemView = (props: Props) => { {bottom} @@ -92,6 +92,11 @@ const styles = StyleSheet.create({ marginVertical: 2, marginRight: 10, }, + bottomText: { + marginStart: 16, + marginVertical: 2, + marginRight: 10, + }, selectedAddIcon: { borderWidth: 1, borderColor: '#4F46E5', diff --git a/src/screens/water/WaterScreen.tsx b/src/screens/water/WaterScreen.tsx index 9fd7be7..98b7889 100644 --- a/src/screens/water/WaterScreen.tsx +++ b/src/screens/water/WaterScreen.tsx @@ -29,6 +29,7 @@ const WaterScreen = () => { const { calendarCarouselRef, chartData, + target, isContentVisible, isImperialWeight, ogMlLabel, @@ -58,11 +59,13 @@ const WaterScreen = () => { style={styles.swipple} > onEditPress(item)} style={styles.item}> - + {Math.round(convertConsumeValueAsUnitSystem(Number(item.consumed)))} -  {ogMlLabel} + +  {ogMlLabel} + - + {displayDate + '\n' + displayTime} @@ -95,6 +98,10 @@ const WaterScreen = () => { ); }; + const renderFooter = () => { + return ; + }; + return ( <> { barData={chartData} title="Water Trend" barChartContainerStyle={{ marginTop: 16 }} + target={target} /> )} { style={styles.sectionList} renderItem={renderItem} ListHeaderComponent={renderHeader} + ListFooterComponent={renderFooter} /> @@ -144,6 +153,9 @@ const waterIntakeStyle = ({ white, border }: Branding) => cardStyle: { paddingVertical: 0, }, + footer: { + height: 0, + }, switchTabContainer: { marginTop: scaleHeight(12), }, @@ -192,7 +204,7 @@ const waterIntakeStyle = ({ white, border }: Branding) => ...scaled(24), }, roundedAndShadowView: { - marginVertical: scaleHeight(24), + marginVertical: scaleHeight(8), }, timestamp: { textAlign: 'right', diff --git a/src/screens/water/useWaters.ts b/src/screens/water/useWaters.ts index 42aa091..e5e3938 100644 --- a/src/screens/water/useWaters.ts +++ b/src/screens/water/useWaters.ts @@ -11,6 +11,7 @@ import { SwitchTabLabelEnum } from '../../types'; import { ShowToast } from '../../utils'; import { useSettingScreen } from '../setting/useSettingScreen'; import { + convertKGToPounds, convertMlToOg, convertOgToMl, } from '../nutritionProfile/unitConversions'; @@ -24,6 +25,7 @@ type ScreenNavigationProps = StackNavigationProp; export const useWaters = () => { const [isContentVisible, setIsContentVisible] = useState(true); // State to toggle content visibility const [waters, setWaterSection] = useState([]); // State to toggle content visibility + const [target, setTarget] = useState(1000); const services = useServices(); @@ -35,6 +37,16 @@ export const useWaters = () => { const [chartData, setChartData] = useState([]); + useEffect(() => { + services.dataService.getNutritionProfile().then((profile) => { + if (isImperialWeight) { + setTarget(convertKGToPounds(profile?.targetWeight ?? 0)); + } else { + setTarget(profile?.targetWeight ?? 0); + } + }); + }, [isImperialWeight, services.dataService]); + const getWaters = useCallback( (startDate: Date, endDate: Date, type: SwitchTabLabelEnum) => { services.dataService @@ -126,6 +138,7 @@ export const useWaters = () => { }; return { + target, calendarCarouselRef, chartData, isContentVisible, diff --git a/src/screens/water/views/waterentry/WaterEntry.style.tsx b/src/screens/water/views/waterentry/WaterEntry.style.tsx index f0df75a..255c6bd 100644 --- a/src/screens/water/views/waterentry/WaterEntry.style.tsx +++ b/src/screens/water/views/waterentry/WaterEntry.style.tsx @@ -17,6 +17,7 @@ const waterEntryStyle = ({ white, backgroundColor }: Branding) => textInput: { padding: scaleHeight(12), marginVertical: scaleHeight(4), + fontSize: 16, paddingStart: scaleWidth(16), backgroundColor: white, }, @@ -31,7 +32,7 @@ const waterEntryStyle = ({ white, backgroundColor }: Branding) => marginTop: 10, }, inputTitle: { - marginTop: scaleHeight(16), + marginTop: scaleHeight(8), }, actionContainer: { diff --git a/src/screens/water/views/waterentry/WaterEntry.tsx b/src/screens/water/views/waterentry/WaterEntry.tsx index 79bd74b..4346d0e 100644 --- a/src/screens/water/views/waterentry/WaterEntry.tsx +++ b/src/screens/water/views/waterentry/WaterEntry.tsx @@ -18,6 +18,7 @@ const WaterEntry = () => { consumed, isEdit, dateRef, + error, timeRef, unitLabel, water, @@ -48,11 +49,7 @@ const WaterEntry = () => { onChangeText={handleWaterInput} style={styles.textInput} returnKeyType="done" - error={ - consumed?.toString() === '' || Number(consumed) <= 0 - ? 'Required' - : '' - } + error={error} keyboardType="numeric" /> ; export const useWaterEntry = () => { const { params } = useRoute>(); - const [consumed, setConsumed] = useState('50'); + const [consumed, setConsumed] = useState(''); const dateRef = useRef(null); const timeRef = useRef(null); const navigation = useNavigation(); const { isImperialWeight, ogMlLabel } = useSettingScreen(); + const [error, setError] = useState(''); useEffect(() => { - setConsumed( - (isImperialWeight - ? Math.round( - convertMlToOg(Number(params.water?.consumed ?? 0)) - ).toString() - : params.water?.consumed) ?? '50' - ); + if (params.water?.consumed) { + setConsumed( + (isImperialWeight + ? Math.round( + convertMlToOg(Number(params.water?.consumed ?? 0)) + ).toString() + : params.water?.consumed) ?? '50' + ); + } }, [isImperialWeight, params.water?.consumed]); const service = useServices(); @@ -46,6 +50,11 @@ export const useWaterEntry = () => { navigation.goBack(); }; const handlePressOk = () => { + if (!isValidDecimalNumber(consumed)) { + setError('Please enter valid input'); + return; + } + const updateDate = dateRef.current?.getTimeStamp(); const updateTime = timeRef.current?.getTimeStamp(); @@ -77,6 +86,7 @@ export const useWaterEntry = () => { water: params.water, unitLabel: ogMlLabel, isEdit: params.water !== undefined, + error, handlePressCancel, handlePressOk, handleWaterInput, diff --git a/src/screens/water/waterUtils.ts b/src/screens/water/waterUtils.ts index 7d00b88..7daf6d1 100644 --- a/src/screens/water/waterUtils.ts +++ b/src/screens/water/waterUtils.ts @@ -40,7 +40,7 @@ export const prepareWeekly = ( const chartData: ChartData[] = Object.entries(sumByLabel).map( ([dayOfWeek, value]) => ({ label: dayOfWeek, - value: value, + value: Math.round(value), }) ); diff --git a/src/screens/weight/WeightScreen.tsx b/src/screens/weight/WeightScreen.tsx index f24523f..358145a 100644 --- a/src/screens/weight/WeightScreen.tsx +++ b/src/screens/weight/WeightScreen.tsx @@ -27,6 +27,7 @@ import { WeightTrendChart } from './linechart/lineChart'; const WeightScreen = () => { const { + target, calendarCarouselRef, isContentVisible, weightLabel, @@ -119,7 +120,7 @@ const WeightScreen = () => { Weight Trend {weightTrendData.length > 0 ? ( - + ) : null} diff --git a/src/screens/weight/linechart/lineChart.tsx b/src/screens/weight/linechart/lineChart.tsx index 3ee5b1b..c8b01f7 100644 --- a/src/screens/weight/linechart/lineChart.tsx +++ b/src/screens/weight/linechart/lineChart.tsx @@ -5,6 +5,7 @@ import { VictoryTheme, VictoryAxis, VictoryLine, + VictoryScatter, } from 'victory-native'; import { useBranding } from '../../../contexts'; import { scaleHeight, scaledSize } from '../../../utils'; @@ -16,30 +17,38 @@ export type WeightTrendChart = { interface WeightTrendChartProps { data: WeightTrendChart[]; + target: number; } -export const WeightTrendChart = ({ data }: WeightTrendChartProps) => { +export const WeightTrendChart = ({ data, target }: WeightTrendChartProps) => { const { primaryColor, black } = useBranding(); - const maxValue = Math.max(...data.map((o) => o.value)); + let maxValue = Math.max(...data.map((o) => o.value)); + + if (maxValue < target) { + maxValue = Math.round(target + (target % 20)); + } else { + maxValue = maxValue > 0 ? Math.round(maxValue + 10) : 30; + } return ( { : undefined; }} style={{ - tickLabels: { - fontSize: 12, - paddingTop: 0, - angle: 0, - fill: black, - }, grid: { stroke: 'none' }, ticks: { stroke: 'node' }, axis: { stroke: 'none' }, - axisLabel: { color: 'red' }, }} maxDomain={{ y: maxValue }} minDomain={{ y: 0 }} @@ -85,7 +87,35 @@ export const WeightTrendChart = ({ data }: WeightTrendChartProps) => { x="label" y="value" data={data} - interpolation="basis" + interpolation="monotoneX" + /> + + o.value > 0)} + size={4} + style={{ + labels: { display: 'none' }, + data: { fill: primaryColor }, + }} + x="label" + y="value" + /> + + {/* Dotted Line for Target */} + diff --git a/src/screens/weight/useWeights.ts b/src/screens/weight/useWeights.ts index a60d88d..d56d011 100644 --- a/src/screens/weight/useWeights.ts +++ b/src/screens/weight/useWeights.ts @@ -26,8 +26,19 @@ export const useWeights = () => { const [isContentVisible, setIsContentVisible] = useState(true); const [weights, setWeightSection] = useState([]); + const [target, setTarget] = useState(1000); const [weightTrendData, seTWeightTrendData] = useState([]); + useEffect(() => { + services.dataService.getNutritionProfile().then((profile) => { + if (isImperialWeight) { + setTarget(convertKGToPounds(profile?.targetWeight ?? 0)); + } else { + setTarget(profile?.targetWeight ?? 0); + } + }); + }, [isImperialWeight, services.dataService]); + const getWeights = useCallback( (startDate: Date, endDate: Date, type: SwitchTabLabelEnum) => { services.dataService @@ -104,6 +115,7 @@ export const useWeights = () => { }; return { + target, calendarCarouselRef, isContentVisible, weightLabel, diff --git a/src/screens/weight/views/weightentry/WeightEntry.style.tsx b/src/screens/weight/views/weightentry/WeightEntry.style.tsx index 75e7fe6..9811f9d 100644 --- a/src/screens/weight/views/weightentry/WeightEntry.style.tsx +++ b/src/screens/weight/views/weightentry/WeightEntry.style.tsx @@ -19,6 +19,7 @@ const weightEntryStyle = ({ white, backgroundColor }: Branding) => marginVertical: scaleHeight(4), paddingStart: scaleWidth(16), backgroundColor: white, + fontSize: 16, }, formView: { @@ -31,7 +32,7 @@ const weightEntryStyle = ({ white, backgroundColor }: Branding) => marginTop: 10, }, inputTitle: { - marginTop: scaleHeight(16), + marginTop: scaleHeight(8), }, actionContainer: { diff --git a/src/screens/weight/views/weightentry/WeightEntry.tsx b/src/screens/weight/views/weightentry/WeightEntry.tsx index b648ad8..ad3cdcf 100644 --- a/src/screens/weight/views/weightentry/WeightEntry.tsx +++ b/src/screens/weight/views/weightentry/WeightEntry.tsx @@ -19,6 +19,7 @@ const WeightEntry = () => { prevWeight, timeRef, weight, + error, isEdit, weightLabel, handlePressCancel, @@ -48,9 +49,7 @@ const WeightEntry = () => { onChangeText={handleWeightInput} style={styles.textInput} returnKeyType="done" - error={ - weight?.toString() === '' || Number(weight) <= 0 ? 'Required' : '' - } + error={error} keyboardType="numeric" /> ; export const useWeightEntry = () => { @@ -21,15 +22,17 @@ export const useWeightEntry = () => { const navigation = useNavigation(); const { isImperialWeight, weightLabel } = useSettingScreen(); const [weight, setWeight] = useState(''); - + const [error, setError] = useState(''); useEffect(() => { - setWeight( - (isImperialWeight - ? Math.round( - convertKGToPounds(Number(params.weight?.weight ?? 0)) - ).toString() - : params.weight?.weight) ?? '50' - ); + if (params.weight?.weight) { + setWeight( + (isImperialWeight + ? Math.round( + convertKGToPounds(Number(params.weight?.weight ?? 0)) + ).toString() + : params.weight?.weight) ?? '50' + ); + } }, [isImperialWeight, params.weight?.weight]); const service = useServices(); @@ -42,6 +45,10 @@ export const useWeightEntry = () => { navigation.goBack(); }; const handlePressOk = () => { + if (!isValidDecimalNumber(weight)) { + setError('Please enter valid input'); + return; + } const updateDate = dateRef.current?.getTimeStamp(); const updateTime = timeRef.current?.getTimeStamp(); @@ -74,6 +81,7 @@ export const useWeightEntry = () => { timeRef, weight, weightLabel, + error, handlePressCancel, handlePressOk, handleWeightInput, diff --git a/src/utils/V3Utils.tsx b/src/utils/V3Utils.tsx index 91579e0..da352b4 100644 --- a/src/utils/V3Utils.tsx +++ b/src/utils/V3Utils.tsx @@ -105,7 +105,7 @@ function convertPassioIngredientToFoodItem(item: PassioIngredient): FoodItem { name: passioIngredient.name, iconId: passioIngredient.iconId, amount: passioIngredient.amount, - weight: passioIngredient.weight, + ingredientWeight: passioIngredient.weight, id: passioIngredient.id, }, passioIngredient.amount.weight diff --git a/src/utils/passioFoodDataInfoUtils.ts b/src/utils/passioFoodDataInfoUtils.ts index fb9b779..3ea7067 100644 --- a/src/utils/passioFoodDataInfoUtils.ts +++ b/src/utils/passioFoodDataInfoUtils.ts @@ -22,7 +22,8 @@ export const createFoodLogUsingFoodDataInfo = async ( for (const item of foods) { if (item && item.foodDataInfo) { const foodItem = await PassioSDK.fetchFoodItemForDataInfo( - item.foodDataInfo + item.foodDataInfo, + item.weightGrams ); if (foodItem) { const foodLog = convertPassioFoodItemToFoodLog( @@ -38,6 +39,18 @@ export const createFoodLogUsingFoodDataInfo = async ( return foodLogs; }; +export const createFoodLogUsingWeightGram = ( + foodItem: PassioFoodItem, + logToDate: Date, + meal: MealLabel, + _weightGram: number, + _portionSize: string +) => { + let foodLog = convertPassioFoodItemToFoodLog(foodItem, logToDate, meal); + + return foodLog; +}; + export const createFoodLogUsingPortionSize = ( foodItem: PassioFoodItem, logToDate: Date, diff --git a/yarn.lock b/yarn.lock index c38c248..f79a148 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1919,10 +1919,10 @@ dependencies: "@octokit/openapi-types" "^20.0.0" -"@passiolife/nutritionai-react-native-sdk-v3@3.1.1-3": - version "3.1.1-3" - resolved "https://npm.pkg.github.com/download/@passiolife/nutritionai-react-native-sdk-v3/3.1.1-3/3f405c758ccbf77ce27465ff69d48ab7a5b885d0#3f405c758ccbf77ce27465ff69d48ab7a5b885d0" - integrity sha512-zKsHmNpKfX8ugBdiEfau2vCvhWc615P3jdh/wEEtKVywR7EVol9Li/q+CUSXeCaIggJaHr61Hv3P5fV3TdxkCA== +"@passiolife/nutritionai-react-native-sdk-v3@3.1.4": + version "3.1.4" + resolved "https://npm.pkg.github.com/download/@passiolife/nutritionai-react-native-sdk-v3/3.1.4/3428561e2f5490df4bc3940301219e97876253e9#3428561e2f5490df4bc3940301219e97876253e9" + integrity sha512-hgDGR/tMzTfR0QFdg6RKXUZX8yVuTlUc73mFZwnXORJrWmZzoe9AxDwH0Dvj93VhNfilcgpFcp+x/kxS06O5Bg== "@pkgjs/parseargs@^0.11.0": version "0.11.0"