Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Username1548
2024-11-15 19:38:08 +07:00
19 changed files with 734 additions and 57 deletions

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>

View File

@@ -5,8 +5,9 @@
</component>
<component name="ChangeListManager">
<list default="true" id="48538bf9-f01e-4931-b825-e9023d9f83f3" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/app/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/settings.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/settings.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" beforeDir="false" afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -19,33 +20,33 @@
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 0
}]]></component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 0
}</component>
<component name="ProjectId" id="2ogeByQ5QpkVqOhNR7IKHeDp4Dx" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.cidr.known.project.marker": "true",
"RunOnceActivity.readMode.enableVisualFormatting": "true",
"android.gradle.sync.needed": "true",
"cf.first.check.clang-format": "false",
"cidr.known.project.marker": "true",
"dart.analysis.tool.window.visible": "false",
"git-widget-placeholder": "main",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "C:/Users/mso7nueh/eatvio_app/android",
"project.structure.last.edited": "Suggestions",
"project.structure.proportion": "0.17",
"project.structure.side.proportion": "0.2",
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle",
"show.migrate.to.gradle.popup": "false"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;RunOnceActivity.readMode.enableVisualFormatting&quot;: &quot;true&quot;,
&quot;android.gradle.sync.needed&quot;: &quot;true&quot;,
&quot;cf.first.check.clang-format&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;dart.analysis.tool.window.visible&quot;: &quot;false&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/mso7nueh/eatvio_app/android&quot;,
&quot;project.structure.last.edited&quot;: &quot;Suggestions&quot;,
&quot;project.structure.proportion&quot;: &quot;0.17&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;reference.settingsdialog.project.gradle&quot;,
&quot;show.migrate.to.gradle.popup&quot;: &quot;false&quot;
}
}]]></component>
}</component>
<component name="PsdUISettings">
<option name="MODULE_TAB" value="Signing Configs" />
<option name="LAST_EDITED_SIGNING_CONFIG" value="debug" />

View File

@@ -6,8 +6,8 @@ plugins {
android {
namespace = "com.eatvioapp.app"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileSdk = 35
ndkVersion = "28.0.12433566"
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
@@ -21,7 +21,7 @@ android {
defaultConfig {
applicationId = "com.eatvioapp.app"
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
targetSdk = 35
versionCode = flutter.versionCode
versionName = flutter.versionName
}

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-all.zip

View File

@@ -0,0 +1,45 @@
import 'dart:io';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as p;
abstract class ImageCompressUtility {
static Future<List<File>> compressFilesList(List<File> files) async {
final List<File> compressedFilesList = [];
for (int i = 0; i < files.length; i++) {
compressedFilesList.add(await compressFile(files[i]));
}
return compressedFilesList;
}
static Future<File> compressFile(File file) async {
final image = img.decodeImage(file.readAsBytesSync())!;
int width = image.width;
int height = image.height;
if (width > height) {
if (width > 1080) {
height = (height * 1080) ~/ width;
width = 1080;
}
} else {
if (height > 1080) {
width = (width * 1080) ~/ height;
height = 1080;
}
}
String filePathWithoutExtension = p.withoutExtension(file.path);
String compressedFilePath = '${filePathWithoutExtension}_compressed.jpg';
final result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
compressedFilePath,
quality: 90,
minHeight: height,
minWidth: width,
);
return File(result!.path);
}
}

View File

@@ -5,6 +5,7 @@ import 'package:audioplayers/audioplayers.dart';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/core/initialization_cubit/initialization_cubit.dart';
import 'package:eatvio_app/core/initialization_cubit/initialization_state.dart';
import 'package:eatvio_app/core/token_storage.dart';
@@ -144,7 +145,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
await chatRepository.sendMessage(
chatId: chatId,
data: data,
files: files,
files: files != null ? await ImageCompressUtility.compressFilesList(files) : null,
);
AudioPlayer().play((AssetSource('audio/send_message.wav')));
}

View File

@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/features/consultant/become_consultant/models/consultant_profile_model.dart';
import 'package:eatvio_app/features/consultant/become_consultant/presentation/bloc/register_consultant_cubit/register_consultant_state.dart';
import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
@@ -38,6 +39,6 @@ class RegisterConsultantCubit extends Cubit<RegisterConsultantState> {
}
Future<List<String>> uploadPhotos(List<File> files) async {
return await consultantRepository.uploadPhotos(files: files);
return await consultantRepository.uploadPhotos(files: await ImageCompressUtility.compressFilesList(files));
}
}

