refactor(blog_post): Refactoring blog post create and conflict resolve

This commit is contained in:
2024-07-01 16:57:43 +03:00
parent beaaecad3c
commit a022d87adb
13 changed files with 23 additions and 56 deletions

View File

@@ -190,12 +190,12 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
final recommendedChartValues =
jsonDecode(responseChart.body)['dailyNutritionRecommendation']
.values
.map<int>((value) => value as int)
.map<double>((value) => value as double)
.toList();
final consumedChartValues =
jsonDecode(responseChart.body)['dailyNutritionIntake']
.values
.map<int>((value) => value as int)
.map<double>((value) => value as double)
.toList();
return [recommendedChartValues, consumedChartValues];
} else {
@@ -275,10 +275,10 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
moderationExpanded: false,
token: token,
recommendedChartValues: recommendedChartValues.values
.map<int>((value) => value as int)
.map<double>((value) => value as double)
.toList(),
consumedChartValues: consumedChartValues.values
.map<int>((value) => value as int)
.map<double>((value) => value as double)
.toList(),
meals: diary['meals'],
portions: diary['meals'][0]['portions'],

View File

@@ -57,8 +57,8 @@ class DiaryEditLoadedState extends DiaryEditState {
List<dynamic>? portions;
List<dynamic>? photos;
List<dynamic>? autocompleteList;
List<int> recommendedChartValues;
List<int> consumedChartValues;
List<double> recommendedChartValues;
List<double> consumedChartValues;
SelectionPopupModel? currentMeal;
SelectionPopupModel? portionController;
TimeOfDay? wakeupController;
@@ -117,8 +117,8 @@ class DiaryEditLoadedState extends DiaryEditState {
SelectionPopupModel? portionController,
String? diaryDate,
String? token,
List<int>? recommendedChartValues,
List<int>? consumedChartValues,
List<double>? recommendedChartValues,
List<double>? consumedChartValues,
List<dynamic>? meals,
List<dynamic>? portions,
List<dynamic>? photos,

View File

@@ -720,8 +720,8 @@ class DiaryEditScreen extends StatelessWidget {
}
_buildNutritionChart(BuildContext context, DiaryEditLoadedState state) {
final List<int> recommendedValues = state.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы
final List<int> consumedValues = state.consumedChartValues; //
final List<double> recommendedValues = state.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы
final List<double> consumedValues = state.consumedChartValues; //
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.h),
padding: EdgeInsets.symmetric(
@@ -1196,7 +1196,7 @@ class DiaryEditScreen extends StatelessWidget {
}
}
bool checkConsumedValues(List<int> recommendedValues, List<int> consumedValues) {
bool checkConsumedValues(List<double> recommendedValues, List<double> consumedValues) {
for (int i = 0; i < recommendedValues.length; i++) {
if (consumedValues[i] > recommendedValues[i]) {
// Если хотя бы одно потребленное значение превышает рекомендованное

View File

@@ -1,239 +0,0 @@
import 'bloc/blog_post_create_bloc.dart';
import 'models/blog_post_create_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'dart:io';
class BlogPostCreateScreen extends StatelessWidget {
const BlogPostCreateScreen({Key? key}) : super(key: key);
static Widget builder(BuildContext context) {
final blog_postID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<BlogPostCreateBloc>(
create: (context) => BlogPostCreateBloc(BlogPostCreateState(
blog_postModelObj:
BlogPostCreateModel(blog_postID: blog_postID.toString())))
..add(BlogPostCreateInitialEvent()),
child: BlogPostCreateScreen(),
);
}
Widget build(BuildContext context) {
return BlocBuilder<BlogPostCreateBloc, BlogPostCreateState>(
builder: (context, state) {
if (state is BlogPostCreateInitialState) {
// Это начальное состояние, данные ещё не загружены.
return Container(
color: Colors.white,
);
} else if (state is BlogPostCreateLoadedState) {
// Данные загружены, можно строить экран.
return _buildBlogPostCreate(context, state);
} else if (state is BlogPostCreateErrorState) {
// Произошла ошибка при загрузке данных.
return Center(child: Text(state.errorMessage));
} else {
// Другие состояния, если они есть.
return Container(); // Пустой контейнер для необработанных состояний.
}
},
);
}
_buildBlogPostCreate(BuildContext context, BlogPostCreateLoadedState state) {
print(state.image);
return Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
width: double.maxFinite,
decoration: AppDecoration.fillGray,
child: ListView(
children: [
SizedBox(height: 30.v),
Container(
margin: EdgeInsets.only(
left: 16.h,
right: 20.h,
bottom: 10.h,
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
children: <Widget>[
Text("Заголовок новости",
style: theme.textTheme.bodyLarge),
CustomTextFormField(
controller: state.title,
validator: _validateTitle,
),
SizedBox(height: 8.0),
Text("Ссылка на YouTube",
style: theme.textTheme.bodyLarge),
CustomTextFormField(
controller: state.youtubeURL,
validator: _validateYouTubeLink,
),
SizedBox(height: 8.0),
Text("Описание", style: theme.textTheme.bodyLarge),
CustomTextFormField(
controller: state.description,
validator: _validateDescription,
maxLines: 6,
),
SizedBox(height: 16.0),
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
// Add widget for image upload here
CustomOutlinedButton(
onPressed: () => _submitForm(context, state.title!.text,
state.description!.text, state.youtubeURL!.text),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
text: 'Создать',
),
],
),
],
),
),
],
),
),
);
}
}
_submitForm(
BuildContext context, String title, String description, String youtubeURL) {
String? titleError = _validateTitle(title);
String? descriptionError = _validateDescription(description);
String? youtubeURLError = _validateYouTubeLink(youtubeURL);
if (titleError != null ||
descriptionError != null ||
youtubeURLError != null) {
// Display error messages or handle invalid input as needed
// For example, show a snackbar with the error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(titleError ?? descriptionError ?? youtubeURLError!)),
);
} else {
// Proceed with form submission
context.read<BlogPostCreateBloc>().add(CreateBlogPost());
}
}
_buildAddPhoto(BuildContext context, BlogPostCreateLoadedState state) {
bool isLoading = state.isLoading;
return GestureDetector(
onTap: addImage(context, isLoading),
child: Stack(
children: [
if (state.image == null)
Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 40.h,
vertical: 80.v,
),
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(
left: 7.h,
top: 5.v,
),
child: Text(
"Добавить фото",
selectionColor: appTheme.blueA400,
),
),
Spacer(),
Icon(
Icons.photo_library,
color: appTheme.blueGray300,
),
],
),
),
if (state.image != null)
Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 40.h,
vertical: 80.v,
),
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Image(image: FileImage(File(state.image!.path))),
)
],
),
);
}
addImage(BuildContext context, bool isLoading) {
if (!isLoading) {
return () async {
final picker = ImagePicker();
List<XFile>? images = await picker.pickMultiImage();
if (images.isNotEmpty) {
for (var image in images) {
context.read<BlogPostCreateBloc>().add(UploadImages(image));
}
}
};
}
}
String? _validateTitle(value) {
if (value!.isEmpty) {
return 'Пожалуйста, введите заголовок';
}
return null;
}
String? _validateDescription(value) {
if (value!.isEmpty) {
return 'Пожалуйста, введите описание';
}
return null;
}
String? _validateYouTubeLink(value) {
if (value!.isEmpty) {
return 'Пожалуйста, введите ссылку';
} else if (!_isValidUrl(value)) {
return 'Пожалуйста, введите корректную ссылку';
}
return null;
}
bool _isValidUrl(String url) {
final RegExp urlRegExp =
new RegExp(r"^(http|https):\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$");
return urlRegExp.hasMatch(url);
}

