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
32 changes: 32 additions & 0 deletions ios/Flutter/ephemeral/flutter_lldb_helper.py
Copy link
Member

Choose a reason for hiding this comment

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

I was really confused about this till I saw that it's one of flutter's generated files 😅

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Generated file, do not edit.
#

import lldb

def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
base = frame.register["x0"].GetValueAsAddress()
page_len = frame.register["x1"].GetValueAsUnsigned()

# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
# first page to see if handled it correctly. This makes diagnosing
# misconfiguration (e.g. missing breakpoint) easier.
data = bytearray(page_len)
data[0:8] = b'IHELPED!'

error = lldb.SBError()
frame.GetThread().GetProcess().WriteMemory(base, data, error)
if not error.Success():
print(f'Failed to write into {base}[+{page_len}]', error)
return

def __lldb_init_module(debugger: lldb.SBDebugger, _):
target = debugger.GetDummyTarget()
# Caveat: must use BreakpointCreateByRegEx here and not
# BreakpointCreateByName. For some reasons callback function does not
# get carried over from dummy target for the later.
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
bp.SetAutoContinue(True)
print("-- LLDB integration loaded --")
5 changes: 5 additions & 0 deletions ios/Flutter/ephemeral/flutter_lldbinit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Generated file, do not edit.
#

command script import --relative-to-command-file flutter_lldb_helper.py
244 changes: 161 additions & 83 deletions lib/screens/add_exercise_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:wger/providers/user.dart';
import 'package:wger/screens/exercise_screen.dart';
import 'package:wger/widgets/add_exercise/steps/step_1_basics.dart';
import 'package:wger/widgets/add_exercise/steps/step_2_variations.dart';
import 'package:wger/widgets/add_exercise/steps/step_3_description.dart';
import 'package:wger/widgets/add_exercise/steps/step_4_translations.dart';
import 'package:wger/widgets/add_exercise/steps/step_3_description.dart' as step3;
import 'package:wger/widgets/add_exercise/steps/step_4_translations.dart' as step4;
import 'package:wger/widgets/add_exercise/steps/step_5_images.dart';
import 'package:wger/widgets/add_exercise/steps/step_6_overview.dart';
import 'package:wger/widgets/core/app_bar.dart';
Expand Down Expand Up @@ -46,7 +46,9 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
int _currentStep = 0;
int lastStepIndex = AddExerciseStepper.STEPS_IN_FORM - 1;
bool _isLoading = false;
bool _isValidating = false;
Widget errorWidget = const SizedBox.shrink();
WgerHttpException? _validationError;

final List<GlobalKey<FormState>> _keys = [
GlobalKey<FormState>(),
Expand All @@ -57,16 +59,63 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
GlobalKey<FormState>(),
];

Future<bool> _validateLanguageOnServer(BuildContext context) async {
Copy link
Member

Choose a reason for hiding this comment

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

I had hoped we could add something to the fields themselves so that the logic would be directly in e.g. Step3Description or AddExerciseTextArea. I find it very baffling that flutter does not have support for async validators, then this would be way easier

final addExerciseProvider = context.read<AddExerciseProvider>();

try {
if (_currentStep == 2) {
await addExerciseProvider.validateLanguage(
addExerciseProvider.descriptionEn ?? '',
'en',
);
}

if (_currentStep == 3 && addExerciseProvider.descriptionTrans != null) {
final languageCode = addExerciseProvider.languageTranslation?.shortName ?? '';
if (languageCode.isNotEmpty) {
await addExerciseProvider.validateLanguage(
addExerciseProvider.descriptionTrans ?? '',
languageCode,
);
}
}

return true;
} on WgerHttpException catch (error) {
if (mounted) {
setState(() {
_validationError = error;
});
}
return false;
} catch (error) {
if (mounted) {
setState(() {
_validationError = WgerHttpException({'error': [error.toString()]});
});
}
return false;
}
}

