mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 23:42:00 +01:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # test/weight/weight_screen_test.dart
This commit is contained in:
@@ -785,7 +785,7 @@
|
||||
"@settingsCacheDescription": {},
|
||||
"settingsCacheDeletedSnackbar": "Zwischenspeicher erfolgreich gelöscht",
|
||||
"@settingsCacheDeletedSnackbar": {},
|
||||
"lb": "Pfund",
|
||||
"lb": "lb",
|
||||
"@lb": {
|
||||
"description": "Generated entry for translation for server strings"
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
"@rirNotUsed": {
|
||||
"description": "Label used in RiR slider when the RiR value is not used/saved for the current setting or log"
|
||||
},
|
||||
"useMetric": "Use metric units for body weight",
|
||||
"weightUnit": "Weight unit",
|
||||
"@weightUnit": {},
|
||||
"repetitionUnit": "Repetition unit",
|
||||
|
||||
@@ -31,6 +31,11 @@ class Profile {
|
||||
@JsonKey(required: true, name: 'is_trustworthy')
|
||||
bool isTrustworthy;
|
||||
|
||||
@JsonKey(required: true, name: 'weight_unit')
|
||||
String weightUnitStr;
|
||||
|
||||
bool get isMetric => weightUnitStr == 'kg';
|
||||
|
||||
@JsonKey(required: true)
|
||||
String email;
|
||||
|
||||
@@ -39,9 +44,11 @@ class Profile {
|
||||
required this.emailVerified,
|
||||
required this.isTrustworthy,
|
||||
required this.email,
|
||||
required this.weightUnitStr,
|
||||
});
|
||||
|
||||
// Boilerplate
|
||||
factory Profile.fromJson(Map<String, dynamic> json) => _$ProfileFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ProfileToJson(this);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@ part of 'profile.dart';
|
||||
Profile _$ProfileFromJson(Map<String, dynamic> json) {
|
||||
$checkKeys(
|
||||
json,
|
||||
requiredKeys: const ['username', 'email_verified', 'is_trustworthy', 'email'],
|
||||
requiredKeys: const ['username', 'email_verified', 'is_trustworthy', 'weight_unit', 'email'],
|
||||
);
|
||||
return Profile(
|
||||
username: json['username'] as String,
|
||||
emailVerified: json['email_verified'] as bool,
|
||||
isTrustworthy: json['is_trustworthy'] as bool,
|
||||
email: json['email'] as String,
|
||||
weightUnitStr: json['weight_unit'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,5 +24,6 @@ Map<String, dynamic> _$ProfileToJson(Profile instance) => <String, dynamic>{
|
||||
'username': instance.username,
|
||||
'email_verified': instance.emailVerified,
|
||||
'is_trustworthy': instance.isTrustworthy,
|
||||
'weight_unit': instance.weightUnitStr,
|
||||
'email': instance.email,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:wger/models/workouts/workout_plan.dart';
|
||||
import 'package:wger/providers/body_weight.dart';
|
||||
import 'package:wger/providers/measurement.dart';
|
||||
import 'package:wger/providers/nutrition.dart';
|
||||
import 'package:wger/providers/user.dart';
|
||||
import 'package:wger/providers/workout_plans.dart';
|
||||
import 'package:wger/screens/form_screen.dart';
|
||||
import 'package:wger/screens/gym_mode.dart';
|
||||
@@ -243,6 +244,7 @@ class _DashboardWeightWidgetState extends State<DashboardWeightWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final profile = context.read<UserProvider>().profile;
|
||||
weightEntriesData = Provider.of<BodyWeightProvider>(context, listen: false);
|
||||
|
||||
return Consumer<BodyWeightProvider>(
|
||||
@@ -267,9 +269,13 @@ class _DashboardWeightWidgetState extends State<DashboardWeightWidget> {
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: MeasurementChartWidgetFl(weightEntriesData.items
|
||||
.map((e) => MeasurementChartEntry(e.weight, e.date))
|
||||
.toList()),
|
||||
child: MeasurementChartWidgetFl(
|
||||
weightEntriesData.items
|
||||
.map((e) => MeasurementChartEntry(e.weight, e.date))
|
||||
.toList(),
|
||||
unit: profile!.isMetric
|
||||
? AppLocalizations.of(context).kg
|
||||
: AppLocalizations.of(context).lb),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
||||
@@ -23,14 +23,26 @@ import 'package:wger/models/user/profile.dart';
|
||||
import 'package:wger/providers/user.dart';
|
||||
import 'package:wger/theme/theme.dart';
|
||||
|
||||
class UserProfileForm extends StatelessWidget {
|
||||
class UserProfileForm extends StatefulWidget {
|
||||
late final Profile _profile;
|
||||
final _form = GlobalKey<FormState>();
|
||||
final emailController = TextEditingController();
|
||||
|
||||
UserProfileForm(Profile profile) {
|
||||
_profile = profile;
|
||||
emailController.text = _profile.email;
|
||||
}
|
||||
|
||||
@override
|
||||
State<UserProfileForm> createState() => _UserProfileFormState();
|
||||
}
|
||||
|
||||
class _UserProfileFormState extends State<UserProfileForm> {
|
||||
final _form = GlobalKey<FormState>();
|
||||
|
||||
final emailController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
emailController.text = widget._profile.email;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -42,16 +54,29 @@ class UserProfileForm extends StatelessWidget {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person, color: wgerPrimaryColor),
|
||||
title: Text(AppLocalizations.of(context).username),
|
||||
subtitle: Text(_profile.username),
|
||||
subtitle: Text(widget._profile.username),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context).useMetric),
|
||||
subtitle: Text(widget._profile.weightUnitStr),
|
||||
value: widget._profile.isMetric,
|
||||
onChanged: (_) {
|
||||
setState(() {
|
||||
widget._profile.weightUnitStr = widget._profile.isMetric
|
||||
? AppLocalizations.of(context).lb
|
||||
: AppLocalizations.of(context).kg;
|
||||
});
|
||||
},
|
||||
dense: true,
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.email_rounded, color: wgerPrimaryColor),
|
||||
title: TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: _profile.emailVerified
|
||||
labelText: widget._profile.emailVerified
|
||||
? AppLocalizations.of(context).verifiedEmail
|
||||
: AppLocalizations.of(context).unVerifiedEmail,
|
||||
suffixIcon: _profile.emailVerified
|
||||
suffixIcon: widget._profile.emailVerified
|
||||
? const Icon(
|
||||
Icons.check_circle,
|
||||
color: Colors.green,
|
||||
@@ -60,7 +85,7 @@ class UserProfileForm extends StatelessWidget {
|
||||
controller: emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
onSaved: (newValue) {
|
||||
_profile.email = newValue!;
|
||||
widget._profile.email = newValue!;
|
||||
},
|
||||
validator: (value) {
|
||||
if (value!.isNotEmpty && !value.contains('@')) {
|
||||
@@ -70,11 +95,11 @@ class UserProfileForm extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (!_profile.emailVerified)
|
||||
if (!widget._profile.emailVerified)
|
||||
OutlinedButton(
|
||||
onPressed: () async {
|
||||
// Email is already verified
|
||||
if (_profile.emailVerified) {
|
||||
if (widget._profile.emailVerified) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,7 +108,7 @@ class UserProfileForm extends StatelessWidget {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context).verifiedEmailInfo(_profile.email),
|
||||
AppLocalizations.of(context).verifiedEmailInfo(widget._profile.email),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -91,9 +116,6 @@ class UserProfileForm extends StatelessWidget {
|
||||
child: Text(AppLocalizations.of(context).verify),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: wgerPrimaryButtonColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50))),
|
||||
onPressed: () async {
|
||||
// Validate and save the current values to the weightEntry
|
||||
final isValid = _form.currentState!.validate();
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/providers/body_weight.dart';
|
||||
import 'package:wger/providers/user.dart';
|
||||
import 'package:wger/screens/form_screen.dart';
|
||||
import 'package:wger/screens/measurement_categories_screen.dart';
|
||||
import 'package:wger/widgets/measurements/charts.dart';
|
||||
@@ -29,6 +30,7 @@ import 'package:wger/widgets/weight/forms.dart';
|
||||
class WeightEntriesList extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final profile = context.read<UserProvider>().profile;
|
||||
final weightProvider = Provider.of<BodyWeightProvider>(context, listen: false);
|
||||
|
||||
return Column(
|
||||
@@ -37,7 +39,11 @@ class WeightEntriesList extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(15),
|
||||
height: 220,
|
||||
child: MeasurementChartWidgetFl(
|
||||
weightProvider.items.map((e) => MeasurementChartEntry(e.weight, e.date)).toList()),
|
||||
weightProvider.items.map((e) => MeasurementChartEntry(e.weight, e.date)).toList(),
|
||||
unit: profile!.isMetric
|
||||
? AppLocalizations.of(context).kg
|
||||
: AppLocalizations.of(context).lb,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pushNamed(
|
||||
|
||||
@@ -23,34 +23,43 @@ import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/providers/body_weight.dart';
|
||||
import 'package:wger/providers/user.dart';
|
||||
import 'package:wger/screens/form_screen.dart';
|
||||
import 'package:wger/screens/weight_screen.dart';
|
||||
import 'package:wger/widgets/measurements/charts.dart';
|
||||
import 'package:wger/widgets/weight/forms.dart';
|
||||
|
||||
import '../../test_data/body_weight.dart';
|
||||
import '../../test_data/profile.dart';
|
||||
import 'weight_screen_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([BodyWeightProvider])
|
||||
@GenerateMocks([BodyWeightProvider, UserProvider])
|
||||
void main() {
|
||||
late MockBodyWeightProvider mockWeightProvider;
|
||||
late MockUserProvider mockUserProvider;
|
||||
|
||||
setUp(() {
|
||||
mockWeightProvider = MockBodyWeightProvider();
|
||||
when(mockWeightProvider.items).thenReturn(getWeightEntries());
|
||||
|
||||
mockUserProvider = MockUserProvider();
|
||||
when(mockUserProvider.profile).thenReturn(tProfile1);
|
||||
});
|
||||
|
||||
Widget createWeightScreen({locale = 'en'}) {
|
||||
return ChangeNotifierProvider<BodyWeightProvider>(
|
||||
create: (context) => mockWeightProvider,
|
||||
child: MaterialApp(
|
||||
locale: Locale(locale),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: WeightScreen(),
|
||||
routes: {
|
||||
FormScreen.routeName: (_) => FormScreen(),
|
||||
},
|
||||
return ChangeNotifierProvider<UserProvider>(
|
||||
create: (context) => mockUserProvider,
|
||||
child: ChangeNotifierProvider<BodyWeightProvider>(
|
||||
create: (context) => mockWeightProvider,
|
||||
child: MaterialApp(
|
||||
locale: Locale(locale),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: WeightScreen(),
|
||||
routes: {
|
||||
FormScreen.routeName: (_) => FormScreen(),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import 'dart:ui' as _i6;
|
||||
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:wger/models/body_weight/weight_entry.dart' as _i3;
|
||||
import 'package:wger/models/user/profile.dart' as _i8;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/body_weight.dart' as _i4;
|
||||
import 'package:wger/providers/user.dart' as _i7;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
@@ -192,3 +194,111 @@ class MockBodyWeightProvider extends _i1.Mock implements _i4.BodyWeightProvider
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [UserProvider].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockUserProvider extends _i1.Mock implements _i7.UserProvider {
|
||||
MockUserProvider() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i2.WgerBaseProvider get baseProvider => (super.noSuchMethod(
|
||||
Invocation.getter(#baseProvider),
|
||||
returnValue: _FakeWgerBaseProvider_0(
|
||||
this,
|
||||
Invocation.getter(#baseProvider),
|
||||
),
|
||||
) as _i2.WgerBaseProvider);
|
||||
|
||||
@override
|
||||
set profile(_i8.Profile? _profile) => super.noSuchMethod(
|
||||
Invocation.setter(
|
||||
#profile,
|
||||
_profile,
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get hasListeners => (super.noSuchMethod(
|
||||
Invocation.getter(#hasListeners),
|
||||
returnValue: false,
|
||||
) as bool);
|
||||
|
||||
@override
|
||||
void clear() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#clear,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
_i5.Future<void> fetchAndSetProfile() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#fetchAndSetProfile,
|
||||
[],
|
||||
),
|
||||
returnValue: _i5.Future<void>.value(),
|
||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||
) as _i5.Future<void>);
|
||||
|
||||
@override
|
||||
_i5.Future<void> saveProfile() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#saveProfile,
|
||||
[],
|
||||
),
|
||||
returnValue: _i5.Future<void>.value(),
|
||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||
) as _i5.Future<void>);
|
||||
|
||||
@override
|
||||
_i5.Future<void> verifyEmail() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#verifyEmail,
|
||||
[],
|
||||
),
|
||||
returnValue: _i5.Future<void>.value(),
|
||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||
) as _i5.Future<void>);
|
||||
|
||||
@override
|
||||
void addListener(_i6.VoidCallback? listener) => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#addListener,
|
||||
[listener],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void removeListener(_i6.VoidCallback? listener) => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#removeListener,
|
||||
[listener],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void dispose() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#dispose,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void notifyListeners() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#notifyListeners,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ final tProfile1 = Profile(
|
||||
emailVerified: true,
|
||||
isTrustworthy: true,
|
||||
email: 'admin@google.com',
|
||||
weightUnitStr: 'kg',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user