Food Detail

To display the food details, passioFoodItem is required.

We start by defining a FoodNutrient interface, which will represent individual nutrient information:

export interface FoodNutrient {
  title: string
  value: number
  unit: string
}

interface Props {
  passioFoodItem: PassioFoodItem
}

This defines the structure of each nutrient, with a title for its name, a value for its numerical data, and a unit to specify the measurement unit (e.g., grams, calories).

We create a state variable, foodNutrients, using React's useState hook to hold an array of FoodNutrient objects:

const [foodNutrients, setFoodNutrients] = useState<FoodNutrient[]>([])

Extracting Nutrients with useEffect

We use useEffect to trigger the extraction of nutrient information when the passioFoodItem changes. The PassioSDK.getNutrientsOfPassioFoodItem() method is used to get the nutrients based on the food item and its weight:

  useEffect(() => {
    function init() {
      setFoodNutrients(
        extractFoodNutrients(
          PassioSDK.getNutrientsOfPassioFoodItem(
            passioFoodItem,
            passioFoodItem.amount.weight
          )
        )
      )
    }
    init()
  }, [passioFoodItem])

he extractFoodNutrients() is a utility function that processes the raw data returned from the SDK and formats it into the FoodNutrient structure.

function extractFoodNutrients(
    passioNutrients: PassioNutrients | null
  ): FoodNutrient[] {
    if (passioNutrients == null) {
      return []
    }
    return Object.entries(passioNutrients)
      .map(([title, { value, unit }]) => ({
        title,
        value: value,
        unit,
      }))
      .filter((item) => item.title !== 'weight')
  }

Rendering the Macro-Nutrients

Next, we build a UI component to display macro-nutrients like calories, carbohydrates, protein, and fat. In the renderMacro function, we map through a predefined array of macro-nutrients, search for the corresponding nutrient in the foodNutrients array, and display its value:

   const renderMacro = () => {
    return (
      <View
        style={{
          flexDirection: "row",
          flex: 1,
        }}
      >
        {["Calories", "Carbs", "Protein", "Fat"].map((title) => {
          const nutrient = nutrients.find(
            (item) => item.title.toLowerCase() === title.toLowerCase()
          );
          return (
            <View
              key={title}
              style={{
                flex: 1,
                marginTop: 12,
              }}
            >
              <Text
                style={{
                  fontSize: 16,
                  fontWeight: "600",
                }}
              >
                {title}
              </Text>
              <Text>{(nutrient?.value ?? 0).toFixed(2)}</Text>
            </View>
          );
        })}
      </View>
    );
  };

Rendering the Micro-Nutrients

const renderNutrientItem = () => (
    <View
      style={{
        backgroundColor: "white",
        padding: 16,
      }}
    >
      <Text
        style={{
          fontWeight: "500",
          color: "black",
          fontSize: 16,
          marginBottom: 12,
          flex: 1,
        }}
      >
        Nutrients
      </Text>
      <FlatList
        data={foodNutrients.filter((item) => item.value >= 1)}
        renderItem={({ item }) => {
          return (
            <View
              style={{
                flexDirection: "row",
                marginVertical: 4,
              }}
            >
              <Text
                style={{
                  fontWeight: "400",
                  color: "black",
                  textTransform: "capitalize",
                  flex: 1,
                }}
              >
                {getNutrientName[item.title] ?? item.title}
              </Text>
              <Text>
                {Math.round(item.value)}
                {" " + item.unit}
              </Text>
            </View>
          );
        }}
      />
    </View>
  );

Rendering Food Information