View File

@@ -0,0 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'finish_diary_day_model.freezed.dart';
part 'finish_diary_day_model.g.dart';
@freezed
class FinishDiaryDayModel with _$FinishDiaryDayModel {
const factory FinishDiaryDayModel({
required String wakeUp,
required String retire,
required String comment,
}) = _FinishDiaryDayModel;
factory FinishDiaryDayModel.fromJson(Map<String, Object?> json) => _$FinishDiaryDayModelFromJson(json);
}

View File

@@ -0,0 +1,201 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// 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 'finish_diary_day_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
FinishDiaryDayModel _$FinishDiaryDayModelFromJson(Map<String, dynamic> json) {
return _FinishDiaryDayModel.fromJson(json);
}
/// @nodoc
mixin _$FinishDiaryDayModel {
String get wakeUp => throw _privateConstructorUsedError;
String get retire => throw _privateConstructorUsedError;
String get comment => throw _privateConstructorUsedError;
/// Serializes this FinishDiaryDayModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$FinishDiaryDayModelCopyWith<FinishDiaryDayModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $FinishDiaryDayModelCopyWith<$Res> {
factory $FinishDiaryDayModelCopyWith(
FinishDiaryDayModel value, $Res Function(FinishDiaryDayModel) then) =
_$FinishDiaryDayModelCopyWithImpl<$Res, FinishDiaryDayModel>;
@useResult
$Res call({String wakeUp, String retire, String comment});
}
/// @nodoc
class _$FinishDiaryDayModelCopyWithImpl<$Res, $Val extends FinishDiaryDayModel>
implements $FinishDiaryDayModelCopyWith<$Res> {
_$FinishDiaryDayModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? wakeUp = null,
Object? retire = null,
Object? comment = null,
}) {
return _then(_value.copyWith(
wakeUp: null == wakeUp
? _value.wakeUp
: wakeUp // ignore: cast_nullable_to_non_nullable
as String,
retire: null == retire
? _value.retire
: retire // ignore: cast_nullable_to_non_nullable
as String,
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$FinishDiaryDayModelImplCopyWith<$Res>
implements $FinishDiaryDayModelCopyWith<$Res> {
factory _$$FinishDiaryDayModelImplCopyWith(_$FinishDiaryDayModelImpl value,
$Res Function(_$FinishDiaryDayModelImpl) then) =
__$$FinishDiaryDayModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String wakeUp, String retire, String comment});
}
/// @nodoc
class __$$FinishDiaryDayModelImplCopyWithImpl<$Res>
extends _$FinishDiaryDayModelCopyWithImpl<$Res, _$FinishDiaryDayModelImpl>
implements _$$FinishDiaryDayModelImplCopyWith<$Res> {
__$$FinishDiaryDayModelImplCopyWithImpl(_$FinishDiaryDayModelImpl _value,
$Res Function(_$FinishDiaryDayModelImpl) _then)
: super(_value, _then);
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? wakeUp = null,
Object? retire = null,
Object? comment = null,
}) {
return _then(_$FinishDiaryDayModelImpl(
wakeUp: null == wakeUp
? _value.wakeUp
: wakeUp // ignore: cast_nullable_to_non_nullable
as String,
retire: null == retire
? _value.retire
: retire // ignore: cast_nullable_to_non_nullable
as String,
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$FinishDiaryDayModelImpl implements _FinishDiaryDayModel {
const _$FinishDiaryDayModelImpl(
{required this.wakeUp, required this.retire, required this.comment});
factory _$FinishDiaryDayModelImpl.fromJson(Map<String, dynamic> json) =>
_$$FinishDiaryDayModelImplFromJson(json);
@override
final String wakeUp;
@override
final String retire;
@override
final String comment;
@override
String toString() {
return 'FinishDiaryDayModel(wakeUp: $wakeUp, retire: $retire, comment: $comment)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$FinishDiaryDayModelImpl &&
(identical(other.wakeUp, wakeUp) || other.wakeUp == wakeUp) &&
(identical(other.retire, retire) || other.retire == retire) &&
(identical(other.comment, comment) || other.comment == comment));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, wakeUp, retire, comment);
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$FinishDiaryDayModelImplCopyWith<_$FinishDiaryDayModelImpl> get copyWith =>
__$$FinishDiaryDayModelImplCopyWithImpl<_$FinishDiaryDayModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$FinishDiaryDayModelImplToJson(
this,
);
}
}
abstract class _FinishDiaryDayModel implements FinishDiaryDayModel {
const factory _FinishDiaryDayModel(
{required final String wakeUp,
required final String retire,
required final String comment}) = _$FinishDiaryDayModelImpl;
factory _FinishDiaryDayModel.fromJson(Map<String, dynamic> json) =
_$FinishDiaryDayModelImpl.fromJson;
@override
String get wakeUp;
@override
String get retire;
@override
String get comment;
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$FinishDiaryDayModelImplCopyWith<_$FinishDiaryDayModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,23 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'finish_diary_day_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$FinishDiaryDayModelImpl _$$FinishDiaryDayModelImplFromJson(
Map<String, dynamic> json) =>
_$FinishDiaryDayModelImpl(
wakeUp: json['wakeUp'] as String,
retire: json['retire'] as String,
comment: json['comment'] as String,
);
Map<String, dynamic> _$$FinishDiaryDayModelImplToJson(
_$FinishDiaryDayModelImpl instance) =>
<String, dynamic>{
'wakeUp': instance.wakeUp,
'retire': instance.retire,
'comment': instance.comment,
};

View File

@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
@@ -34,6 +35,19 @@ class DiaryCubit extends Cubit<DiaryState> {
}
}
finishDiaryDay({
required String date,
required int diaryId,
}) async {
await diaryRepository.finishDiaryDay(
diaryId: diaryId,
wakeUp: '',
retire: '',
comment: '',
);
await getDiaryDay(date, false);
}
Future<NutritionDataModel> getNutrition(int index) async {
return (await diaryRepository.getNutritionData(index));
}
@@ -101,7 +115,7 @@ class DiaryCubit extends Cubit<DiaryState> {
required String date,
}) async {
await diaryRepository.uploadPhotos(
files: files,
files: await ImageCompressUtility.compressFilesList(files),
diaryId: diaryId,
mealId: mealId,
);

View File

@@ -5,6 +5,7 @@ import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/finish_day_button_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/satiety_rating_widget.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
@@ -33,6 +34,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
CalendarFormat _calendarFormat = CalendarFormat.month;
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay = DateTime.now();
late final DateTime? today;
int currentSelectedMealNumber = 1;
GlossaryModel? selectedGlossary;
final productNameController = TextEditingController();
@@ -40,6 +42,13 @@ class _DiaryScreenState extends State<DiaryScreen> {
int currentSelectedPhotoIndex = 0;
String time = "${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}";
DateTime _time = DateTime.now();
bool enableQuantity = false;
@override
void initState() {
super.initState();
today = _selectedDay;
}
_showDatePicker() {
showModalBottomSheet<void>(
@@ -65,12 +74,6 @@ class _DiaryScreenState extends State<DiaryScreen> {
InkWell(
onTap: () {
Navigator.of(context).pop();
showModalBottomSheet(
useRootNavigator: true,
useSafeArea: false,
context: context,
builder: (context) => GlossaryModerationRequestModalWidget(),
);
},
borderRadius: BorderRadius.circular(20),
child: Ink(
@@ -128,6 +131,11 @@ class _DiaryScreenState extends State<DiaryScreen> {
state.currentSelectedSatietyRating,
),
if (state is DiaryStateLoaded) _chart(state.nutrition),
if (state is DiaryStateLoaded && state.diary.closed == 0 && _selectedDay == today)
Padding(
padding: const EdgeInsets.only(top: 12),
child: FinishDayButtonWidget(diaryDay: state.diary),
),
const SizedBox(height: 32),
],
),
@@ -164,13 +172,20 @@ class _DiaryScreenState extends State<DiaryScreen> {
);
}
_nextCalendarFormat() {
setState(() {
_calendarFormat = CalendarFormat.values[(_calendarFormat.index - 1) % CalendarFormat.values.length];
});
}
_arrowDown() {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onVerticalDragStart: (_) {
setState(() {
_calendarFormat = CalendarFormat.values[(_calendarFormat.index - 1) % CalendarFormat.values.length];
});
_nextCalendarFormat();
},
onTap: () {
_nextCalendarFormat();
},
child: Container(
width: double.infinity,
@@ -504,17 +519,27 @@ class _DiaryScreenState extends State<DiaryScreen> {
),
Column(
children: [
_chartData('Калории',
'${nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал',),
_chartData(
'Калории',
'${nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал',
),
const SizedBox(height: 8),
_chartData('Белки',
'${nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed(0)} грамм',),
_chartData(
'Белки',
'${nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed(
0,
)} грамм',
),
const SizedBox(height: 8),
_chartData('Жиры',
'${nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм',),
_chartData(
'Жиры',
'${nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм',
),
const SizedBox(height: 8),
_chartData('Углеводы',
'${nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм',),
_chartData(
'Углеводы',
'${nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм',
),
],
),
],
@@ -835,7 +860,10 @@ class _DiaryScreenState extends State<DiaryScreen> {
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
return Padding(
padding: const EdgeInsets.all(16) + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
padding: const EdgeInsets.all(16) +
EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
@@ -878,6 +906,11 @@ class _DiaryScreenState extends State<DiaryScreen> {
selectedGlossary = suggestionsList.first;
});
}
if (suggestionsList.length == 0) {
setState(() {
enableQuantity = true;
});
}
return suggestionsList;
},
builder: (context, controller, focusNode) {
@@ -888,21 +921,31 @@ class _DiaryScreenState extends State<DiaryScreen> {
hintText: 'Введите название продукта/блюда',
);
},
emptyBuilder: (context) => const SizedBox.shrink(),
emptyBuilder: (context) {
return const SizedBox.shrink();
},
errorBuilder: (context, object) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Нет подходящих вариантов'),
),
),
if (selectedGlossary != null)
if (selectedGlossary != null || enableQuantity)
Padding(
padding: const EdgeInsets.only(top: 8),
child: CustomTextFormField(
controller: quantityController,
keyboardType: TextInputType.number,
hintText: selectedGlossary!.grInPortion == 0 ? '100' : '1',
hintText: selectedGlossary == null
? '100'
: selectedGlossary!.grInPortion == 0
? '100'
: '1',
autofocus: true,
suffixText: selectedGlossary!.grInPortion == 0 ? 'гр' : 'шт',
suffixText: selectedGlossary == null
? 'гр'
: selectedGlossary?.grInPortion == 0
? 'гр'
: 'шт',
),
),
const SizedBox(height: 16),
@@ -941,7 +984,11 @@ class _DiaryScreenState extends State<DiaryScreen> {
diaryId: diaryDay.id,
mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
productName: productNameController.text,
portion: selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs',
portion: selectedGlossary == null
? 'gr'
: selectedGlossary!.grInPortion == 0
? 'gr'
: 'pcs',
quantity: int.parse(quantityController.text),
date: _selectedDay.toString(),
);
@@ -951,12 +998,32 @@ class _DiaryScreenState extends State<DiaryScreen> {
mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
portionId: portionId,
productName: productNameController.text,
portion: selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs',
portion: selectedGlossary == null
? 'gr'
: selectedGlossary!.grInPortion == 0
? 'gr'
: 'pcs',
quantity: int.parse(quantityController.text),
date: _selectedDay.toString(),
);
}
Navigator.of(context).pop();
if (enableQuantity) {
showModalBottomSheet(
isScrollControlled: true,
useRootNavigator: true,
context: context,
builder: (context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: GlossaryModerationRequestModalWidget(productName: productNameController.text),
),
);
}
setState(() {
enableQuantity = false;
});
},
borderRadius: BorderRadius.circular(20),
child: Ink(
@@ -994,6 +1061,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
onTap: () {
setState(() {
selectedGlossary = null;
enableQuantity = false;
productNameController.clear();
quantityController.clear();
});

View File

@@ -0,0 +1,50 @@
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class FinishDayButtonWidget extends StatefulWidget {
final DiaryDayModel diaryDay;
const FinishDayButtonWidget({
super.key,
required this.diaryDay,
});
@override
State<FinishDayButtonWidget> createState() => _FinishDayButtonWidgetState();
}
class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => context.read<DiaryCubit>().finishDiaryDay(
diaryId: widget.diaryDay.id,
date: widget.diaryDay.date,
),
borderRadius: BorderRadius.circular(20),
child: Ink(
padding: EdgeInsets.symmetric(horizontal: 8),
width: MediaQuery.of(context).size.width - 16,
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Закончить день',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
);
}
}

