Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
45 changes: 45 additions & 0 deletions lib/helpers/date.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,51 @@ List<DateTime> daysInRange(DateTime first, DateTime last) {
);
}

/// Formats a Duration into a human-readable string with years, months, and days
String humanDuration(DateTime startDate, DateTime endDate) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just use an external package for this (one of the very first results was https://pub.dev/packages/humanize_duration), then we would also have i18n solved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these types of libraries don't do it the way i think makes sense.
e.g. day A you reported a weight at 9:20 AM, one day later you weigh in at 9:00 AM, i think that should qualify as "a day"

var years = endDate.year - startDate.year;
var months = endDate.month - startDate.month;
var days = endDate.day - startDate.day;

if (months < 0) {
months += 12;
years -= 1;
}

// if we overcounted the days, it's a bit trickier to solve than overcounting months
// e.g. consider a start date february 10 and end date june 8
// -> days = -2
// -> months = 4
// the proper answer can be thought of in 3 ways:
// * count whole months first, then days: from febr 10 to may 10, and then to june 8. that's 3 months + (num_days_in_may - 10 + 8 days)
// * count days first, then whole months: from febr 10 to march 8, and then to june 8. that's (num_days_in_feb - 10 + 8 days) + 3 months
// * technically, one can also use any month to adjust the day: e.g.
// count months from febr 10 to april 10, count days to may 8, and count months again to june 8.
// probably no-one uses the last method. The first approach seems most natural.
// it means we need to know the number of days (aka the last day index) of the month before endDate
// which we can do by setting the day to 0 and asking for the day
if (days < 0) {
days += DateTime(endDate.year, endDate.month, 0).day;
months -= 1;
}

final parts = <String>[];
if (years > 0) {
parts.add('$years year${years == 1 ? '' : 's'}');
}
if (months > 0) {
parts.add('$months month${months == 1 ? '' : 's'}');
}
if (days > 0) {
parts.add('$days day${days == 1 ? '' : 's'}');
}

if (parts.isEmpty) {
return 'Duration: 0 days';
}
return 'Duration: ${parts.join(', ')}';
}

extension DateTimeExtension on DateTime {
bool isSameDayAs(DateTime other) {
final thisDay = DateTime(year, month, day);
Expand Down
2 changes: 1 addition & 1 deletion lib/providers/nutrition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class NutritionPlansProvider with ChangeNotifier {
for (final planData in data) {
final plan = NutritionalPlan.fromJson(planData);
_plans.add(plan);
_plans.sort((a, b) => b.creationDate.compareTo(a.creationDate));
_plans.sort((a, b) => a.startDate.compareTo(b.startDate));
}
notifyListeners();
}
Expand Down
30 changes: 20 additions & 10 deletions lib/widgets/nutrition/nutritional_plans_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/measurements.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/body_weight.dart';
Expand Down Expand Up @@ -48,21 +49,30 @@ class NutritionalPlansList extends StatelessWidget {
final lastWeight = entries7dAvg.last;
final weightDifference = lastWeight.value - firstWeight.value;

// Calculate the time period in weeks
final timeDifference = lastWeight.date.difference(firstWeight.date);
final weeklyRate =
weightDifference / (timeDifference.inDays == 0 ? 1 : timeDifference.inDays) * 7;

// Format the weight change text and determine color
final String weightChangeText;
final String weeklyRateText;
final Color weightChangeColor;
final profile = context.read<UserProvider>().profile;

final unit = weightUnit(profile!.isMetric, context);

if (weightDifference > 0) {
weightChangeText = '+${weightDifference.toStringAsFixed(1)} $unit';
weeklyRateText = '+${weeklyRate.toStringAsFixed(2)} $unit';
weightChangeColor = Colors.red;
} else if (weightDifference < 0) {
weightChangeText = '${weightDifference.toStringAsFixed(1)} $unit';
weeklyRateText = '${weeklyRate.toStringAsFixed(2)} $unit';
weightChangeColor = Colors.green;
} else {
weightChangeText = '0 $unit';
weeklyRateText = '0 $unit';
weightChangeColor = Colors.grey;
}

Expand All @@ -71,15 +81,11 @@ class NutritionalPlansList extends StatelessWidget {
child: Row(
children: [
Text(
'${AppLocalizations.of(context).weight} change: ',
style: Theme.of(context).textTheme.bodySmall,
'${AppLocalizations.of(context).weight}: ',
),
Text(
weightChangeText,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.bold,
color: weightChangeColor,
),
'$weightChangeText ($weeklyRateText/week)',
style: TextStyle(color: weightChangeColor),
),
],
),
Expand Down Expand Up @@ -109,14 +115,18 @@ class NutritionalPlansList extends StatelessWidget {
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
humanDuration(
currentPlan.startDate, currentPlan.endDate ?? DateTime.now()),
),
Text(
currentPlan.endDate != null
? 'from ${DateFormat.yMd(
? 'From: ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.startDate)} to ${DateFormat.yMd(
).format(currentPlan.startDate)} To: ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.endDate!)}'
: 'from ${DateFormat.yMd(
: 'From: ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.startDate)} (open ended)',
),
Expand Down
8 changes: 4 additions & 4 deletions test/nutrition/nutritional_plans_screen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,15 @@ void main() {
await tester.pumpWidget(createHomeScreen());

// note .. "(open ended)" at the time, depending on localisation strings
expect(find.textContaining('from 1/1/2021 ('), findsOneWidget);
expect(find.textContaining('from 1/10/2021 ('), findsOneWidget);
expect(find.textContaining('From: 1/1/2021 ('), findsOneWidget);
expect(find.textContaining('From: 1/10/2021 ('), findsOneWidget);
});

testWidgets('Tests the localization of dates - DE', (WidgetTester tester) async {
await tester.pumpWidget(createHomeScreen(locale: 'de'));
// note .. "(open ended)" at the time, depending on localisation strings

expect(find.textContaining('from 1.1.2021 ('), findsOneWidget);
expect(find.textContaining('from 10.1.2021 ('), findsOneWidget);
expect(find.textContaining('From: 1.1.2021 ('), findsOneWidget);
expect(find.textContaining('From: 10.1.2021 ('), findsOneWidget);
});
}