The renderFoodDetailCard function is a React Native component that generates a card UI displaying detailed information about a food item. It features an icon, the food item's name, and additional information based on the food item's attributes.

  • Icon Display:

    • The icon of the food item is displayed within a circular View. The PassioIconView component is used to render the icon based on the iconId and iconSize properties.

  • Food Name:

    • The food item's name is displayed in a Text component, styled for clarity with padding, font size, weight, and text transformation.

  • Additional Information:

    • The renderMacro() function is called to render macronutrient information.

    • If passioFoodItem.isOpenFood is true, the renderOpenFood() function is called to display additional details relevant to open food items.

 
  const renderFoodDetailCard = () => {
    return (
      <View
        style={{
          borderRadius: 16,
          backgroundColor: "white",
          padding: 16,
        }}
      >
        <View
          style={{
            flexDirection: "row",
            alignSelf: "center",
            justifyContent: "center",
            alignItems: "center",
            flex: 1,
          }}
        >
          <View
            style={{
              overflow: "hidden",
              height: 60,
              width: 60,
              borderRadius: 30,
            }}
          >
            <PassioIconView
              style={{
                height: 60,
                width: 60,
              }}
              config={{
                passioID: passioFoodItem.iconId,
                iconSize: IconSize.PX90,
              }}
            />
          </View>
          <View
            style={{
              flex: 1,
            }}
          >
            <Text
              style={{
                paddingHorizontal: 16,
                fontSize: 16,
                textTransform: "capitalize",
                fontWeight: "600",
              }}
            >
              {passioFoodItem.name}
            </Text>
          </View>
        </View>
        {renderMacro()}
      </View>
    );
  };
  

Edit Serving Size

React Native component that generates a user interface for editing serving sizes of a food item. It displays the current serving size, provides an input field for updating the quantity, and offers selectable serving units.

The structure of passioFoodItem is determined by the properties of prop.passioFoodItem, which could include fields such as:

  • name: The name of the food item.

  • iconId: An identifier for the food item's icon.

  • amount: An object containing serving sizes and units.

  • isOpenFood: A boolean indicating whether the food item is an open food.

  const [passioFoodItem, setPassioFoodItem] = useState(({ ...prop.passioFoodItem }))

onServingQuantityChange && onServingSizeSelect

The onServingQuantityChange function is a callback that handles changes to the serving quantity input. It updates the state of the input and recalculates the weight based on the quantity entered by the user. This function is optimized with useCallback to prevent unnecessary re-renders.

Purpose

To manage the input for serving quantities, ensuring that the food item's state is updated dynamically based on user input. It calculates the corresponding weight based on the selected serving unit and updates the passioFoodItem state accordingly.

 const onServingQuantityChange = useCallback(
    (value: string) => {
      setTextInput(value)
      const newQuantity = Number(value.length > 0 ? value : 1)
      const weight =
        passioFoodItem.amount.servingUnits?.filter(
          (i) => i.unitName === passioFoodItem.amount.selectedUnit
        )[0].value ?? 1

      setPassioFoodItem((foodItem) => {
        foodItem.amount.weight.value = weight * newQuantity
        foodItem.amount.selectedQuantity = newQuantity
        return {
          ...foodItem,
        }
      })
    },
    [passioFoodItem.amount.selectedUnit, passioFoodItem.amount.servingUnits]
  )
  const onServingSizeSelect = useCallback(
    (value: ServingUnit) => {
      const defaultWeight = passioFoodItem.amount.weight.value ?? 0
      const newQuantity = Number((defaultWeight / value.value).toFixed(2))
      setTextInput((newQuantity ?? 1).toString())
      setPassioFoodItem((foodItem) => {
        foodItem.amount.selectedUnit = value.unitName
        foodItem.amount.selectedQuantity = newQuantity
        return {
          ...foodItem,
        }
      })
    },
    [passioFoodItem.amount.weight.value]
  )