View File

@@ -1,16 +0,0 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'blog_post_create_credentials_model.freezed.dart';
part 'blog_post_create_credentials_model.g.dart';
@freezed
class BlogPostCreateCredentialsModel with _$BlogPostCreateCredentialsModel {
const factory BlogPostCreateCredentialsModel({
required String title,
required String description,
@JsonKey(name: 'youtube_url') required String youtubeUrl,
@Default('active') String status,
}) = _BlogPostCreateCredentialsModel;
factory BlogPostCreateCredentialsModel.fromJson(Map<String, Object?> json) =>
_$BlogPostCreateCredentialsModelFromJson(json);
}

View File

@@ -3,7 +3,7 @@
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'blog_post_create_credentials_model.dart';
part of '../../../../features/blog_post_create/models/blog_post_create_credentials_model.dart';
// **************************************************************************
// FreezedGenerator

View File

@@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'blog_post_create_credentials_model.dart';
part of '../../../../features/blog_post_create/models/blog_post_create_credentials_model.dart';
// **************************************************************************
// JsonSerializableGenerator

View File

@@ -1,20 +0,0 @@
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/models/blog_post_create_credentials_model.dart';
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/services/blog_service.dart';
class BlogRepository {
final BlogService blogService;
BlogRepository({
required this.blogService,
});
Future<BlogPostCreateCredentialsModel> createBlogPost(
BlogPostCreateCredentialsModel model) async {
try {
return (await blogService.createBlogPost(model));
} catch (e) {
print(e);
throw (e);
}
}
}

View File

@@ -1,14 +0,0 @@
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/models/blog_post_create_credentials_model.dart';
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
part 'blog_service.g.dart';
@RestApi()
abstract class BlogService {
factory BlogService(Dio dio, {String baseUrl}) = _BlogService;
@POST('/blog_post/create')
Future<BlogPostCreateCredentialsModel> createBlogPost(
BlogPostCreateCredentialsModel model);
}

View File

@@ -1,78 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'blog_service.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers
class _BlogService implements BlogService {
_BlogService(
this._dio, {
this.baseUrl,
});
final Dio _dio;
String? baseUrl;
@override
Future<BlogPostCreateCredentialsModel> createBlogPost(
BlogPostCreateCredentialsModel model) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _result = await _dio.fetch<Map<String, dynamic>>(
_setStreamType<BlogPostCreateCredentialsModel>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/blog_post/create',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
))));
final value = BlogPostCreateCredentialsModel.fromJson(_result.data!);
return value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
String _combineBaseUrls(
String dioBaseUrl,
String? baseUrl,
) {
if (baseUrl == null || baseUrl.trim().isEmpty) {
return dioBaseUrl;
}
final url = Uri.parse(baseUrl);
if (url.isAbsolute) {
return url.toString();
}
return Uri.parse(dioBaseUrl).resolveUri(url).toString();
}
}