This commit is contained in:
2026-07-13 01:38:13 +03:00
parent 9eda119527
commit 98c5a7a1d1
54 changed files with 860 additions and 454 deletions

View File

@@ -8,7 +8,11 @@ class PhoneAuthBloc extends Bloc<PhoneAuthEvent, PhoneAuthState> {
PhoneAuthBloc(this._loginUseCase) : super(PhoneAuthState.initial()) {
on<PhoneChanged>((event, emit) {
emit(state.copyWith(phone: event.phone));
emit(state.copyWith(
phone: event.phone,
isSuccess: false,
error: null,
));
});
on<IsAdultChanged>((event, emit) {

View File

@@ -267,7 +267,7 @@ class MapBloc extends Bloc<ScooterEvent, ScooterState> {
) async {
await logoutUseCase();
stopNotificationStream();
splashBloc.add(AuthCheckRequested());
splashBloc.add(AuthLogoutRequested());
}
FutureOr<void> _onCheckUser(

View File

@@ -9,26 +9,53 @@ class NotificationsBloc extends Bloc<NotificationsEvent, NotificationsState> {
NotificationsBloc(this._getNotificationsUsecase) : super(const NotificationsState()) {
on<NotificationsFetchRequested>(_onFetchRequested);
on<NotificationsLoadMoreRequested>(_onLoadMoreRequested);
}
Future<void> _onFetchRequested(
NotificationsFetchRequested event,
Emitter<NotificationsState> emit,
) async {
emit(state.copyWith(status: NotificationsStatus.loading));
if (event.page == 1) {
emit(state.copyWith(status: NotificationsStatus.loading));
}
try {
final notifications = await _getNotificationsUsecase();
final result = await _getNotificationsUsecase(
page: event.page,
pageSize: 100,
);
final newNotifications = event.page == 1
? result.notifications
: [...state.notifications, ...result.notifications];
emit(state.copyWith(
status: NotificationsStatus.success,
notifications: notifications,
notifications: newNotifications,
currentPage: result.pagination.currentPage,
totalPages: result.pagination.lastPage,
hasMore: result.pagination.currentPage < result.pagination.lastPage,
isLoadingMore: false,
));
} catch (e) {
dev.log('NotificationsBloc: Ошибка загрузки: $e');
emit(state.copyWith(
status: NotificationsStatus.failure,
errorMessage: e.toString(),
status: event.page == 1 ? NotificationsStatus.failure : NotificationsStatus.success,
errorMessage: event.page == 1 ? e.toString() : null,
isLoadingMore: false,
));
}
}
Future<void> _onLoadMoreRequested(
NotificationsLoadMoreRequested event,
Emitter<NotificationsState> emit,
) async {
if (!state.hasMore || state.isLoadingMore) return;
emit(state.copyWith(isLoadingMore: true));
final nextPage = state.currentPage + 1;
add(NotificationsFetchRequested(page: nextPage));
}
}

View File

@@ -3,15 +3,17 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import '../../core/result.dart';
import '../../domain/entities/payment_card.dart';
import '../../domain/usecase/get_payment_cards_usecase.dart';
import '../../domain/usecase/get_profile_usecase.dart';
import '../../domain/usecase/set_main_payment_card_usecase.dart';
import '../event/payment_method_sheet_event.dart';
import '../state/payment_method_sheet_state.dart';
class PaymentMethodSheetBloc extends Bloc<PaymentMethodSheetEvent, PaymentMethodSheetState> {
final GetPaymentCardsUsecase _getPaymentCardsUsecase;
final GetProfileUseCase _getProfileUseCase;
final SetMainPaymentCardUsecase _setMainPaymentCardUsecase;
PaymentMethodSheetBloc(this._getPaymentCardsUsecase, this._setMainPaymentCardUsecase)
PaymentMethodSheetBloc(this._getPaymentCardsUsecase, this._setMainPaymentCardUsecase, this._getProfileUseCase)
: super(PaymentMethodSheetState(status: PaymentMethodSheetStatus.initial)) {
on<PaymentMethodSheetStarted>(_onStarted);
}
@@ -24,10 +26,13 @@ class PaymentMethodSheetBloc extends Bloc<PaymentMethodSheetEvent, PaymentMethod
try {
final result = await _getPaymentCardsUsecase();
final profile = await _getProfileUseCase();
if (result is Success<List<PaymentCard>>) {
emit(state.copyWith(
status: PaymentMethodSheetStatus.success,
balance: profile.balance,
cards: result.data ?? [],
));
} else {

View File

@@ -46,6 +46,8 @@ class PaymentMethodsBloc extends Bloc<PaymentMethodsEvent, PaymentMethodsState>
if (result is Success<List<PaymentCard>>) {
print("BALANCE: ${profile.balance} BYN");
emit(state.copyWith(
status: PaymentMethodsStatus.success,
cards: result.data ?? [],

View File

@@ -48,7 +48,7 @@ class ReservedRideBloc extends Bloc<ReservedRideEvent, ReservedRideState> {
final result = await _cancelRideUsecase(event.orderId);
if (result is Success<ScooterOrder>) {
if (result is Success) {
emit(state.copyWith(
status: ReservedRideStatus.success,
rideCancelled: true,

View File

@@ -40,7 +40,6 @@ class SendPhotoBloc extends Bloc<SendPhotoEvent, SendPhotoState> {
switch (result) {
case Success<List<int>>():
// ✅ Защита: если data null или пустой — не вызываем finish
if (result.data == null || result.data!.isEmpty) {
emit(state.copyWith(
status: SendPhotoStatus.failure,
@@ -49,20 +48,19 @@ class SendPhotoBloc extends Bloc<SendPhotoEvent, SendPhotoState> {
return;
}
// ✅ Передаём два параметра в UseCase
final finishResult = await _finishRideUsecase(
event.orderId,
result.data! // ✅ ! т.к. проверили выше
result.data!
);
switch (finishResult) {
case Success<ScooterOrder>():
case Success():
emit(state.copyWith(
status: SendPhotoStatus.success,
recievedPhotoIds: result.data,
));
case Failure():
print("❌ FINISH ERROR: ${finishResult.failure.message}");
print("FINISH ERROR: ${finishResult.failure.message}");
emit(state.copyWith(
status: SendPhotoStatus.failure,
errorMessage: 'Ошибка завершения поездки',

View File

@@ -16,6 +16,9 @@ class SplashBloc extends Bloc<SplashEvent, SplashState> {
on<PinVerificationSuccess>((event, emit) {
emit(AuthPinVerified());
});
on<AuthLogoutRequested>((event, emit) {
emit(AuthUnauthenticated());
});
}
Future<void> _onAuthCheckRequested(

View File

@@ -18,9 +18,10 @@ class TopUpBloc extends Bloc<TopUpEvent, TopUpState> {
required this.getCertificatesUsecase,
required this.purchaseCertificateUsecase,
required this.getUserCards,
}) : super(TopUpState(isLoading: true)) {
}) : super(const TopUpLoading()) {
on<LoadTopUpData>((event, emit) async {
emit(state.copyWith(isLoading: true));
emit(const TopUpLoading());
final results = await Future.wait([
getCertificatesUsecase(),
@@ -33,64 +34,68 @@ class TopUpBloc extends Bloc<TopUpEvent, TopUpState> {
switch ((certResult, cardResult)) {
case (Success(data: final certs), Success(data: final cards)):
emit(
state.copyWith(
certificates: certs,
cards: cards,
selectedTariff: certs!.length > 1
TopUpLoaded(
certificates: certs ?? [],
cards: cards ?? [],
selectedTariff: certs != null && certs.length > 1
? certs[1]
: (certs.isNotEmpty ? certs.first : null),
selectedCard: cards!.isEmpty
: (certs != null && certs.isNotEmpty ? certs.first : null),
selectedCard: cards == null || cards.isEmpty
? null
: cards.firstWhere(
(c) => c.isMain,
orElse: () => cards.first,
),
isLoading: false,
(c) => c.isMain,
orElse: () => cards.first,
),
),
);
case (
Success<List<Certificate>>(data: null),
Failure<List<PaymentCard>>(),
):
// TODO: Handle this case.
throw UnimplementedError();
case (
Success<List<Certificate>>(data: [...]),
Failure<List<PaymentCard>>(),
):
// TODO: Handle this case.
throw UnimplementedError();
case (Failure<List<Certificate>>(), _):
// TODO: Handle this case.
throw UnimplementedError();
default:
emit(const TopUpPaymentFailure('Не удалось загрузить данные'));
}
});
on<SelectCertificate>(
(event, emit) => emit(state.copyWith(selectedTariff: event.certificate)),
);
on<SelectCard>(
(event, emit) => emit(state.copyWith(selectedCard: event.card)),
);
on<ToggleAgreement>(
(event, emit) => emit(state.copyWith(isAgreed: event.value)),
);
on<SelectCertificate>((event, emit) {
if (state is TopUpLoaded) {
final currentState = state as TopUpLoaded;
emit(currentState.copyWith(selectedTariff: event.certificate));
}
});
on<SelectCard>((event, emit) {
if (state is TopUpLoaded) {
final currentState = state as TopUpLoaded;
emit(currentState.copyWith(selectedCard: event.card));
}
});
on<ToggleAgreement>((event, emit) {
if (state is TopUpLoaded) {
final currentState = state as TopUpLoaded;
emit(currentState.copyWith(isAgreed: event.value));
}
});
on<TopUpButtonPressed>((event, emit) async {
emit(state.copyWith(isLoading: true));
if (state is TopUpLoaded) {
final currentState = state as TopUpLoaded;
if (state.isAgreed) {
final result = await purchaseCertificateUsecase(
certificateId: state.selectedTariff!.id,
cardId:state.selectedCard!.id,
);
if (currentState.isAgreed &&
currentState.selectedTariff != null &&
currentState.selectedCard != null) {
if (result is Success) {
emit(state.copyWith(isLoading: false));
emit(currentState.copyWith(isProcessingPayment: true));
final result = await purchaseCertificateUsecase(
certificateId: currentState.selectedTariff!.id,
cardId: currentState.selectedCard!.id,
);
if (result is Success) {
emit(const TopUpPaymentSuccess());
} else {
emit(const TopUpPaymentFailure('Ошибка при оплате'));
}
}
}
});
}
}
}

View File

@@ -2,9 +2,11 @@ import 'dart:async';
import 'package:be_happy/core/failures.dart';
import 'package:be_happy/core/result.dart';
import 'package:be_happy/presentation/viewmodel/splash_bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../domain/usecase/verify_code_usecase.dart';
import '../event/spalsh_event.dart';
import '../event/verify_code_event.dart';
import '../state/verify_code_state.dart';
@@ -21,11 +23,11 @@ import '../state/verify_code_state.dart';
class VerifyCodeBloc extends Bloc<VerifyCodeEvent, VerifyCodeState> {
final VerifyCodeUseCase _verifyCodeUseCase;
final LoginUseCase _loginUseCase; // 🔹 Добавляем поле для юзкейса логина
final LoginUseCase _loginUseCase;
final SplashBloc _splashBloc;
Timer? _timer;
// 🔹 Обновляем конструктор
VerifyCodeBloc(this._verifyCodeUseCase, this._loginUseCase)
VerifyCodeBloc(this._verifyCodeUseCase, this._loginUseCase, this._splashBloc)
: super(VerifyCodeState.initial()) {
on<VerifyCodeStarted>((event, emit) {
@@ -96,12 +98,14 @@ class VerifyCodeBloc extends Bloc<VerifyCodeEvent, VerifyCodeState> {
emit(state.copyWith(isSubmitting: true, error: null));
// 🔹 Здесь автоматически подставится обновленный из стейта tempToken
final result = await _verifyCodeUseCase.execute(state.code, state.tempToken);
final newState = switch (result) {
Success() => (() {
_timer?.cancel();
_splashBloc.add(AuthCheckRequested());
return state.copyWith(
isSubmitting: false,
isSuccess: true,