mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
POST images when creating new exercises
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:wger/models/exercises/base.dart';
|
||||
import 'package:wger/models/exercises/category.dart';
|
||||
import 'package:wger/models/exercises/equipment.dart';
|
||||
@@ -23,7 +24,7 @@ class AddExerciseProvider with ChangeNotifier {
|
||||
Language? _language;
|
||||
List<String> _alternativeNamesEn = [];
|
||||
List<String> _alternativeNamesTranslation = [];
|
||||
ExerciseCategory? _targetArea;
|
||||
ExerciseCategory? _category;
|
||||
List<ExerciseBase> _variations = [];
|
||||
List<Equipment> _equipment = [];
|
||||
List<Muscle> _primaryMuscles = [];
|
||||
@@ -32,13 +33,14 @@ class AddExerciseProvider with ChangeNotifier {
|
||||
AddExerciseProvider(this.baseProvider);
|
||||
|
||||
static const _exerciseBaseUrlPath = 'exercise-base';
|
||||
static const _imagesUrlPath = 'exerciseimage';
|
||||
static const _exerciseTranslationUrlPath = 'exercise-translation';
|
||||
|
||||
void clear() {
|
||||
_exerciseImages = [];
|
||||
_alternativeNamesEn = [];
|
||||
_alternativeNamesTranslation = [];
|
||||
_targetArea = null;
|
||||
_category = null;
|
||||
_equipment = [];
|
||||
_primaryMuscles = [];
|
||||
_secondaryMuscles = [];
|
||||
@@ -52,12 +54,15 @@ class AddExerciseProvider with ChangeNotifier {
|
||||
set alternateNamesTrans(List<String> names) => _alternativeNamesTranslation = names;
|
||||
|
||||
set equipment(List<Equipment> equipment) => _equipment = equipment;
|
||||
set targetArea(ExerciseCategory target) => _targetArea = target;
|
||||
List<Equipment> get equipment => [..._equipment];
|
||||
set category(ExerciseCategory category) => _category = category;
|
||||
ExerciseCategory get category => _category!;
|
||||
set language(Language language) => _language = language;
|
||||
Language get language => _language!;
|
||||
|
||||
ExerciseBase get base {
|
||||
return ExerciseBase(
|
||||
category: _targetArea,
|
||||
category: _category,
|
||||
equipment: _equipment,
|
||||
muscles: _primaryMuscles,
|
||||
musclesSecondary: _secondaryMuscles,
|
||||
@@ -107,7 +112,7 @@ class AddExerciseProvider with ChangeNotifier {
|
||||
log('------------------------');
|
||||
|
||||
log('Base data...');
|
||||
log('Target area : $_targetArea');
|
||||
log('Target area : $_category');
|
||||
log('Primary muscles: $_primaryMuscles');
|
||||
log('Secondary muscles: $_secondaryMuscles');
|
||||
log('Equipment: $_equipment');
|
||||
@@ -133,14 +138,14 @@ class AddExerciseProvider with ChangeNotifier {
|
||||
// Create the translations
|
||||
final exerciseTranslationEn = exerciseEn;
|
||||
exerciseTranslationEn.base = base;
|
||||
addExerciseTranslation(exerciseTranslationEn);
|
||||
await addExerciseTranslation(exerciseTranslationEn);
|
||||
|
||||
final exerciseTranslationTranslation = exerciseTranslation;
|
||||
exerciseTranslationTranslation.base = base;
|
||||
addExerciseTranslation(exerciseTranslationTranslation);
|
||||
await addExerciseTranslation(exerciseTranslationTranslation);
|
||||
|
||||
// Create the images
|
||||
// ...
|
||||
await addImages(base);
|
||||
}
|
||||
|
||||
Future<ExerciseBase> addExerciseBase() async {
|
||||
@@ -153,6 +158,22 @@ class AddExerciseProvider with ChangeNotifier {
|
||||
return newExerciseBase;
|
||||
}
|
||||
|
||||
Future<void> addImages(ExerciseBase base) async {
|
||||
for (final image in _exerciseImages) {
|
||||
final request = http.MultipartRequest('POST', baseProvider.makeUrl(_imagesUrlPath));
|
||||
request.headers.addAll(baseProvider.getDefaultHeaders(includeAuth: true));
|
||||
|
||||
request.files.add(await http.MultipartFile.fromPath('image', image.path));
|
||||
request.fields['exercise_base'] = base.id!.toString();
|
||||
request.fields['style'] = '4';
|
||||
|
||||
final res = await request.send();
|
||||
//final respStr = await res.stream.bytesToString();
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<Exercise> addExerciseTranslation(Exercise exercise) async {
|
||||
final Uri postUri = baseProvider.makeUrl(_exerciseTranslationUrlPath);
|
||||
|
||||
|
||||
@@ -37,6 +37,19 @@ class WgerBaseProvider {
|
||||
this.client = client ?? http.Client();
|
||||
}
|
||||
|
||||
Map<String, String> getDefaultHeaders({includeAuth = false}) {
|
||||
final out = {
|
||||
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
|
||||
HttpHeaders.userAgentHeader: auth.getAppNameHeader(),
|
||||
};
|
||||
|
||||
if (includeAuth) {
|
||||
out[HttpHeaders.authorizationHeader] = 'Token ${auth.token}';
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Helper function to make a URL.
|
||||
Uri makeUrl(String path, {int? id, String? objectMethod, Map<String, dynamic>? query}) {
|
||||
return makeUri(auth.serverUrl!, path, id, objectMethod, query);
|
||||
@@ -47,10 +60,7 @@ class WgerBaseProvider {
|
||||
// Send the request
|
||||
final response = await client.get(
|
||||
uri,
|
||||
headers: {
|
||||
HttpHeaders.authorizationHeader: 'Token ${auth.token}',
|
||||
HttpHeaders.userAgentHeader: auth.getAppNameHeader(),
|
||||
},
|
||||
headers: getDefaultHeaders(includeAuth: true),
|
||||
);
|
||||
|
||||
// Something wrong with our request
|
||||
@@ -66,11 +76,7 @@ class WgerBaseProvider {
|
||||
Future<Map<String, dynamic>> post(Map<String, dynamic> data, Uri uri) async {
|
||||
final response = await client.post(
|
||||
uri,
|
||||
headers: {
|
||||
HttpHeaders.authorizationHeader: 'Token ${auth.token}',
|
||||
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
|
||||
HttpHeaders.userAgentHeader: auth.getAppNameHeader(),
|
||||
},
|
||||
headers: getDefaultHeaders(includeAuth: true),
|
||||
body: json.encode(data),
|
||||
);
|
||||
|
||||
@@ -86,11 +92,7 @@ class WgerBaseProvider {
|
||||
Future<Map<String, dynamic>> patch(Map<String, dynamic> data, Uri uri) async {
|
||||
final response = await client.patch(
|
||||
uri,
|
||||
headers: {
|
||||
HttpHeaders.authorizationHeader: 'Token ${auth.token}',
|
||||
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
|
||||
HttpHeaders.userAgentHeader: auth.getAppNameHeader(),
|
||||
},
|
||||
headers: getDefaultHeaders(includeAuth: true),
|
||||
body: json.encode(data),
|
||||
);
|
||||
|
||||
@@ -108,10 +110,7 @@ class WgerBaseProvider {
|
||||
|
||||
final response = await client.delete(
|
||||
deleteUrl,
|
||||
headers: {
|
||||
HttpHeaders.authorizationHeader: 'Token ${auth.token}',
|
||||
HttpHeaders.userAgentHeader: auth.getAppNameHeader(),
|
||||
},
|
||||
headers: getDefaultHeaders(includeAuth: true),
|
||||
);
|
||||
|
||||
// Something wrong with our request
|
||||
|
||||
@@ -44,15 +44,16 @@ class _AddExerciseScreenState extends State<AddExerciseScreen> {
|
||||
Widget _controlsBuilder(BuildContext context, ControlsDetails details) {
|
||||
return Row(
|
||||
children: [
|
||||
_currentStep == lastStepIndex
|
||||
? ElevatedButton(
|
||||
onPressed: details.onStepContinue,
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
)
|
||||
: TextButton(
|
||||
onPressed: details.onStepContinue,
|
||||
child: Text(AppLocalizations.of(context).next),
|
||||
),
|
||||
if (_currentStep == lastStepIndex)
|
||||
ElevatedButton(
|
||||
onPressed: details.onStepContinue,
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
)
|
||||
else
|
||||
TextButton(
|
||||
onPressed: details.onStepContinue,
|
||||
child: Text(AppLocalizations.of(context).next),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: details.onStepCancel,
|
||||
child: Text(AppLocalizations.of(context).previous),
|
||||
@@ -140,13 +141,14 @@ class _BasicStepContent extends StatelessWidget {
|
||||
categories: categories,
|
||||
title: AppLocalizations.of(context).category,
|
||||
callback: (ExerciseCategory newValue) {
|
||||
addExerciseProvider.targetArea = newValue;
|
||||
addExerciseProvider.category = newValue;
|
||||
},
|
||||
displayName: (ExerciseCategory c) => c.name,
|
||||
),
|
||||
AddExerciseMultiselectButton<Equipment>(
|
||||
title: AppLocalizations.of(context).equipment,
|
||||
items: equipment,
|
||||
initialItems: addExerciseProvider.equipment,
|
||||
onChange: (dynamic value) => print(value),
|
||||
onSaved: (dynamic entries) {
|
||||
if (entries != null && entries.isNotEmpty) {
|
||||
@@ -157,6 +159,7 @@ class _BasicStepContent extends StatelessWidget {
|
||||
AddExerciseMultiselectButton<Muscle>(
|
||||
title: AppLocalizations.of(context).muscles,
|
||||
items: muscles,
|
||||
initialItems: addExerciseProvider.primaryMuscles,
|
||||
onChange: (dynamic value) => print(value),
|
||||
onSaved: (dynamic muscles) {
|
||||
if (muscles != null && muscles.isNotEmpty) {
|
||||
@@ -166,6 +169,7 @@ class _BasicStepContent extends StatelessWidget {
|
||||
AddExerciseMultiselectButton<Muscle>(
|
||||
title: AppLocalizations.of(context).musclesSecondary,
|
||||
items: muscles,
|
||||
initialItems: addExerciseProvider.secondaryMuscles,
|
||||
onChange: (dynamic value) => print(value),
|
||||
onSaved: (dynamic muscles) {
|
||||
if (muscles != null && muscles.isNotEmpty) {
|
||||
@@ -233,13 +237,9 @@ class _ImagesStepContentState extends State<_ImagesStepContent> with ExerciseIma
|
||||
MaterialStateProperty.resolveWith((states) => Colors.black)),
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
children: const <TextSpan>[
|
||||
TextSpan(text: 'Only JPEG, PNG and WEBP files below 20 MB are supported'),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'Only JPEG, PNG and WEBP files below 20 MB are supported',
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
@@ -3,15 +3,17 @@ import 'package:multi_select_flutter/multi_select_flutter.dart';
|
||||
|
||||
class AddExerciseMultiselectButton<T> extends StatefulWidget {
|
||||
final List<T> items;
|
||||
List<T> initialItems = [];
|
||||
final String title;
|
||||
final ValueChanged<List<T?>> onChange;
|
||||
final FormFieldSetter<List<T?>?>? onSaved;
|
||||
|
||||
const AddExerciseMultiselectButton({
|
||||
AddExerciseMultiselectButton({
|
||||
Key? key,
|
||||
required this.items,
|
||||
required this.title,
|
||||
required this.onChange,
|
||||
this.initialItems = const [],
|
||||
this.onSaved,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -26,6 +28,7 @@ class _AddExerciseMultiselectButtonState<T> extends State<AddExerciseMultiselect
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MultiSelectDialogField(
|
||||
initialValue: widget.initialItems,
|
||||
onSaved: widget.onSaved,
|
||||
items: widget.items.map((item) => MultiSelectItem<T?>(item, item.name)).toList(),
|
||||
onConfirm: (value) {
|
||||
|
||||
Reference in New Issue
Block a user