Skip to content
Merged
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
40 changes: 40 additions & 0 deletions lib/helpers/date.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (C) wger Team
*
* wger Workout Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* wger Workout Manager is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/// Returns a timezone aware DateTime object from a date and time string.
DateTime getDateTimeFromDateAndTime(String date, String time) {
return DateTime.parse('$date $time');
}

/// Returns a list of [DateTime] objects from [first] to [last], inclusive.
List<DateTime> daysInRange(DateTime first, DateTime last) {
final dayCount = last.difference(first).inDays + 1;
return List.generate(
dayCount,
(index) => DateTime.utc(first.year, first.month, first.day + index),
);
}

extension DateTimeExtension on DateTime {
bool isSameDayAs(DateTime other) {
final thisDay = DateTime(year, month, day);
final otherDay = DateTime(other.year, other.month, other.day);

return thisDay.isAtSameMomentAs(otherDay);
}
}
9 changes: 9 additions & 0 deletions lib/helpers/json.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ String? dateToYYYYMMDD(DateTime? dateTime) {
return DateFormat('yyyy-MM-dd').format(dateTime);
}

/// Convert a date to UTC and then to an ISO8601 string.
///
/// This makes sure that the serialized data has correct timezone information.
/// Otherwise the django backend will possibly treat the date as local time,
/// which will not be correct in most cases.
String dateToUtcIso8601(DateTime dateTime) {
return dateTime.toUtc().toIso8601String();
}

/*
* Converts a time to a date object.
* Needed e.g. when the wger api only sends a time but no date information.
Expand Down
32 changes: 0 additions & 32 deletions lib/helpers/misc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,38 +63,6 @@ String repText(
return out.join(' ');
}

/// Returns a list of [DateTime] objects from [first] to [last], inclusive.
List<DateTime> daysInRange(DateTime first, DateTime last) {
final dayCount = last.difference(first).inDays + 1;
return List.generate(
dayCount,
(index) => DateTime.utc(first.year, first.month, first.day + index),
);
}

extension TimeOfDayExtension on TimeOfDay {
bool isAfter(TimeOfDay other) {
return toMinutes() > other.toMinutes();
}

bool isBefore(TimeOfDay other) {
return toMinutes() < other.toMinutes();
}

int toMinutes() {
return (hour * 60) + minute;
}
}

extension DateTimeExtension on DateTime {
bool isSameDayAs(DateTime other) {
final thisDay = DateTime(year, month, day);
final otherDay = DateTime(other.year, other.month, other.day);

return thisDay.isAtSameMomentAs(otherDay);
}
}

void launchURL(String url, BuildContext context) async {
final scaffoldMessenger = ScaffoldMessenger.of(context);
final launched = await launchUrl(Uri.parse(url));
Expand Down
2 changes: 1 addition & 1 deletion lib/models/nutrition/log.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Log {
@JsonKey(required: true, name: 'plan')
int planId;

@JsonKey(required: true)
@JsonKey(required: true, toJson: dateToUtcIso8601)
late DateTime datetime;

String? comment;
Expand Down
2 changes: 1 addition & 1 deletion lib/models/nutrition/log.g.dart

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

2 changes: 1 addition & 1 deletion lib/models/nutrition/meal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/helpers/misc.dart';
import 'package:wger/models/nutrition/log.dart';
import 'package:wger/models/nutrition/meal_item.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';
Expand Down
2 changes: 1 addition & 1 deletion lib/models/nutrition/nutritional_plan.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class NutritionalPlan {
@JsonKey(required: true)
late String description;

@JsonKey(required: true, name: 'creation_date', toJson: dateToYYYYMMDD)
@JsonKey(required: true, name: 'creation_date', toJson: dateToUtcIso8601)
late DateTime creationDate;

@JsonKey(required: true, name: 'only_logging')
Expand Down
2 changes: 1 addition & 1 deletion lib/models/nutrition/nutritional_plan.g.dart

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

2 changes: 1 addition & 1 deletion lib/models/workouts/log.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Log {
@JsonKey(includeFromJson: false, includeToJson: false)
late WeightUnit? weightUnitObj;

@JsonKey(required: true, toJson: dateToYYYYMMDD)
@JsonKey(required: true, toJson: dateToUtcIso8601)
late DateTime date;

Log({
Expand Down
2 changes: 1 addition & 1 deletion lib/models/workouts/log.g.dart

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

4 changes: 2 additions & 2 deletions lib/models/workouts/routine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
*/

