mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 23:42:00 +01:00
Merge pull request #791 from wger-project/fix/delete-workout-logs
Properly update the UI when deleting workout logs
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const LIST_OF_COLORS8 = [
|
||||
Color(0xFF2A4C7D),
|
||||
@@ -41,4 +41,9 @@ Iterable<Color> generateChartColors(int nrOfItems) sync* {
|
||||
for (final color in colors) {
|
||||
yield color;
|
||||
}
|
||||
|
||||
// Always return black after generating nrOfItems colors
|
||||
while (true) {
|
||||
yield Colors.black;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ import 'package:logging/logging.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/exceptions/http_exception.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/exercises/translation.dart';
|
||||
import 'package:wger/models/workouts/log.dart';
|
||||
import 'package:wger/providers/routines.dart';
|
||||
|
||||
@@ -108,13 +106,7 @@ void showHttpExceptionErrorDialog(
|
||||
showDialog(context: context, builder: (context) => Container());
|
||||
}
|
||||
|
||||
dynamic showDeleteDialog(
|
||||
BuildContext context,
|
||||
String confirmDeleteName,
|
||||
Log log,
|
||||
Translation exercise,
|
||||
Map<Exercise, List<Log>> exerciseData,
|
||||
) async {
|
||||
void showDeleteDialog(BuildContext context, String confirmDeleteName, Log log) async {
|
||||
final res = await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext contextDialog) {
|
||||
@@ -124,19 +116,18 @@ dynamic showDeleteDialog(
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
key: const ValueKey('cancel-button'),
|
||||
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
|
||||
onPressed: () => Navigator.of(contextDialog).pop(),
|
||||
),
|
||||
TextButton(
|
||||
key: const ValueKey('delete-button'),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).delete,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
onPressed: () {
|
||||
exerciseData[exercise]!.removeWhere((el) => el.id == log.id);
|
||||
Provider.of<RoutinesProvider>(context, listen: false).deleteLog(
|
||||
log,
|
||||
);
|
||||
context.read<RoutinesProvider>().deleteLog(log.id!, log.routineId);
|
||||
|
||||
Navigator.of(contextDialog).pop();
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:wger/helpers/json.dart';
|
||||
import 'package:wger/helpers/misc.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/workouts/day.dart';
|
||||
import 'package:wger/models/workouts/day_data.dart';
|
||||
import 'package:wger/models/workouts/log.dart';
|
||||
@@ -177,28 +176,4 @@ class Routine {
|
||||
|
||||
return groupedLogs;
|
||||
}
|
||||
|
||||
/// Massages the log data to more easily present on the log overview
|
||||
///
|
||||
Map<DateTime, Map<String, dynamic>> get logData {
|
||||
final out = <DateTime, Map<String, dynamic>>{};
|
||||
for (final sessionData in sessions) {
|
||||
final date = sessionData.session.date;
|
||||
if (!out.containsKey(date)) {
|
||||
out[date] = {
|
||||
'session': sessionData.session,
|
||||
'exercises': <Exercise, List<Log>>{},
|
||||
};
|
||||
}
|
||||
|
||||
for (final log in sessionData.logs) {
|
||||
final exercise = log.exercise;
|
||||
if (!out[date]!['exercises']!.containsKey(exercise)) {
|
||||
out[date]!['exercises']![exercise] = <Log>[];
|
||||
}
|
||||
out[date]!['exercises']![exercise].add(log);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/workouts/log.dart';
|
||||
import 'package:wger/models/workouts/session.dart';
|
||||
|
||||
@@ -32,6 +33,17 @@ class WorkoutSessionApi {
|
||||
|
||||
WorkoutSessionApi({required this.session, this.logs = const []});
|
||||
|
||||
/// Returns all different exercises in the session
|
||||
///
|
||||
/// This is used to display the exercises in the session
|
||||
List<Exercise> get exercises {
|
||||
final Set<Exercise> exerciseSet = {};
|
||||
for (final log in logs) {
|
||||
exerciseSet.add(log.exercise);
|
||||
}
|
||||
return exerciseSet.toList();
|
||||
}
|
||||
|
||||
// Boilerplate
|
||||
factory WorkoutSessionApi.fromJson(Map<String, dynamic> json) =>
|
||||
_$WorkoutSessionApiFromJson(json);
|
||||
|
||||
@@ -673,13 +673,9 @@ class RoutinesProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}*/
|
||||
|
||||
Future<void> deleteLog(Log log) async {
|
||||
await baseProvider.deleteRequest(_logsUrlPath, log.id!);
|
||||
for (final workout in _routines) {
|
||||
for (final sessionData in workout.sessions) {
|
||||
sessionData.session.logs.removeWhere((element) => element.id == log.id);
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
Future<void> deleteLog(int logId, int routineId) async {
|
||||
_logger.fine('Deleting log ${logId}');
|
||||
await baseProvider.deleteRequest(_logsUrlPath, logId);
|
||||
await fetchAndSetRoutineFull(routineId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
import 'package:wger/providers/routines.dart';
|
||||
import 'package:wger/widgets/routines/app_bar.dart';
|
||||
import 'package:wger/widgets/core/app_bar.dart';
|
||||
import 'package:wger/widgets/routines/workout_logs.dart';
|
||||
|
||||
class WorkoutLogsScreen extends StatelessWidget {
|
||||
@@ -30,13 +29,12 @@ class WorkoutLogsScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final routine = ModalRoute.of(context)!.settings.arguments as Routine;
|
||||
final routineId = ModalRoute.of(context)!.settings.arguments as int;
|
||||
final routine = Provider.of<RoutinesProvider>(context).findById(routineId);
|
||||
|
||||
return Scaffold(
|
||||
appBar: RoutineDetailAppBar(routine),
|
||||
body: Consumer<RoutinesProvider>(
|
||||
builder: (context, value, child) => WorkoutLogs(routine),
|
||||
),
|
||||
appBar: EmptyAppBar(routine.name),
|
||||
body: WorkoutLogs(routine),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,15 +238,10 @@ class MealItemEditableFullTile extends StatelessWidget {
|
||||
|
||||
return NutritionTile(
|
||||
leading: IngredientAvatar(ingredient: _item.ingredient),
|
||||
title: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
'${_item.amount.toStringAsFixed(0)}$unit ${_item.ingredient.name}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
title: Text(
|
||||
'${_item.amount.toStringAsFixed(0)}$unit ${_item.ingredient.name}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
subtitle: (_viewMode != viewMode.withAllDetails && !_editing)
|
||||
? null
|
||||
|
||||
@@ -122,7 +122,7 @@ class RoutineDetailAppBar extends StatelessWidget implements PreferredSizeWidget
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
WorkoutLogsScreen.routeName,
|
||||
arguments: routine,
|
||||
arguments: routine.id,
|
||||
);
|
||||
|
||||
case _RoutineDetailBarOptions.delete:
|
||||
|
||||
@@ -22,7 +22,6 @@ import 'package:wger/helpers/colors.dart';
|
||||
import 'package:wger/helpers/misc.dart';
|
||||
import 'package:wger/helpers/ui.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/workouts/log.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
import 'package:wger/models/workouts/session.dart';
|
||||
@@ -116,18 +115,22 @@ class ExerciseLogChart extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
LogChartWidgetFl(_logs, _selectedDate),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
..._logs.keys.map((reps) {
|
||||
colors.moveNext();
|
||||
return Indicator(
|
||||
color: colors.current,
|
||||
text: formatNum(reps).toString(),
|
||||
isSquare: false,
|
||||
);
|
||||
}),
|
||||
],
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
..._logs.keys.map((reps) {
|
||||
colors.moveNext();
|
||||
|
||||
return Indicator(
|
||||
color: colors.current,
|
||||
text: formatNum(reps).toString(),
|
||||
isSquare: false,
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
],
|
||||
@@ -139,49 +142,42 @@ class DayLogWidget extends StatelessWidget {
|
||||
final DateTime _date;
|
||||
final Routine _routine;
|
||||
|
||||
final WorkoutSession _session;
|
||||
final Map<Exercise, List<Log>> _exerciseMap;
|
||||
|
||||
const DayLogWidget(this._date, this._exerciseMap, this._session, this._routine);
|
||||
const DayLogWidget(this._date, this._routine);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sessionApi =
|
||||
_routine.sessions.firstWhere((sessionApi) => sessionApi.session.date.isSameDayAs(_date));
|
||||
final exercises = sessionApi.exercises;
|
||||
|
||||
return Card(
|
||||
child: Column(
|
||||
children: [
|
||||
SessionInfo(_session),
|
||||
..._exerciseMap.keys.map((exercise) {
|
||||
SessionInfo(sessionApi.session),
|
||||
...exercises.map((exercise) {
|
||||
final translation =
|
||||
exercise.getTranslation(Localizations.localeOf(context).languageCode);
|
||||
return Column(
|
||||
children: [
|
||||
if (_exerciseMap[exercise]!.isNotEmpty)
|
||||
Text(
|
||||
translation.name,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
)
|
||||
else
|
||||
Container(),
|
||||
..._exerciseMap[exercise]!.map(
|
||||
(log) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(log.singleLogRepTextNoNl),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
showDeleteDialog(
|
||||
context,
|
||||
translation.name,
|
||||
log,
|
||||
translation,
|
||||
_exerciseMap,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
translation.name,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
...sessionApi.logs.where((l) => l.exerciseId == exercise.id).map(
|
||||
(log) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(log.singleLogRepTextNoNl),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
key: ValueKey('delete-log-${log.id}'),
|
||||
onPressed: () {
|
||||
showDeleteDialog(context, translation.name, log);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: ExerciseLogChart(
|
||||
|
||||
@@ -21,10 +21,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/workouts/log.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
import 'package:wger/models/workouts/session.dart';
|
||||
import 'package:wger/theme/theme.dart';
|
||||
import 'package:wger/widgets/routines/log.dart';
|
||||
|
||||
@@ -49,27 +46,19 @@ class _WorkoutLogsState extends State<WorkoutLogs> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).labelWorkoutLogs,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context).labelWorkoutLogs,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).logHelpEntries,
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context).logHelpEntries,
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).logHelpEntriesUnits,
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context).logHelpEntriesUnits,
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
@@ -83,10 +72,8 @@ class _WorkoutLogsState extends State<WorkoutLogs> {
|
||||
/// An event in the workout log calendar
|
||||
class WorkoutLogEvent {
|
||||
final DateTime dateTime;
|
||||
final WorkoutSession session;
|
||||
final Map<Exercise, List<Log>> exercises;
|
||||
|
||||
const WorkoutLogEvent(this.dateTime, this.session, this.exercises);
|
||||
const WorkoutLogEvent(this.dateTime);
|
||||
}
|
||||
|
||||
class WorkoutLogCalendar extends StatefulWidget {
|
||||
@@ -101,8 +88,8 @@ class WorkoutLogCalendar extends StatefulWidget {
|
||||
class _WorkoutLogCalendarState extends State<WorkoutLogCalendar> {
|
||||
DateTime _focusedDay = clock.now();
|
||||
DateTime? _selectedDay;
|
||||
late final ValueNotifier<List<WorkoutLogEvent>> _selectedEvents;
|
||||
late Map<String, List<WorkoutLogEvent>> _events;
|
||||
late final ValueNotifier<List<DateTime>> _selectedEvents;
|
||||
late Map<String, List<DateTime>> _events;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -121,18 +108,16 @@ class _WorkoutLogCalendarState extends State<WorkoutLogCalendar> {
|
||||
}
|
||||
|
||||
void loadEvents() {
|
||||
for (final date in widget._routine.logData.keys) {
|
||||
final entry = widget._routine.logData[date]!;
|
||||
_events[DateFormatLists.format(date)] = [
|
||||
WorkoutLogEvent(date, entry['session'], entry['exercises']),
|
||||
for (final sessionApi in widget._routine.sessions) {
|
||||
_events[DateFormatLists.format(sessionApi.session.date)] = [
|
||||
sessionApi.session.date,
|
||||
];
|
||||
}
|
||||
|
||||
// Add initial selected day to events list
|
||||
_selectedEvents.value = _getEventsForDay(_selectedDay!);
|
||||
}
|
||||
|
||||
List<WorkoutLogEvent> _getEventsForDay(DateTime day) {
|
||||
List<DateTime> _getEventsForDay(DateTime day) {
|
||||
return _events[DateFormatLists.format(day)] ?? [];
|
||||
}
|
||||
|
||||
@@ -165,23 +150,17 @@ class _WorkoutLogCalendarState extends State<WorkoutLogCalendar> {
|
||||
availableCalendarFormats: const {CalendarFormat.month: ''},
|
||||
onDaySelected: _onDaySelected,
|
||||
onPageChanged: (focusedDay) {
|
||||
// No need to call `setState()` here
|
||||
_focusedDay = focusedDay;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
SizedBox(
|
||||
child: ValueListenableBuilder<List<WorkoutLogEvent>>(
|
||||
child: ValueListenableBuilder<List<DateTime>>(
|
||||
valueListenable: _selectedEvents,
|
||||
builder: (context, logEvents, _) {
|
||||
// At the moment there is only one "event" per day
|
||||
return logEvents.isNotEmpty
|
||||
? DayLogWidget(
|
||||
logEvents.first.dateTime,
|
||||
logEvents.first.exercises,
|
||||
logEvents.first.session,
|
||||
widget._routine,
|
||||
)
|
||||
? DayLogWidget(logEvents.first, widget._routine)
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
|
||||
@@ -398,14 +398,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_calendar_carousel:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_calendar_carousel
|
||||
sha256: "2fd1b58cefbefe0504ca7d0080a9e63be96041acc93b0c7f340ef0a55b9808fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
flutter_driver:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
||||
@@ -41,7 +41,6 @@ dependencies:
|
||||
fl_chart: ^0.69.2
|
||||
flex_color_scheme: ^8.1.1
|
||||
flex_seed_scheme: ^3.5.1
|
||||
flutter_calendar_carousel: ^2.4.4
|
||||
flutter_html: ^3.0.0
|
||||
flutter_staggered_grid_view: ^0.7.0
|
||||
flutter_svg: ^2.0.17
|
||||
|
||||
@@ -1,78 +1,88 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:wger/helpers/colors.dart';
|
||||
|
||||
void main() {
|
||||
group('generateChartColors', () {
|
||||
test('should generate 3 colors for 3 items using iterator', () {
|
||||
test('should generate 3 colors for 3 items', () {
|
||||
final iterator = generateChartColors(3).iterator;
|
||||
final expectedColors = LIST_OF_COLORS3.iterator;
|
||||
|
||||
while (iterator.moveNext() && expectedColors.moveNext()) {
|
||||
while (expectedColors.moveNext()) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(expectedColors.current));
|
||||
}
|
||||
|
||||
expect(iterator.moveNext(), isFalse);
|
||||
expect(expectedColors.moveNext(), isFalse);
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(Colors.black));
|
||||
});
|
||||
|
||||
test('should generate 5 colors for 5 items using iterator', () {
|
||||
test('should generate 5 colors for 5 items', () {
|
||||
final iterator = generateChartColors(5).iterator;
|
||||
final expectedColors = LIST_OF_COLORS5.iterator;
|
||||
|
||||
while (iterator.moveNext() && expectedColors.moveNext()) {
|
||||
while (expectedColors.moveNext()) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(expectedColors.current));
|
||||
}
|
||||
|
||||
expect(iterator.moveNext(), isFalse);
|
||||
expect(expectedColors.moveNext(), isFalse);
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(Colors.black));
|
||||
});
|
||||
|
||||
test('should generate 8 colors for 8 items using iterator', () {
|
||||
test('should generate 8 colors for 8 items', () {
|
||||
final iterator = generateChartColors(8).iterator;
|
||||
final expectedColors = LIST_OF_COLORS8.iterator;
|
||||
|
||||
while (iterator.moveNext() && expectedColors.moveNext()) {
|
||||
while (expectedColors.moveNext()) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(expectedColors.current));
|
||||
}
|
||||
|
||||
expect(iterator.moveNext(), isFalse);
|
||||
expect(expectedColors.moveNext(), isFalse);
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(Colors.black));
|
||||
});
|
||||
|
||||
test('should generate 8 colors for more than 8 items using iterator', () {
|
||||
test('should generate surplus color for more than 8 items', () {
|
||||
final iterator = generateChartColors(10).iterator;
|
||||
final expectedColors = LIST_OF_COLORS8.iterator;
|
||||
|
||||
while (iterator.moveNext() && expectedColors.moveNext()) {
|
||||
while (expectedColors.moveNext()) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(expectedColors.current));
|
||||
}
|
||||
|
||||
expect(iterator.moveNext(), isFalse);
|
||||
expect(expectedColors.moveNext(), isFalse);
|
||||
// After exhausting the 8 colors, it should always return black
|
||||
for (int i = 0; i < 12; i++) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(Colors.black));
|
||||
}
|
||||
});
|
||||
|
||||
test('should generate 8 colors for 0 items using iterator', () {
|
||||
test('should generate 3 colors for 0 items', () {
|
||||
final iterator = generateChartColors(0).iterator;
|
||||
final expectedColors = LIST_OF_COLORS3.iterator;
|
||||
|
||||
while (iterator.moveNext() && expectedColors.moveNext()) {
|
||||
while (expectedColors.moveNext()) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(expectedColors.current));
|
||||
}
|
||||
|
||||
expect(iterator.moveNext(), isFalse);
|
||||
expect(expectedColors.moveNext(), isFalse);
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(Colors.black));
|
||||
});
|
||||
|
||||
test('should generate 8 colors for negative items using iterator', () {
|
||||
test('should generate 3 colors for negative number of items', () {
|
||||
final iterator = generateChartColors(-5).iterator;
|
||||
final expectedColors = LIST_OF_COLORS3.iterator;
|
||||
|
||||
while (iterator.moveNext() && expectedColors.moveNext()) {
|
||||
while (expectedColors.moveNext()) {
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(expectedColors.current));
|
||||
}
|
||||
|
||||
expect(iterator.moveNext(), isFalse);
|
||||
expect(expectedColors.moveNext(), isFalse);
|
||||
expect(iterator.moveNext(), isTrue);
|
||||
expect(iterator.current, equals(Colors.black));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 9.6 KiB |
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:clock/clock.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
@@ -40,7 +41,9 @@ void main() {
|
||||
|
||||
setUp(() {
|
||||
routine = getTestRoutine();
|
||||
routine.logs[0].date = DateTime.now();
|
||||
routine.sessions[0].session.date = DateTime(2025, 3, 29);
|
||||
|
||||
when(mockRoutinesProvider.findById(any)).thenAnswer((_) => routine);
|
||||
});
|
||||
|
||||
Widget renderWidget({locale = 'en'}) {
|
||||
@@ -56,7 +59,7 @@ void main() {
|
||||
home: TextButton(
|
||||
onPressed: () => key.currentState!.push(
|
||||
MaterialPageRoute<void>(
|
||||
settings: RouteSettings(arguments: routine),
|
||||
settings: RouteSettings(arguments: routine.id),
|
||||
builder: (_) => const WorkoutLogsScreen(),
|
||||
),
|
||||
),
|
||||
@@ -89,8 +92,29 @@ void main() {
|
||||
|
||||
expect(find.text('Training logs'), findsOneWidget);
|
||||
expect(find.byType(WorkoutLogCalendar), findsOneWidget);
|
||||
expect(find.text('Bench press'), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('delete-log-1')), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('delete-log-2')), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('delete-log-3')), findsNothing);
|
||||
});
|
||||
},
|
||||
tags: ['golden'],
|
||||
);
|
||||
|
||||
testWidgets('Test deleting log entries', (WidgetTester tester) async {
|
||||
await withClock(Clock.fixed(DateTime(2025, 3, 29)), () async {
|
||||
await tester.pumpWidget(renderWidget());
|
||||
await tester.tap(find.byType(TextButton));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.drag(find.byType(ListView), const Offset(0, -500));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const ValueKey('delete-log-1')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const ValueKey('delete-button')), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('cancel-button')), findsOneWidget);
|
||||
await tester.tap(find.byKey(const ValueKey('delete-button')));
|
||||
verify(mockRoutinesProvider.deleteLog(1, 1)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
@@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
) as _i13.Future<_i11.Log>);
|
||||
|
||||
@override
|
||||
_i13.Future<void> deleteLog(_i11.Log? log) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [log]),
|
||||
_i13.Future<void> deleteLog(int? logId, int? routineId) => (super.noSuchMethod(
|
||||
Invocation.method(#deleteLog, [logId, routineId]),
|
||||
returnValue: _i13.Future<void>.value(),
|
||||
returnValueForMissingStub: _i13.Future<void>.value(),
|
||||
) as _i13.Future<void>);
|
||||
|
||||
Reference in New Issue
Block a user