Widget _controlsBuilder(BuildContext context, ControlsDetails details) {
return Column(
children: [
const SizedBox(height: 10),

if (_validationError != null && _currentStep != lastStepIndex)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: FormHttpErrorsWidget(_validationError!),
),

if (_currentStep == lastStepIndex) errorWidget,

Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
OutlinedButton(
onPressed: details.onStepCancel,
onPressed: _isValidating ? null : details.onStepCancel,
child: Text(AppLocalizations.of(context).previous),
),

Expand All @@ -76,70 +125,112 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
onPressed: _isLoading
? null
: () async {
setState(() {
_isLoading = true;
errorWidget = const SizedBox.shrink();
});
final addExerciseProvider = context.read<AddExerciseProvider>();
final exerciseProvider = context.read<ExercisesProvider>();

Exercise? exercise;
try {
final exerciseId = await addExerciseProvider.postExerciseToServer();
exercise = await exerciseProvider.fetchAndSetExercise(exerciseId);
} on WgerHttpException catch (error) {
if (context.mounted) {
setState(() {
errorWidget = FormHttpErrorsWidget(error);
});
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}

if (exercise == null || !context.mounted) {
return;
}

final name = exercise
.getTranslation(Localizations.localeOf(context).languageCode)
.name;

return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(AppLocalizations.of(context).success),
content: Text(AppLocalizations.of(context).cacheWarning),
actions: [
TextButton(
child: Text(name),
onPressed: () {
Navigator.of(context).pop();
Navigator.pushReplacementNamed(
context,
ExerciseDetailScreen.routeName,
arguments: exercise,
);
},
),
],
);
},
);
},
setState(() {
_isLoading = true;
errorWidget = const SizedBox.shrink();
});
final addExerciseProvider = context.read<AddExerciseProvider>();
final exerciseProvider = context.read<ExercisesProvider>();

Exercise? exercise;
try {
final exerciseId = await addExerciseProvider.postExerciseToServer();
exercise = await exerciseProvider.fetchAndSetExercise(exerciseId);
} on WgerHttpException catch (error) {
if (context.mounted) {
setState(() {
errorWidget = FormHttpErrorsWidget(error);
});
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}

if (exercise == null || !context.mounted) {
return;
}

final name = exercise
.getTranslation(Localizations.localeOf(context).languageCode)
.name;

return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(AppLocalizations.of(context).success),
content: Text(AppLocalizations.of(context).cacheWarning),
actions: [
TextButton(
child: Text(name),
onPressed: () {
Navigator.of(context).pop();
Navigator.pushReplacementNamed(
context,
ExerciseDetailScreen.routeName,
arguments: exercise,
);
},
),
],
);
},
);
},
child: _isLoading
? const SizedBox(height: 20, width: 20, child: CircularProgressIndicator())
: Text(AppLocalizations.of(context).save),
)
else
ElevatedButton(
onPressed: details.onStepContinue,
child: Text(AppLocalizations.of(context).next),
onPressed: _isValidating
? null
: () async {
setState(() {
_validationError = null;
});

if (!(_keys[_currentStep].currentState?.validate() ?? false)) {
return;
}

_keys[_currentStep].currentState?.save();

if (_currentStep == 2 || _currentStep == 3) {
setState(() {
_isValidating = true;
});

final isValid = await _validateLanguageOnServer(context);

if (mounted) {
setState(() {
_isValidating = false;
});
}

if (!isValid) {
return;
}
}

if (_currentStep != lastStepIndex) {
setState(() {
_currentStep += 1;
});
}
},
child: _isValidating
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(AppLocalizations.of(context).next),
),
],
),
Expand All @@ -164,42 +255,29 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
),
Step(
title: Text(AppLocalizations.of(context).description),
content: Step3Description(formkey: _keys[2]),
content: step3.Step3Description(formkey: _keys[2]),
),
Step(
title: Text(AppLocalizations.of(context).translation),
content: Step4Translation(formkey: _keys[3]),
content: step4.Step4Translation(formkey: _keys[3]),
),
Step(
title: Text(AppLocalizations.of(context).images),
content: Step5Images(formkey: _keys[4]),
),
Step(title: Text(AppLocalizations.of(context).overview), content: Step6Overview()),
Step(
title: Text(AppLocalizations.of(context).overview),
content: Step6Overview(),
),
],
currentStep: _currentStep,
onStepContinue: () {
if (_keys[_currentStep].currentState?.validate() ?? false) {
_keys[_currentStep].currentState?.save();

if (_currentStep != lastStepIndex) {
setState(() {
_currentStep += 1;
});
}
}
},
onStepContinue: null, // Použijeme vlastnú logiku v _controlsBuilder
onStepCancel: () => setState(() {
if (_currentStep != 0) {
_currentStep -= 1;
_validationError = null; // Resetovať chybu pri návrate
}
}),
/*
onStepTapped: (int index) {
setState(() {
_currentStep = index;
});
},
*/
),
);
}
Expand Down Expand Up @@ -256,4 +334,4 @@ class EmailNotVerified extends StatelessWidget {
),
);
}
}
}
6 changes: 4 additions & 2 deletions lib/widgets/add_exercise/steps/step_3_description.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ class Step3Description extends StatelessWidget {
child: Column(
children: [
AddExerciseTextArea(
onChange: (value) => {},
onChange: (value) => {
addExerciseProvider.descriptionEn = value
},
title: '${i18n.description}*',
helperText: i18n.enterTextInLanguage,
isMultiline: true,
Expand All @@ -31,4 +33,4 @@ class Step3Description extends StatelessWidget {
),
);
}
}
}
Loading