import 'package:json_annotation/json_annotation.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/helpers/misc.dart';
import 'package:wger/models/workouts/day.dart';
import 'package:wger/models/workouts/day_data.dart';
import 'package:wger/models/workouts/log.dart';
Expand All @@ -42,7 +42,7 @@ class Routine {
@JsonKey(required: true, includeToJson: false)
int? id;

@JsonKey(required: true)
@JsonKey(required: true, toJson: dateToUtcIso8601)
late DateTime created;

@JsonKey(required: true, name: 'name')
Expand Down
2 changes: 1 addition & 1 deletion lib/models/workouts/routine.g.dart

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

2 changes: 1 addition & 1 deletion lib/providers/nutrition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ class NutritionPlansProvider with ChangeNotifier {
]) async {
final plan = findById(planId);
mealItem.ingredient = await fetchIngredient(mealItem.ingredientId);
final Log log = Log.fromMealItem(mealItem, plan.id!, null, dateTime);
final log = Log.fromMealItem(mealItem, plan.id!, null, dateTime);

final data = await baseProvider.post(
log.toJson(),
Expand Down
47 changes: 27 additions & 20 deletions lib/screens/log_meal_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/nutrition/meal.dart';
Expand Down Expand Up @@ -70,8 +71,10 @@ class _LogMealScreenState extends State<LogMealScreen> {
.toList(),
);

final i18n = AppLocalizations.of(context);

return Scaffold(
appBar: AppBar(title: Text(AppLocalizations.of(context).logMeal)),
appBar: AppBar(title: Text(i18n.logMeal)),
body: Consumer<NutritionPlansProvider>(
builder: (context, nutritionProvider, child) => SingleChildScrollView(
child: Padding(
Expand All @@ -83,7 +86,7 @@ class _LogMealScreenState extends State<LogMealScreen> {
style: Theme.of(context).textTheme.headlineSmall,
),
if (meal.mealItems.isEmpty)
ListTile(title: Text(AppLocalizations.of(context).noIngredientsDefined))
ListTile(title: Text(i18n.noIngredientsDefined))
else
Column(
children: [
Expand Down Expand Up @@ -113,7 +116,7 @@ class _LogMealScreenState extends State<LogMealScreen> {
child: TextFormField(
key: const ValueKey('field-date'),
readOnly: true,
decoration: InputDecoration(labelText: AppLocalizations.of(context).date),
decoration: InputDecoration(labelText: i18n.date),
enableInteractiveSelection: false,
controller: _dateController,
onTap: () async {
Expand All @@ -138,7 +141,7 @@ class _LogMealScreenState extends State<LogMealScreen> {
child: TextFormField(
key: const ValueKey('field-time'),
readOnly: true,
decoration: InputDecoration(labelText: AppLocalizations.of(context).time),
decoration: InputDecoration(labelText: i18n.time),
controller: _timeController,
onTap: () async {
// Open time picker
Expand All @@ -165,28 +168,32 @@ class _LogMealScreenState extends State<LogMealScreen> {
children: [
if (meal.mealItems.isNotEmpty)
TextButton(
child: const Text('Log'),
child: Text(i18n.save),
onPressed: () async {
final loggedTime = getDateTimeFromDateAndTime(
_dateController.text,
_timeController.text,
);

await Provider.of<NutritionPlansProvider>(
context,
listen: false,
).logMealToDiary(
meal,
DateTime.parse('${_dateController.text} ${_timeController.text}'),
);
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
// ignore: use_build_context_synchronously
AppLocalizations.of(context).mealLogged,
textAlign: TextAlign.center,
).logMealToDiary(meal, loggedTime);

if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
i18n.mealLogged,
textAlign: TextAlign.center,
),
),
),
);
Navigator.of(context).pop();
if (args.popTwice) {
);

Navigator.of(context).pop();
if (args.popTwice) {
Navigator.of(context).pop();
}
}
},
),
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/nutritional_plan_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class NutritionalPlanScreen extends StatelessWidget {
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).logIngredient,
IngredientLogForm(nutritionalPlan),
getIngredientLogForm(nutritionalPlan),
hasListView: true,
),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/dashboard/calendar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/helpers/misc.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/body_weight.dart';
import 'package:wger/providers/measurement.dart';
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/dashboard/widgets/nutrition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).logIngredient,
IngredientLogForm(_plan!),
getIngredientLogForm(_plan!),
hasListView: true,
),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/dashboard/widgets/routines.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/misc.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/workouts/day_data.dart';
import 'package:wger/models/workouts/routine.dart';
Expand Down
18 changes: 7 additions & 11 deletions lib/widgets/nutrition/forms.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/nutrition/ingredient.dart';
Expand Down Expand Up @@ -114,7 +115,7 @@ class MealForm extends StatelessWidget {
}
}

Widget MealItemForm(
Widget getMealItemForm(
Meal meal,
List<MealItem> recent, [
String? barcode,
Expand All @@ -133,7 +134,7 @@ Widget MealItemForm(
);
}

Widget IngredientLogForm(NutritionalPlan plan) {
Widget getIngredientLogForm(NutritionalPlan plan) {
return IngredientForm(
recent: plan.dedupDiaryEntries,
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
Expand Down Expand Up @@ -397,16 +398,11 @@ class IngredientFormState extends State<IngredientForm> {
_form.currentState!.save();
_mealItem.ingredientId = int.parse(_ingredientIdController.text);

var date = DateTime.parse(_dateController.text);
final tod = stringToTime(_timeController.text);
date = DateTime(
date.year,
date.month,
date.day,
tod.hour,
tod.minute,
final loggedDate = getDateTimeFromDateAndTime(
_dateController.text,
_timeController.text,
);
widget.onSave(context, _mealItem, date);
widget.onSave(context, _mealItem, loggedDate);

Navigator.of(context).pop();
},
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/nutrition/meal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class _MealWidgetState extends State<MealWidget> {
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).addIngredient,
MealItemForm(widget._meal, widget._recentMealItems),
getMealItemForm(widget._meal, widget._recentMealItems),
hasListView: true,
),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/routines/day.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

import 'package:flutter/material.dart';
import 'package:wger/helpers/misc.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/exercises/exercise.dart';
import 'package:wger/models/workouts/day_data.dart';
Expand Down
Loading