Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@careevolution/mydatahelps-ui",
"version": "3.21.2",
"version": "3.21.3-meal-logging2.1",
"description": "MyDataHelps UI Library",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
78 changes: 54 additions & 24 deletions src/components/container/MealEditor/MealEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { CSSProperties, useContext, useEffect, useRef, useState } from 'react';
import './MealEditor.css';
import { getMealImageUrls, getMeals, getMealToEdit, getMealTypeDisplayText, language, Meal, resolveColor, saveMeals, timestampSortAsc, uploadMealImageFile } from '../../../helpers';
import { getMealImageUrls, getMeals, getMealToEdit, getMealTypeDisplayText, language, Meal, resolveColor, saveMeals, timestampSortAsc, uploadMealImageFile, logMealEvent } from '../../../helpers';
import { Button, Card, LayoutContext, LoadingIndicator, UnstyledButton } from '../../presentational';
import { format, parse, startOfDay } from 'date-fns';
import { createPreviewData, MealEditorPreviewState } from './MealEditor.previewData';
Expand All @@ -9,6 +9,7 @@ import { faEdit, faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons';
import { getFullDayAndDateString } from '../../../helpers/date-helpers';
import { combineItemsWithAnalysisItems, itemSortByNameAsc } from '../../../helpers/glucose-and-meals/meals';
import MealAnalysis from '../../presentational/MealAnalysis';
import MyDataHelps, { DeviceInfo } from "@careevolution/mydatahelps-js";

export interface MealEditorProps {
previewState?: 'loading' | MealEditorPreviewState;
Expand Down Expand Up @@ -36,9 +37,15 @@ export default function MealEditor(props: MealEditorProps) {
const [newImageFile, setNewImageFile] = useState<File>();
const [imageTypeError, setImageTypeError] = useState<boolean>(false);
const [imageUploadError, setImageUploadError] = useState<boolean>(false);
const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>();

const itemsToAddInputRef = useRef<HTMLInputElement>(null);

const onError = (message: string) => {
logMealEvent("meal-error", mealToEdit, deviceInfo, message);
props.onError();
}

useEffect(() => {
setLoading(true);
setAllMeals([]);
Expand All @@ -65,28 +72,32 @@ export default function MealEditor(props: MealEditorProps) {
return;
}

getMealToEdit().then(mealReference => {
if (mealReference) {
getMeals(mealReference.date).then(async allMeals => {
const activeMeals = allMeals.filter(meal => !meal.archiveTimestamp);
const referencedMeal = activeMeals.find(meal => meal.id === mealReference.id);
if (referencedMeal) {
const imageUrls = await getMealImageUrls([referencedMeal]);
const imageUrl = imageUrls[referencedMeal.id.toString()];

setLoading(false);
setAllMeals(allMeals);
setActiveMeals(activeMeals);
setMealToEdit(referencedMeal);
setImageUrl(imageUrl);
setImageLoading(!!imageUrl);
} else {
MyDataHelps.getDeviceInfo().then(info => {
setDeviceInfo(info);

getMealToEdit().then(mealReference => {
if (mealReference) {
Comment thread
greinard marked this conversation as resolved.
getMeals(mealReference.date).then(async allMeals => {
const activeMeals = allMeals.filter(meal => !meal.archiveTimestamp);
const referencedMeal = activeMeals.find(meal => meal.id === mealReference.id);
if (referencedMeal) {
const imageUrls = await getMealImageUrls([referencedMeal]);
const imageUrl = imageUrls[referencedMeal.id.toString()];

setLoading(false);
setAllMeals(allMeals);
setActiveMeals(activeMeals);
setMealToEdit(referencedMeal);
setImageUrl(imageUrl);
setImageLoading(!!imageUrl);
} else {
props.onError();
Comment thread
greinard marked this conversation as resolved.
Outdated
}
});
} else {
props.onError();
}
}
});
} else {
onError("No meal reference provided.");
Comment thread
greinard marked this conversation as resolved.
Outdated
}
});
});
}, [props.previewState]);

Expand All @@ -102,6 +113,8 @@ export default function MealEditor(props: MealEditorProps) {
return;
}

logMealEvent("delete", mealToEdit, deviceInfo);

setLoading(true);

mealToEdit.archiveTimestamp = new Date();
Expand All @@ -118,14 +131,18 @@ export default function MealEditor(props: MealEditorProps) {
return;
}

logMealEvent("save", mealToEdit, deviceInfo);

setLoading(true);
setImageTypeError(false);
setImageUploadError(false);

if (newImageFile) {
logMealEvent("uploading-image", mealToEdit, deviceInfo, { name: newImageFile.name, size: newImageFile.size });
try {
await uploadMealImageFile(mealToEdit, newImageFile);
} catch {
Comment thread
greinard marked this conversation as resolved.
} catch (err) {
logMealEvent("image-upload-error", mealToEdit, deviceInfo, { name: newImageFile.name, size: newImageFile.size, error: err });
setLoading(false);
setImageUploadError(true);
return;
Expand All @@ -143,6 +160,8 @@ export default function MealEditor(props: MealEditorProps) {
const otherMeals = allMeals.filter(meal => meal.id !== mealToEdit.id);
const updatedMeals = [...otherMeals, mealToEdit].sort(timestampSortAsc);

logMealEvent("saving-meals", mealToEdit, deviceInfo);

saveMeals(startOfDay(mealToEdit.timestamp), updatedMeals).then(props.onSave);
};

Expand All @@ -162,6 +181,8 @@ export default function MealEditor(props: MealEditorProps) {
};

const onFileChanged = (file: File | undefined) => {
logMealEvent("change-file", mealToEdit, deviceInfo);

if (file) {
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp', 'image/webp'];
if (allowedTypes.includes(file.type)) {
Expand All @@ -170,13 +191,17 @@ export default function MealEditor(props: MealEditorProps) {
setImageUrl(URL.createObjectURL(file));
setImageTypeError(false);
setImageUploadError(false);
logMealEvent("image-added", mealToEdit, deviceInfo, `Image type=${file.type} size=${file.size}`);
} else {
logMealEvent("image-type-error", mealToEdit, deviceInfo, `Invalid image type ${file.type}`);
setImageTypeError(true);
}
}
};

const onRemoveImage = () => {
logMealEvent("remove-image", mealToEdit, deviceInfo);

setMealToEdit({ ...mealToEdit, hasImage: false });
setNewImageFile(undefined);
setImageUrl(undefined);
Expand Down Expand Up @@ -211,6 +236,11 @@ export default function MealEditor(props: MealEditorProps) {
setMealToEdit({ ...mealToEdit, items: updatedItems });
};

const onCancel = () => {
logMealEvent("cancel", mealToEdit, deviceInfo);
props.onCancel();
};

const colorStyles = {
'--mdhui-meal-editor-image-action-background-color': resolveColor(layoutContext.colorScheme, {
lightMode: 'var(--mdhui-meal-editor-image-action-background-color-light)',
Expand Down Expand Up @@ -341,7 +371,7 @@ export default function MealEditor(props: MealEditorProps) {
{hasDuplicateTimestamp() && <div className="mdhui-meal-editor-error">{language('meal-editor-duplicate-timestamp-error')}</div>}
{imageUploadError && <div className="mdhui-meal-editor-error">{language('meal-editor-image-upload-error')}</div>}
<div className="mdhui-meal-editor-buttons">
<Button onClick={() => props.onCancel()} variant="light">{language('cancel')}</Button>
<Button onClick={() => onCancel()} variant="light">{language('cancel')}</Button>
<Button onClick={() => onSave()} disabled={hasDuplicateTimestamp()}>{language('save')}</Button>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/glucose-and-meals/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { getGlucoseReadings, computeBestFitGlucoseValue, computeGlucoseReadingRanges, computeGlucoseReadingRecentAverage } from './glucose';
export { getMeals, saveMeals, prepareMealForEditing, getMealToEdit, uploadMealImageFile, getMealImageUrls } from './meals';
export { getMeals, saveMeals, prepareMealForEditing, getMealToEdit, uploadMealImageFile, getMealImageUrls, logMealEvent } from './meals';
export { generateGlucose, generateSteps, generateSleep } from './sample-data';
export { getSleepMinutes } from './sleep';
export { getSteps } from './steps';
Expand Down
15 changes: 14 additions & 1 deletion src/helpers/glucose-and-meals/meals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import MyDataHelps, { PersistableDeviceDataPoint } from '@careevolution/mydatahelps-js';
import MyDataHelps, { PersistableDeviceDataPoint, DeviceInfo } from '@careevolution/mydatahelps-js';
import { add, compareDesc, endOfDay, parseISO, startOfDay } from 'date-fns';
import { Meal, MealItem, MealReference } from './types';
import { timestampSortAsc } from './util';
Expand Down Expand Up @@ -135,4 +135,17 @@ export function itemSortByNameAsc(item1: MealItem, item2: MealItem): number {

export function itemSortByConfidenceDesc(item1: MealItem, item2: MealItem): number {
return (item2.confidenceScore ?? 0) - (item1.confidenceScore ?? 0);
}

export function logMealEvent(type: string, meal: Meal | undefined, deviceInfo: DeviceInfo | undefined, data: unknown = {}) {
MyDataHelps.trackCustomEvent({
eventType: "meal-event",
properties: {
type: type,
data: data,
meal: meal?.id,
platform: deviceInfo?.platform,
deviceId: deviceInfo?.identifier
},
});
}
Loading