Exemple #1
0
        async void RefreshFromUserDataAsync()
        {
            try
            {
                IncrementPendingRequestCount(); // show loading

                // do async data read
                var todaysWeightEntry = await DataService.GetWeightEntryForDate(DateTime.Today.Date);

                var goal = await DataService.GetGoal();

                // update the UI color (NOTE:: both goal and todaysWeightEntry could be null)
                var infoForToday = WeightLogicHelpers.GetTodaysDisplayInfo(goal, todaysWeightEntry);
                WindowColorService.ChangeAppBaseColor(infoForToday.ColorToShow);
            }
            catch (Exception ex)
            {
                AnalyticsService.TrackFatalError($"{nameof(RefreshFromUserDataAsync)} - an exception occurred.", ex);
                // NOTE:: not showing an error here as this is not in response to user action. potentially should show a non-intrusive error banner
            }
            finally
            {
                DecrementPendingRequestCount(); // hide loading
            }
        }
        async void RefreshFromUserDataAsync()
        {
            try
            {
                IncrementPendingRequestCount(); // show loading

                IsWeightListingVisible = false;
                PlaceholderText        = Constants.Strings.GraphPage_PlaceholderText_Loading; // defaults to 'loading..' as it is seen briefly

                // do async data read
                var todaysWeightEntry = await DataService.GetWeightEntryForDate(DateTime.Today.Date);

                var goal = await DataService.GetGoal();

                // update the UI color (NOTE:: both goal and todaysWeightEntry could be null)
                var infoForToday = WeightLogicHelpers.GetTodaysDisplayInfo(goal, todaysWeightEntry);
                WindowColorService.ChangeAppBaseColor(infoForToday.ColorToShow);

                // update the listing of weight entries
                var latestWeightEntries = await DataService.GetLatestWeightEntries(Constants.App.WeightListingMaxCount)
                                          .ConfigureAwait(false) as List <WeightEntry>;

                IsWeightListingVisible = latestWeightEntries.Count > 0; // will show/hide list and placeholder label
                PlaceholderText        = IsWeightListingVisible ? string.Empty : Constants.Strings.GraphPage_PlaceholderText_NoEntries;

                // create a lookup of success/failure values for weight entries and set on converter (used for showing/hiding checkmark per row)
                Dictionary <WeightEntry, bool> successForDateLookup = new Dictionary <WeightEntry, bool>();
                foreach (var entry in latestWeightEntries)
                {
                    bool success = WeightLogicHelpers.WeightMetGoalOnDate(goal, entry.Date, entry.Weight);
                    successForDateLookup.Add(entry, success);
                }
                CheckmarkVisibilityConverter.SuccessForDateLookup = successForDateLookup;

                // NOTE:: Updates to observable collections must happen on UI thread
                Device.BeginInvokeOnMainThread(() =>
                {
                    LatestWeightEntries = (latestWeightEntries == null) ?
                                          LatestWeightEntries = new ObservableCollection <WeightEntry>() :
                                                                LatestWeightEntries = new ObservableCollection <WeightEntry>(latestWeightEntries);
                });

                // NOTE:: we limit how many points are graphed as it can be a performance concern if too high, and OxyPlot appears to stop drawing connecting lines beyond ~530 items
                int maxGraphPoints = (Device.RuntimePlatform == Device.Android) ? Constants.App.WeightGraphingMaxCount_Android :
                                     Constants.App.WeightGraphingMaxCount;
                var limitedEntries = latestWeightEntries.OrderByDescending(x => x.Date).Take(maxGraphPoints).ToList();
                RefreshGraphDataModel(limitedEntries, goal);
            }
            catch (Exception ex)
            {
                AnalyticsService.TrackFatalError($"{nameof(RefreshFromUserDataAsync)} - an exception occurred.", ex);
                // NOTE:: not showing an error here as this is not in response to user action. potentially should show a non-intrusive error banner
            }
            finally
            {
                DecrementPendingRequestCount(); // hide loading
            }
        }
        async void RefreshFromUserDataAsync()
        {
            if (_isFirstStartup)
            {
                _isFirstStartup = false;

                // if we need to show the welcome flow don't bother updating data (will be updated when this page appears again)
                if (await ShowWelcomeFlowIfNeeded())
                {
                    return;
                }
            }

            try
            {
                IncrementPendingRequestCount(); // show loading

                // do async data read
                var todaysWeightEntry = await DataService.GetWeightEntryForDate(DateTime.Today.Date);

                var goal = await DataService.GetGoal();

                // get all the info needed to update the UI (NOTE:: both goal and todaysWeightEntry could be null)
                var infoForToday = WeightLogicHelpers.GetTodaysDisplayInfo(goal, todaysWeightEntry);

                TodaysWeight  = infoForToday.TodaysDisplayWeight;
                TodaysMessage = infoForToday.TodaysMessage;
                HowToEatText  = infoForToday.HowToEatMessage;

                IsEnterWeightButtonVisible = infoForToday.IsEnterWeightButtonVisible;
                IsSetGoalButtonVisible     = infoForToday.IsSetGoalButtonVisible;

                MainLabelFontSize = SettingsService.WeightUnit == Enumerations.WeightUnitEnum.StonesAndPounds ?
                                    Constants.UI.DailyInfoFontSize_Stones : Constants.UI.DailyInfoFontSize_Normal;
                WindowColorService.ChangeAppBaseColor(infoForToday.ColorToShow);
            }
            catch (Exception ex)
            {
                AnalyticsService.TrackFatalError($"{nameof(RefreshFromUserDataAsync)} - an exception occurred.", ex);
                // NOTE:: not showing an error here as this is not in response to user action. potentially should show a non-intrusive error banner
            }
            finally
            {
                DecrementPendingRequestCount(); // hide loading
            }
        }
        void RefreshGraphDataModel(List <WeightEntry> entries, WeightLossGoal goal)
        {
            // TODO:: FUTURE:: does all of this have to be done for each refresh? could the axis's be re-used and just
            // have the data points repopulated?
            var      dateRange      = WeightLogicHelpers.GetGraphDateRange(entries);
            DateTime dateRangeStart = dateRange.Item1;
            DateTime dateRangeEnd   = dateRange.Item2;

            var     weightRange    = WeightLogicHelpers.GetMinMaxWeightRange(goal, entries, dateRangeStart, dateRangeEnd);
            decimal minGraphWeight = weightRange.Item1;
            decimal maxGraphWeight = weightRange.Item2;

            // OxyPlot
            var plotModel = new PlotModel(); //  { Title = "OxyPlot Demo" };

            plotModel.IsLegendVisible     = false;
            plotModel.PlotAreaBorderColor = GRID_LINES_COLOR_BORDER;

            // NOTE:: it is assumed the date will always be the first axes in the AxisChanged wiring/un-wiring as that is used
            // to adjust line markers to show months instead of weeks at a certain zoom level

            // XAxis - dates
            plotModel.Axes.Add(
                new DateTimeAxis
            {
                Position           = AxisPosition.Bottom,
                Minimum            = DateTimeAxis.ToDouble(dateRangeStart),
                Maximum            = DateTimeAxis.ToDouble(dateRangeEnd),
                AxislineColor      = OxyColors.White,
                TicklineColor      = OxyColors.White,
                MajorGridlineColor = GRID_LINES_COLOR_MAJOR,
                MinorGridlineColor = GRID_LINES_COLOR_MINOR,
                AxislineStyle      = LineStyle.Solid,
                TextColor          = OxyColors.White,
                TickStyle          = TickStyle.Outside,
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Solid,
                MinorIntervalType  = DateTimeIntervalType.Days,
                IntervalType       = DateTimeIntervalType.Days,
                MinorStep          = WeekScale_MinorStep,
                MajorStep          = WeekScale_MajorStep,                     // a week
                StringFormat       = WeekScale_AxisFormat,                    // TODO:: FUTURE:: make swap for some cultures?
                IsZoomEnabled      = true,
                MinimumRange       = Constants.App.Graph_MinDateRangeVisible, // closest zoom in shows at least 5 days
                MaximumRange       = Constants.App.Graph_MaxDateRangeVisible, // furthest zoom out shows at most 1 year
            });

            // YAxis - weights
            plotModel.Axes.Add(
                new LinearAxis
            {
                Position           = AxisPosition.Left,
                Minimum            = (double)minGraphWeight,
                Maximum            = (double)maxGraphWeight,
                AxislineColor      = OxyColors.White,
                TicklineColor      = OxyColors.White,
                MajorGridlineColor = GRID_LINES_COLOR_MAJOR,
                MinorGridlineColor = GRID_LINES_COLOR_MINOR,
                AxislineStyle      = LineStyle.Solid,
                TextColor          = OxyColors.White,
                TickStyle          = TickStyle.Outside,
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Solid,
                MinorStep          = 1,
                MajorStep          = 5,
                IsZoomEnabled      = true,
                MinimumRange       = Constants.App.Graph_MinWeightRangeVisible, // closest zoom in shows at least 5 pounds
                MaximumRange       = Constants.App.Graph_MaxWeightRangeVisible  // furthest zoom out shows at most 100 pounds
            });

            var series1 = new LineSeries
            {
                MarkerType            = MarkerType.Circle,
                MarkerSize            = 4,
                MarkerStroke          = OxyColors.White,
                MarkerFill            = OxyColors.White,
                StrokeThickness       = 4,
                MarkerStrokeThickness = 1,
                Color = OxyColors.White
            };

            foreach (var entry in entries)
            {
                series1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(entry.Date), Decimal.ToDouble(entry.Weight)));
            }

            plotModel.Series.Clear();
            plotModel.Series.Add(series1);

            // setup goal line
            if (goal != null)
            {
                var series2 = new LineSeries
                {
                    MarkerType = MarkerType.None,
                    LineStyle  = LineStyle.Dash,
                    Color      = OxyColors.White
                };

                // diet line

                // we want to extend the goal line at least 30 days past the end of the goal and extend it further if they are already past
                // the goal date (ex: they are 90 days past goal end date and just using the line for maintenance)
                var goalExtendedDate = goal.GoalDate + TimeSpan.FromDays(30);
                if (DateTime.Today.Date > goalExtendedDate)
                {
                    goalExtendedDate = DateTime.Today.Date + TimeSpan.FromDays(30);
                }

                series2.Points.Add(new DataPoint(DateTimeAxis.ToDouble(goal.StartDate - TimeSpan.FromDays(30)), (double)goal.StartWeight));
                series2.Points.Add(new DataPoint(DateTimeAxis.ToDouble(goal.StartDate), (double)goal.StartWeight));
                series2.Points.Add(new DataPoint(DateTimeAxis.ToDouble(goal.GoalDate), (double)goal.GoalWeight));
                series2.Points.Add(new DataPoint(DateTimeAxis.ToDouble(goalExtendedDate), (double)goal.GoalWeight));

                plotModel.Series.Add(series2);
            }

            UnwireDateAxisChangedEvent(); // unwire any previous event handlers for axis zoom changing
            PlotModel = plotModel;
            PlotModel.Axes[0].AxisChanged += DateAxis_Changed;
        }
        public async Task <ResultWithErrorText> ChangeWeightAndGoalUnits(WeightUnitEnum newUnits, bool convertValues)
        {
            // convert all values
            bool   success   = true;
            string errorText = string.Empty;

            var allEntries = await GetAllWeightEntries();

            if (allEntries == null)
            {
                AnalyticsService.TrackFatalError($"{nameof(ChangeWeightAndGoalUnits)} - an error occurred trying to get weights!");
                success   = false;
                errorText = Constants.Strings.DataService_ChangeWeightAndGoalUnits_UnableToGetWeights;
            }
            else
            {
                var weightsWithDifferentUnits = allEntries.Where(w => w.WeightUnit != newUnits);
                if (weightsWithDifferentUnits.Any())
                {
                    foreach (var weight in weightsWithDifferentUnits)
                    {
                        if (!await RemoveWeightEntryForDate(weight.Date.Date))
                        {
                            AnalyticsService.TrackFatalError($"{nameof(ChangeWeightAndGoalUnits)} - an error occurred removing weight entry for date {weight.Date.Date}!");
                            success   = false;
                            errorText = string.Format(Constants.Strings.DataService_ChangeWeightAndGoalUnits_FailedRemovingWeight, weight.Date.Date);
                            break;
                        }

                        if (convertValues)
                        {
                            weight.Weight = WeightLogicHelpers.ConvertWeightUnits(weight.Weight, weight.WeightUnit, newUnits);
                        }

                        weight.WeightUnit = newUnits;
                        if (!await AddWeightEntry(weight))
                        {
                            AnalyticsService.TrackFatalError($"{nameof(ChangeWeightAndGoalUnits)} - an error occurred adding weight entry for date {weight.Date.Date}!");
                            success   = false;
                            errorText = string.Format(Constants.Strings.DataService_ChangeWeightAndGoalUnits_FailedAddingWeight, weight.Date.Date);
                            break;
                        }
                    }
                }

                // if we have converted all weights, now update the goal (if exists)
                if (success)
                {
                    var goal = await GetGoal();

                    if (goal != null)
                    {
                        if (convertValues)
                        {
                            goal.StartWeight = WeightLogicHelpers.ConvertWeightUnits(goal.StartWeight, goal.WeightUnit, newUnits);
                            goal.GoalWeight  = WeightLogicHelpers.ConvertWeightUnits(goal.GoalWeight, goal.WeightUnit, newUnits);
                        }
                        goal.WeightUnit = newUnits;

                        if (!await RemoveGoal())
                        {
                            AnalyticsService.TrackFatalError($"{nameof(ChangeWeightAndGoalUnits)} - an error occurred removing previous goal!");
                            errorText = Constants.Strings.DataService_ChangeWeightAndGoalUnits_FailedRemovingGoal;
                            success   = false;
                        }
                        else
                        {
                            if (!await SetGoal(goal))
                            {
                                AnalyticsService.TrackFatalError($"{nameof(ChangeWeightAndGoalUnits)} - an error occurred adding new goal!");
                                errorText = Constants.Strings.DataService_ChangeWeightAndGoalUnits_FailedAddingGoal;
                                success   = false;
                            }
                        }
                    }
                }
            }

            return(new ResultWithErrorText(success, errorText));
        }