import 'dart:typed_data'; import 'package:commons_core/commons_core.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../data/variety_repository.dart'; import '../db/enums.dart'; const Object _unset = Object(); /// Form state for the quick-add sheet. Only [label] is required; everything /// else is progressive disclosure behind [expanded]. class QuickAddState extends Equatable { const QuickAddState({ this.label = '', this.quantity, this.lotType = LotType.seed, this.photoBytes, this.expanded = false, this.submitting = false, this.submitted = false, this.showLabelError = false, this.addedCount = 0, }); final String label; final Quantity? quantity; final LotType lotType; final Uint8List? photoBytes; final bool expanded; final bool submitting; final bool submitted; final bool showLabelError; /// How many seeds this sheet has saved via "save & add another" (it stays /// open); drives the "N added" feedback and resets of the label field. final int addedCount; bool get hasValidLabel => label.trim().isNotEmpty; QuickAddState copyWith({ String? label, Object? quantity = _unset, LotType? lotType, Uint8List? photoBytes, bool? expanded, bool? submitting, bool? submitted, bool? showLabelError, int? addedCount, }) { return QuickAddState( label: label ?? this.label, quantity: identical(quantity, _unset) ? this.quantity : quantity as Quantity?, lotType: lotType ?? this.lotType, photoBytes: photoBytes ?? this.photoBytes, expanded: expanded ?? this.expanded, submitting: submitting ?? this.submitting, submitted: submitted ?? this.submitted, showLabelError: showLabelError ?? this.showLabelError, addedCount: addedCount ?? this.addedCount, ); } @override List get props => [ label, quantity, lotType, photoBytes, expanded, submitting, submitted, showLabelError, addedCount, ]; } /// Drives the 20-second quick-add flow and persists via [VarietyRepository]. class QuickAddCubit extends Cubit { QuickAddCubit(this._repo) : super(const QuickAddState()); final VarietyRepository _repo; void labelChanged(String value) => emit(state.copyWith(label: value, showLabelError: false)); void setQuantity(Quantity? quantity) => emit(state.copyWith(quantity: quantity)); void setLotType(LotType type) => emit(state.copyWith(lotType: type)); void photoPicked(Uint8List bytes) => emit(state.copyWith(photoBytes: bytes)); void toggleExpanded() => emit(state.copyWith(expanded: !state.expanded)); /// Validates and saves. No-op (sets [showLabelError]) if the label is empty. Future submit() async { if (!state.hasValidLabel) { emit(state.copyWith(showLabelError: true)); return; } if (state.submitting) return; emit(state.copyWith(submitting: true)); await _repo.addQuickVariety( label: state.label.trim(), lotType: state.lotType, quantity: state.quantity, photoBytes: state.photoBytes, ); emit(state.copyWith(submitting: false, submitted: true)); } /// Saves and keeps the sheet open for the next seed: resets the form to a /// clean state (keeping the chosen [lotType]) and bumps [addedCount]. Lets a /// user rip through a whole shelf without reopening the sheet each time. Future submitAndAddAnother() async { if (!state.hasValidLabel) { emit(state.copyWith(showLabelError: true)); return; } if (state.submitting) return; emit(state.copyWith(submitting: true)); await _repo.addQuickVariety( label: state.label.trim(), lotType: state.lotType, quantity: state.quantity, photoBytes: state.photoBytes, ); emit(QuickAddState(lotType: state.lotType, addedCount: state.addedCount + 1)); } }