View File

@@ -1,17 +1,222 @@
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:flutter/material.dart';
class GlossaryModerationRequestModalWidget extends StatefulWidget {
const GlossaryModerationRequestModalWidget({super.key});
final String productName;
const GlossaryModerationRequestModalWidget({
super.key,
required this.productName,
});
@override
State<GlossaryModerationRequestModalWidget> createState() => _GlossaryModerationRequestModalWidgetState();
}
enum GlossaryState { alert, addNewProduct, finish }
class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModerationRequestModalWidget> {
GlossaryState glossaryState = GlossaryState.alert;
final productNameController = TextEditingController();
@override
Widget build(BuildContext context) {
return Dialog(
child: Column(),
if (glossaryState == GlossaryState.alert) {
return _alert();
} else {
return _addNewGlossaryProduct();
}
}
@override
void initState() {
super.initState();
productNameController.text = widget.productName;
}
_alert() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Добавление нового продукта',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
Text(
'Оу... а у нас нет в базе калорийности такого продукта. Если вы знаете, давайте её запишем?',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 24),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.pop(context);
},
borderRadius: BorderRadius.circular(20),
child: Ink(
decoration: BoxDecoration(
color: Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'В другой раз',
style: TextStyle(
color: Color(0xFF6D6F71),
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
),
),
),
const SizedBox(width: 8),
Expanded(
child: InkWell(
onTap: () {
setState(() {
glossaryState = GlossaryState.addNewProduct;
});
},
borderRadius: BorderRadius.circular(20),
child: Ink(
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Записать',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
),
),
),
],
),
],
),
);
}
_addNewGlossaryProduct() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Добавление нового продукта',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 16),
Text(
'Если у вас данные по нему\nпросим заполнить значения на 100гр.',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 24),
CustomTextFormField(
hintText: 'Сырный суп',
controller: productNameController,
),
const SizedBox(height: 8),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: CustomTextFormField(
hintText: 'Калории',
),
),
const SizedBox(width: 8),
Expanded(
child: CustomTextFormField(
hintText: 'Белки',
),
),
],
),
const SizedBox(height: 8),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: CustomTextFormField(
hintText: 'Жиры',
),
),
const SizedBox(width: 8),
Expanded(
child: CustomTextFormField(
hintText: 'Углеводы',
),
),
],
),
const SizedBox(height: 12),
InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(20),
child: Ink(
padding: EdgeInsets.symmetric(horizontal: 8),
width: MediaQuery.of(context).size.width - 16,
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Сохранить',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
),
],
),
);
}
}

