fix
This commit is contained in:
@@ -52,17 +52,17 @@ class AddCardBloc extends Bloc<AddCardEvent, AddCardState> {
|
||||
}
|
||||
|
||||
final result = await addPaymentCardUsecase(
|
||||
cardNumber: state.cardNumber.replaceAll(' ', ''),
|
||||
/*cardNumber: state.cardNumber.replaceAll(' ', ''),
|
||||
expiryMonth: expiryParts[0],
|
||||
expiryYear: expiryParts[1],
|
||||
cardHolder: state.cardHolder.trim(),
|
||||
cvv: state.cvv,
|
||||
cvv: state.cvv,*/
|
||||
);
|
||||
|
||||
if (result is Success) {
|
||||
emit(state.copyWith(status: AddCardStatus.success));
|
||||
} else if (result is Failure) {
|
||||
final failure = result.failure;
|
||||
/*final failure = result.failure;
|
||||
|
||||
String errorMessage = 'Ошибка добавления карты';
|
||||
|
||||
@@ -72,12 +72,12 @@ class AddCardBloc extends Bloc<AddCardEvent, AddCardState> {
|
||||
errorMessage = 'Доступ заблокирован';
|
||||
} else if (failure is UnknownFailure) {
|
||||
errorMessage = failure.message ?? 'Неизвестная ошибка';
|
||||
}
|
||||
}*/
|
||||
|
||||
emit(state.copyWith(
|
||||
/* emit(state.copyWith(
|
||||
status: AddCardStatus.failure,
|
||||
errorMessage: errorMessage,
|
||||
));
|
||||
));*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,8 +264,9 @@ class MapBloc extends Bloc<ScooterEvent, ScooterState> {
|
||||
FutureOr<void> _onLogoutPressed(
|
||||
LogoutPressed event,
|
||||
Emitter<ScooterState> emit,
|
||||
) {
|
||||
logoutUseCase();
|
||||
) async {
|
||||
await logoutUseCase();
|
||||
stopNotificationStream();
|
||||
splashBloc.add(AuthCheckRequested());
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@ 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/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 SetMainPaymentCardUsecase _setMainPaymentCardUsecase;
|
||||
|
||||
PaymentMethodSheetBloc(this._getPaymentCardsUsecase)
|
||||
PaymentMethodSheetBloc(this._getPaymentCardsUsecase, this._setMainPaymentCardUsecase)
|
||||
: super(PaymentMethodSheetState(status: PaymentMethodSheetStatus.initial)) {
|
||||
on<PaymentMethodSheetStarted>(_onStarted);
|
||||
}
|
||||
@@ -41,4 +43,53 @@ class PaymentMethodSheetBloc extends Bloc<PaymentMethodSheetEvent, PaymentMethod
|
||||
));
|
||||
}
|
||||
}
|
||||
Future<void> _onSelectCard(
|
||||
SelectPaymentCardRequested event,
|
||||
Emitter<PaymentMethodSheetState> emit,
|
||||
) async {
|
||||
// Включаем локальный лоадер на шторке, либо просто ждем ответа
|
||||
emit(state.copyWith(status: PaymentMethodSheetStatus.loading));
|
||||
|
||||
final result = await _setMainPaymentCardUsecase(event.card.id);
|
||||
|
||||
if (result is Success<void>) {
|
||||
final cardsResult = await _getPaymentCardsUsecase();
|
||||
if (cardsResult is Success<List<PaymentCard>>) {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodSheetStatus.success,
|
||||
cards: cardsResult.data ?? [],
|
||||
));
|
||||
} else {
|
||||
emit(state.copyWith(status: PaymentMethodSheetStatus.failure, errorMessage: 'Ошибка обновления списка карт'));
|
||||
}
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodSheetStatus.failure,
|
||||
errorMessage: 'Не удалось установить карту основной',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Выносим загрузку в отдельный метод для переиспользования
|
||||
Future<void> _loadCards(Emitter<PaymentMethodSheetState> emit) async {
|
||||
try {
|
||||
final result = await _getPaymentCardsUsecase();
|
||||
if (result is Success<List<PaymentCard>>) {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodSheetStatus.success,
|
||||
cards: result.data ?? [],
|
||||
));
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodSheetStatus.failure,
|
||||
errorMessage: 'Failed to load payment cards',
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodSheetStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:be_happy/domain/usecase/add_payment_card_usecase.dart';
|
||||
import 'package:be_happy/domain/usecase/get_profile_usecase.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../core/result.dart';
|
||||
import '../../core/failures.dart';
|
||||
@@ -15,16 +19,19 @@ class PaymentMethodsBloc extends Bloc<PaymentMethodsEvent, PaymentMethodsState>
|
||||
final RemovePaymentCardUsecase _removePaymentCardUsecase;
|
||||
final SetMainPaymentCardUsecase _setMainPaymentCardUsecase;
|
||||
final GetProfileUseCase _getProfileUseCase;
|
||||
final AddPaymentCardUsecase _addPaymentCardUsecase;
|
||||
|
||||
PaymentMethodsBloc(
|
||||
this._getPaymentCardsUsecase,
|
||||
this._removePaymentCardUsecase,
|
||||
this._setMainPaymentCardUsecase,
|
||||
this._getProfileUseCase,
|
||||
this._addPaymentCardUsecase,
|
||||
) : super(PaymentMethodsState(status: PaymentMethodsStatus.initial)) {
|
||||
on<PaymentMethodsStarted>(_onStarted);
|
||||
on<PaymentMethodsDeleteCard>(_onDeleteCard);
|
||||
on<PaymentMethodsSetMainCard>(_onSetMainCard);
|
||||
on<AddPayemntCardPressed>(_onAddPayemntCardPressed);
|
||||
}
|
||||
|
||||
Future<void> _onStarted(
|
||||
@@ -136,4 +143,54 @@ class PaymentMethodsBloc extends Bloc<PaymentMethodsEvent, PaymentMethodsState>
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onAddPayemntCardPressed(
|
||||
AddPayemntCardPressed event,
|
||||
Emitter<PaymentMethodsState> emit
|
||||
) async {
|
||||
emit(state.copyWith(status: PaymentMethodsStatus.loading));
|
||||
|
||||
try {
|
||||
final result = await _addPaymentCardUsecase();
|
||||
|
||||
if (result is Success<Map<String, dynamic>>) {
|
||||
final formUrlString = result.data?['formUrl'] as String?;
|
||||
|
||||
if (formUrlString != null && formUrlString.isNotEmpty) {
|
||||
final url = Uri.parse(formUrlString);
|
||||
|
||||
if (await canLaunchUrl(url)) {
|
||||
launchUrl(
|
||||
url,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
emit(state.copyWith(status: PaymentMethodsStatus.success));
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodsStatus.failure,
|
||||
errorMessage: 'Не удалось открыть ссылку для оплаты',
|
||||
));
|
||||
}
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodsStatus.failure,
|
||||
errorMessage: 'Ссылка для привязки карты не получена от сервера',
|
||||
));
|
||||
}
|
||||
|
||||
} else if (result is Failure) {
|
||||
String errorMessage = 'Ошибка при добавлении карты';
|
||||
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodsStatus.failure,
|
||||
errorMessage: errorMessage,
|
||||
));
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: PaymentMethodsStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
));
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:be_happy/core/result.dart';
|
||||
import 'package:be_happy/domain/entities/payment_card.dart';
|
||||
import 'package:be_happy/domain/entities/subscription.dart';
|
||||
import 'package:be_happy/domain/usecase/activate_subscription_usecase.dart';
|
||||
import 'package:be_happy/domain/usecase/get_payment_cards_usecase.dart';
|
||||
import 'package:be_happy/domain/usecase/get_subscription_by_id_usecase.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -12,27 +14,35 @@ import '../state/susbcription_details_state.dart';
|
||||
class SubscriptionDetailsBloc
|
||||
extends Bloc<SubscriptionDetailsEvent, SubscriptionDetailsState> {
|
||||
final GetSubscriptionByIdUsecase getSubscriptionByIdUsecase;
|
||||
final GetPaymentCardsUsecase getPaymentCardsUsecase;
|
||||
final ActivateSubscriptionUsecase activateSubscriptionUsecase;
|
||||
final GetClientSubscriptionsUsecase getClientSubscriptionsUsecase;
|
||||
|
||||
SubscriptionDetailsBloc(
|
||||
this.getSubscriptionByIdUsecase,
|
||||
this.getPaymentCardsUsecase,
|
||||
this.activateSubscriptionUsecase,
|
||||
this.getClientSubscriptionsUsecase,
|
||||
) : super(DetailsLoading()) {
|
||||
on<LoadDetailsEvent>((event, emit) async {
|
||||
emit(DetailsLoading());
|
||||
try {
|
||||
final result = await getSubscriptionByIdUsecase(event.subscriptionId);
|
||||
final cardsResult = await getPaymentCardsUsecase();
|
||||
|
||||
final subsResult = await getSubscriptionByIdUsecase(
|
||||
event.subscriptionId,
|
||||
);
|
||||
|
||||
final clientSubsResult = await getClientSubscriptionsUsecase();
|
||||
bool isPurchased = false;
|
||||
|
||||
switch (clientSubsResult) {
|
||||
case Success<List<ClientSubscription>>():
|
||||
isPurchased = clientSubsResult.data?.any(
|
||||
isPurchased =
|
||||
clientSubsResult.data?.any(
|
||||
(element) => element.subscriptionId == event.subscriptionId,
|
||||
) ?? false;
|
||||
) ??
|
||||
false;
|
||||
break;
|
||||
|
||||
case Failure<List<ClientSubscription>>():
|
||||
@@ -40,16 +50,30 @@ class SubscriptionDetailsBloc
|
||||
break;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
switch (subsResult) {
|
||||
case Success<Subscription>():
|
||||
final sub = result.data;
|
||||
final sub = subsResult.data;
|
||||
if (sub == null) return;
|
||||
|
||||
List<PaymentCard> loadedCards = [];
|
||||
PaymentCard? defaultCard;
|
||||
|
||||
if (cardsResult is Success<List<PaymentCard>>) {
|
||||
loadedCards = cardsResult.data ?? [];
|
||||
if (loadedCards.isNotEmpty) {
|
||||
defaultCard = loadedCards.firstWhere(
|
||||
(element) => element.isMain,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
emit(
|
||||
DetailsContentState(
|
||||
subscription: sub,
|
||||
selectedPeriod: sub.options.first,
|
||||
isAlreadyPurchased: isPurchased,
|
||||
cards: loadedCards,
|
||||
selectedCard: defaultCard ?? loadedCards.first,
|
||||
),
|
||||
);
|
||||
break;
|
||||
@@ -70,6 +94,12 @@ class SubscriptionDetailsBloc
|
||||
}
|
||||
});
|
||||
|
||||
on<SelectCardEvent>((event, emit) {
|
||||
if (state is DetailsContentState) {
|
||||
emit((state as DetailsContentState).copyWith(selectedCard: event.card));
|
||||
}
|
||||
});
|
||||
|
||||
on<ToggleAgreementEvent>((event, emit) {
|
||||
if (state is DetailsContentState) {
|
||||
emit((state as DetailsContentState).copyWith(isAgreed: event.value));
|
||||
@@ -79,16 +109,18 @@ class SubscriptionDetailsBloc
|
||||
on<ActivateSubscriptionPressed>((event, emit) async {
|
||||
switch (state) {
|
||||
case DetailsContentState contentState:
|
||||
if (!contentState.isAgreed) {
|
||||
if (!contentState.isAgreed || contentState.selectedCard == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await activateSubscriptionUsecase(contentState.selectedPeriod.id);
|
||||
final result = await activateSubscriptionUsecase(
|
||||
contentState.selectedPeriod.id,
|
||||
contentState.selectedCard.id,
|
||||
);
|
||||
|
||||
if (result is Success) {
|
||||
emit(contentState.copyWith(isSuccess: true));
|
||||
} else {
|
||||
}
|
||||
} else {}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -22,7 +22,6 @@ class TopUpBloc extends Bloc<TopUpEvent, TopUpState> {
|
||||
on<LoadTopUpData>((event, emit) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
|
||||
// Запускаем оба запроса параллельно
|
||||
final results = await Future.wait([
|
||||
getCertificatesUsecase(),
|
||||
getUserCards(),
|
||||
@@ -77,5 +76,21 @@ class TopUpBloc extends Bloc<TopUpEvent, TopUpState> {
|
||||
on<ToggleAgreement>(
|
||||
(event, emit) => emit(state.copyWith(isAgreed: event.value)),
|
||||
);
|
||||
on<TopUpButtonPressed>((event, emit) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
|
||||
if (state.isAgreed) {
|
||||
final result = await purchaseCertificateUsecase(
|
||||
certificateId: state.selectedTariff!.id,
|
||||
cardId:state.selectedCard!.id,
|
||||
);
|
||||
|
||||
if (result is Success) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,26 @@ import '../../domain/usecase/verify_code_usecase.dart';
|
||||
import '../event/verify_code_event.dart';
|
||||
import '../state/verify_code_state.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:be_happy/core/failures.dart';
|
||||
import 'package:be_happy/core/result.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../domain/usecase/verify_code_usecase.dart';
|
||||
import '../../domain/usecase/login_usecase.dart'; // 🔹 Импортируем LoginUseCase
|
||||
import '../event/verify_code_event.dart';
|
||||
import '../state/verify_code_state.dart';
|
||||
|
||||
class VerifyCodeBloc extends Bloc<VerifyCodeEvent, VerifyCodeState> {
|
||||
final VerifyCodeUseCase _verifyCodeUseCase;
|
||||
final LoginUseCase _loginUseCase; // 🔹 Добавляем поле для юзкейса логина
|
||||
Timer? _timer;
|
||||
|
||||
VerifyCodeBloc(this._verifyCodeUseCase)
|
||||
// 🔹 Обновляем конструктор
|
||||
VerifyCodeBloc(this._verifyCodeUseCase, this._loginUseCase)
|
||||
: super(VerifyCodeState.initial()) {
|
||||
|
||||
on<VerifyCodeStarted>((event, emit) {
|
||||
emit(state.copyWith(
|
||||
phoneNumber: event.phoneNumber,
|
||||
@@ -24,56 +38,89 @@ class VerifyCodeBloc extends Bloc<VerifyCodeEvent, VerifyCodeState> {
|
||||
});
|
||||
|
||||
on<CodeChanged>((event, emit) {
|
||||
emit(state.copyWith(code: event.code));
|
||||
emit(state.copyWith(
|
||||
code: event.code,
|
||||
error: null,
|
||||
));
|
||||
});
|
||||
|
||||
on<ResendCodePressed>((event, emit) {
|
||||
if (state.secondsLeft == 0) {
|
||||
emit(state.copyWith(secondsLeft: 60));
|
||||
_startTimer();
|
||||
on<ResendCodePressed>((event, emit) async {
|
||||
if (state.secondsLeft == 0 && !state.isSubmitting) {
|
||||
emit(state.copyWith(isSubmitting: true, error: null));
|
||||
|
||||
try {
|
||||
final newTempToken = await _loginUseCase.execute("+${state.phoneNumber}");
|
||||
|
||||
emit(state.copyWith(
|
||||
isSubmitting: false,
|
||||
tempToken: newTempToken,
|
||||
secondsLeft: 60,
|
||||
code: '',
|
||||
error: null,
|
||||
isBlocked: false,
|
||||
));
|
||||
|
||||
_startTimer();
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
isSubmitting: false,
|
||||
error: 'Не удалось повторно отправить код. Попробуйте позже.',
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
on<TimerTicked>((event, emit) {
|
||||
emit(state.copyWith(secondsLeft: event.secondsLeft));
|
||||
});
|
||||
|
||||
on<VerifyCodeSubmitted>(_onVerifyCodeSubmitted);
|
||||
}
|
||||
|
||||
void _startTimer() {
|
||||
_timer?.cancel();
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (state.secondsLeft <= 1) {
|
||||
final currentSeconds = state.secondsLeft;
|
||||
if (currentSeconds <= 1) {
|
||||
timer.cancel();
|
||||
emit(state.copyWith(secondsLeft: 0));
|
||||
add(TimerTicked(0));
|
||||
} else {
|
||||
emit(state.copyWith(secondsLeft: state.secondsLeft - 1));
|
||||
add(TimerTicked(currentSeconds - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onVerifyCodeSubmitted(VerifyCodeSubmitted event,
|
||||
Emitter<VerifyCodeState> emit,) async {
|
||||
if (state.isSubmitting || state.code.length != 6) return;
|
||||
if (state.isSubmitting || state.code.length != 6 || state.secondsLeft == 0) return;
|
||||
|
||||
emit(state.copyWith(isSubmitting: true, error: null));
|
||||
|
||||
// 🔹 Здесь автоматически подставится обновленный из стейта tempToken
|
||||
final result = await _verifyCodeUseCase.execute(state.code, state.tempToken);
|
||||
|
||||
final newState = switch (result) {
|
||||
Success() => state.copyWith(
|
||||
isSubmitting: false,
|
||||
isSuccess: true,
|
||||
),
|
||||
Success() => (() {
|
||||
_timer?.cancel();
|
||||
return state.copyWith(
|
||||
isSubmitting: false,
|
||||
isSuccess: true,
|
||||
);
|
||||
})(),
|
||||
Failure(failure: final f) => switch (f) {
|
||||
AuthFailure(attemptsLeft: final count) => state.copyWith(
|
||||
isSubmitting: false,
|
||||
attemptsLeft: count,
|
||||
error: 'Неверный код. Осталось попыток: $count',
|
||||
),
|
||||
AuthBlockFailure() => state.copyWith(
|
||||
isSubmitting: false,
|
||||
isBlocked: true,
|
||||
error: 'Вы заблокированы за слишком частые попытки',
|
||||
),
|
||||
AuthBlockFailure() => (() {
|
||||
_timer?.cancel();
|
||||
return state.copyWith(
|
||||
isSubmitting: false,
|
||||
isBlocked: true,
|
||||
error: 'Вы заблокированы за слишком частые попытки',
|
||||
);
|
||||
})(),
|
||||
_ => state.copyWith(
|
||||
isSubmitting: false,
|
||||
error: 'Произошла ошибка. Попробуйте позже',
|
||||
@@ -89,4 +136,4 @@ class VerifyCodeBloc extends Bloc<VerifyCodeEvent, VerifyCodeState> {
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user