Render View of Edit Serving

  • Serving Size Display:

    • A Text component displays the serving sizes, formatted to show the calculated weight and its unit. The text is styled for emphasis.

  • Serving Quantity Input:

    • A TextInput component allows users to input the serving quantity. It is styled with borders, padding, and a placeholder that indicates the expected input.

  • Selectable Serving Units:

    • A FlatList displays the available serving units horizontally. Each item in the list is rendered as a Pressable, allowing users to select a unit.

    • The background color of the selected unit changes to blue, and the text color changes to white for clarity.

    calculatedWeight: passioFoodItem.amount?.weight.value,
    calculatedWeightUnit: passioFoodItem?.amount?.weight?.unit,
    selectedServingUnit: passioFoodItem?.amount.selectedUnit,

  const renderEditServing = () => {
    return (
      <View
        style={{
          backgroundColor: "white",
          padding: 16,
        }}
      >
        <Text
          style={{
            fontWeight: "500",
            color: "black",
            fontSize: 16,
            marginBottom: 12,
            flex: 1,
          }}
        >{`Serving Sizes (${Math.round(
          calculatedWeight ?? 0
        )} ${calculatedWeightUnit})`}</Text>
        <TextInput
          value={textInputServingQty.toString()}
          style={{
            borderColor: "rgba(209, 213, 219, 1)",
            backgroundColor: "rgba(238, 242, 255, 1)",
            borderWidth: 1,
            paddingHorizontal: 16,
            borderRadius: 16,
            padding: 16,
            marginVertical: 16,
          }}
          keyboardType="numeric"
          placeholder="Serving Size"
          placeholderTextColor={"gray"}
          onChangeText={onServingQuantityChange}
        />
        <FlatList
          horizontal
          showsHorizontalScrollIndicator={false}
          data={passioFoodItem.amount.servingUnits}
          renderItem={({ item }) => (
            <Pressable
              onPress={() => onServingSizeSelect(item)}
              style={[
                styles.servingContainer,
                selectedServingUnit === item.unitName && {
                  backgroundColor: "blue",
                },
              ]}
            >
              <Text
                style={[
                  {
                    fontWeight: "400",
                    color: "black",
                    fontSize: 13,
                    paddingVertical: 6,
                    overflow: "hidden",
                    textTransform: "capitalize",
                  },
                  selectedServingUnit === item.unitName && {
                    color: "white",
                  },
                ]}
              >
                {item.unitName}
              </Text>
            </Pressable>
          )}
        />
      </View>
    );
  };

Show Ingredients

The renderIngredient function is a React Native component that displays a list of ingredients for a food item. It checks if the passioFoodItem has ingredients and renders a list of them, including each ingredient's name, amount, and icon.

FlatList:

  • A FlatList is used to display each ingredient.

  • Each ingredient is represented by a row, consisting of:

    • An icon (PassioIconView) displaying the ingredient’s iconId.

    • The name of the ingredient.

    • The selected unit or amount of the ingredient.

Render Item:

  • For each ingredient, a row is rendered, showing:

    • The ingredient’s icon.

    • The ingredient’s name.

    • The selected unit of the ingredient, such as its amount.

  const renderIngredient = () => {
    return (
      <View
        style={{
          backgroundColor: "white",
          padding: 16,
        }}
      >
        {passioFoodItem.ingredients &&
          passioFoodItem.ingredients.length > 1 && (
            <>
              <Text style={styles.title}>Ingredients</Text>
              <FlatList
                data={passioFoodItem.ingredients}
                keyExtractor={(item, index) => item.toString() + index}
                renderItem={({ item }) => (
                  <View
                    style={{
                      flexDirection: "row",
                      marginVertical: 4,
                      padding: 8,
                      backgroundColor: "rgba(238, 242, 255, 1)",
                    }}
                  >
                    <View
                      style={{
                        overflow: "hidden",
                        height: 60,
                        width: 60,
                        borderRadius: 30,
                      }}
                    >
                      <PassioIconView
                        style={{
                          height: 60,
                          width: 60,
                        }}
                        config={{
                          passioID: item.iconId,
                          iconSize: IconSize.PX360,
                        }}
                      />
                    </View>
                    <View
                      style={{
                        alignSelf: "center",
                      }}
                    >
                      <Text
                        style={{
                          marginHorizontal: 16,
                          fontWeight: "500",
                          fontSize: 14,
                        }}
                      >
                        {item.name}
                      </Text>
                      <Text
                        style={{
                          marginHorizontal: 16,
                        }}
                      >
                        {item.amount?.selectedUnit}{" "}
                      </Text>
                    </View>
                  </View>
                )}
              />
            </>
          )}
      </View>
    );
  };

Check out the full code here. FULL CODE

Last updated