View File

@@ -107,4 +107,20 @@ class DiaryRepository {
rethrow;
}
}
Future<void> finishDiaryDay({
required int diaryId,
required String wakeUp,
required String retire,
required String comment,
}) async {
return await diaryService.finishDiaryDay(
diaryId: diaryId,
// finishDiaryDayModel: FinishDiaryDayModel(
// wakeUp: wakeUp,
// retire: retire,
// comment: comment,
// ),
);
}
}

View File

@@ -65,4 +65,10 @@ abstract class DiaryService {
@Path('meal_id') required int mealId,
@Path('portion_id') required int portionId,
});
@POST('/api/diary/{diary_id}/finishDay')
Future<void> finishDiaryDay({
@Path('diary_id') required int diaryId,
// @Body() required FinishDiaryDayModel finishDiaryDayModel,
});
}

View File

@@ -292,6 +292,31 @@ class _DiaryService implements DiaryService {
await _dio.fetch<void>(_options);
}
@override
Future<void> finishDiaryDay({required int diaryId}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/api/diary/${diaryId}/finishDay',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -1,8 +1,10 @@
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/locator_service.dart';
import 'package:eatvio_app/old/feed/blog_post_create_screen/models/blog_post_create_model.dart';
@@ -81,7 +83,7 @@ class BlogPostCreateBloc extends Bloc<BlogPostCreateEvent, BlogPostCreateState>
'description': description,
'state': 'active',
'youtube_url': youtubeURL,
'image': image != null ? await MultipartFile.fromFile(image.path, filename: image.name) : null,
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image.path))).path, filename: image.name) : null,
});
final response = await sl<Dio>().post(

View File

@@ -54,6 +54,8 @@ dependencies:
flutter_portal: ^1.1.4
fluttertoast: ^8.2.8
flutter_rating_bar: ^4.0.1
flutter_image_compress: ^2.3.0
image: ^4.3.0
dev_dependencies:
build_runner: ^2.4.13