fix(diary): some fixes offline-diary

This commit is contained in:
mso7nueh
2026-05-14 19:52:15 +03:00
parent 63fc824449
commit af3b40d5f2
11 changed files with 114 additions and 228 deletions

View File

@@ -170,10 +170,19 @@ class _AddProductModalState extends State<AddProductModal> {
if (widget.portion != null) const SizedBox(width: 8),
InkWell(
onTap: () {
// BUG-4 fix: валидация полей перед отправкой
final productName = productNameController.text.trim();
if (productName.isEmpty) return;
final isGrams = selectedGlossary == null || selectedGlossary!.grInPortion == 0;
final portionType = isGrams ? 'gr' : 'pcs';
// BUG-4 fix: безопасный парсинг количества с дефолтом
final quantity = int.tryParse(quantityController.text) ?? (isGrams ? 100 : 1);
// Автоматически выбираем первый элемент, если текст точно совпадает
if (selectedGlossary == null &&
glossariesList.isNotEmpty &&
productNameController.text.trim().toLowerCase() == glossariesList[0].name.toLowerCase()) {
productName.toLowerCase() == glossariesList[0].name.toLowerCase()) {
selectedGlossary = glossariesList[0];
}
@@ -181,13 +190,9 @@ class _AddProductModalState extends State<AddProductModal> {
widget.diaryCubit.addProductToMeal(
diaryId: widget.state.diary.id,
mealId: widget.state.selectedMeal,
productName: productNameController.text,
portion: selectedGlossary == null
? 'gr'
: selectedGlossary!.grInPortion == 0
? 'gr'
: 'pcs',
quantity: int.parse(quantityController.text),
productName: productName,
portion: portionType,
quantity: quantity,
productNutritionalValueId: selectedGlossary?.id,
);
} else {
@@ -195,26 +200,22 @@ class _AddProductModalState extends State<AddProductModal> {
diaryId: widget.state.diary.id,
mealId: widget.state.selectedMeal,
portionId: widget.portion!.id,
productName: productNameController.text,
portion: selectedGlossary == null
? 'gr'
: selectedGlossary!.grInPortion == 0
? 'gr'
: 'pcs',
quantity: int.parse(quantityController.text),
productName: productName,
portion: portionType,
quantity: quantity,
productNutritionalValueId: selectedGlossary?.id,
portionState: widget.portion!.portionState,
);
}
Navigator.of(context).pop();
if (selectedGlossary == null && _previousProductName != productNameController.text) {
if (selectedGlossary == null && _previousProductName != productName) {
showModalBottomSheet(
isScrollControlled: true,
useRootNavigator: true,
context: context,
builder: (context) => Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: GlossaryModerationRequestModalWidget(productName: productNameController.text, diaryCubit: widget.diaryCubit),
child: GlossaryModerationRequestModalWidget(productName: productName, diaryCubit: widget.diaryCubit),
),
);
}

View File

@@ -116,7 +116,13 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
builder: (context, state) {
if (state is DiaryStateLoaded) {
int mealIndex = state.selectedMeal - 1;
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
final photos = state.diary.meals[mealIndex].photos;
// BUG-5 fix: сбросить индекс, если он вышел за пределы
// (например, после переключения на meal с меньшим числом фото)
if (currentSelectedPhotoIndex >= photos.length) {
currentSelectedPhotoIndex = 0;
}
if (photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
return GestureDetector(
onTap: () async {
showImagePicker(state.diary);

View File

@@ -25,6 +25,7 @@ class ProductWidget extends StatefulWidget {
class _ProductWidgetState extends State<ProductWidget> {
late final DiaryCubit diaryCubit;
late String portionName;
// BUG-8 fix: инициализируем из актуального state порции
late bool isEaten;
bool showCommentWidget = false;
@override
@@ -32,7 +33,7 @@ class _ProductWidgetState extends State<ProductWidget> {
super.initState();
diaryCubit = context.read<DiaryCubit>();
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false;
isEaten = widget.portion.portionState == PortionState.eatenFromDiet;
}
Widget _buildNutritionalInfo() {
@@ -93,7 +94,8 @@ class _ProductWidgetState extends State<ProductWidget> {
void didUpdateWidget(ProductWidget oldWidget) {
super.didUpdateWidget(oldWidget);
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false;
// BUG-8 fix: синхронизируем isEaten с актуальным state порции
isEaten = widget.portion.portionState == PortionState.eatenFromDiet;
}
Future updateProductInMeal(DiaryStateLoaded state, PortionModel portion) {

View File

@@ -23,6 +23,12 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
diaryCubit = context.read<DiaryCubit>();
}
/// Безопасное форматирование времени (HH:MM:SS → HH:MM, пустое → '--:--')
String _formatTime(String time) {
if (time.isEmpty || time.length < 5) return '--:--';
return time.substring(0, 5);
}
void showTimePicker(int diaryId) {
showModalBottomSheet<void>(
context: context,
@@ -110,7 +116,7 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
state.diary.meals[state.selectedMeal - 1].time.substring(0, state.diary.meals[state.selectedMeal - 1].time.length - 3),
_formatTime(state.diary.meals[state.selectedMeal - 1].time),
style: const TextStyle(
color: Color(0xFF6D6F71),
fontSize: 17,

View File

@@ -26,6 +26,12 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
diaryCubit = context.read<DiaryCubit>();
}
/// BUG-3 fix: безопасное форматирование времени (HH:MM:SS → HH:MM, null → '--:--')
String _formatTime(String? time) {
if (time == null || time.length < 5) return '--:--';
return time.substring(0, 5);
}
void showTimePicker(int diaryId, bool isWakeup) {
showModalBottomSheet<void>(
context: context,
@@ -142,10 +148,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
state.diary.wakeup.toString().substring(
0,
state.diary.wakeup.toString().length - 3,
),
_formatTime(state.diary.wakeup),
style: const TextStyle(
color: Color(0xFF6D6F71),
fontSize: 17,
@@ -201,10 +204,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
state.diary.retire.toString().substring(
0,
state.diary.retire.toString().length - 3,
),
_formatTime(state.diary.retire),
style: const TextStyle(
color: Color(0xFF6D6F71),
fontSize: 17,