Skip to content

Commit

Permalink
Hide open indicator on API error; Move opening hours code into featur…
Browse files Browse the repository at this point in the history
…e (Clean Architecture); Use dartz package for Either class (#394)

Co-authored-by: Omid Marfavi <[email protected]>
  • Loading branch information
fremartini and marfavi authored Feb 14, 2023
1 parent 2ee3c26 commit cbb4ff4
Show file tree
Hide file tree
Showing 71 changed files with 1,004 additions and 465 deletions.
11 changes: 11 additions & 0 deletions lib/core/errors/exceptions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:chopper/chopper.dart';

class ServerException implements Exception {
final String error;

ServerException({required this.error});

ServerException.fromResponse(Response response) : error = response.bodyString;
}

class LocalStorageException implements Exception {}
19 changes: 19 additions & 0 deletions lib/core/errors/failures.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:equatable/equatable.dart';

abstract class Failure extends Equatable {
final String reason;

const Failure(this.reason);

@override
List<Object?> get props => [reason];
}

// General failures
class ServerFailure extends Failure {
const ServerFailure(super.reason);
}

class LocalStorageFailure extends Failure {
const LocalStorageFailure(super.reason);
}
12 changes: 12 additions & 0 deletions lib/core/usecases/usecase.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:coffeecard/core/errors/failures.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';

abstract class UseCase<Type, Params> {
Future<Either<Failure, Type>> call(Params params);
}

class NoParams extends Equatable {
@override
List<Object> get props => [];
}
4 changes: 3 additions & 1 deletion lib/cubits/contributor/contributor_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ class ContributorCubit extends Cubit<ContributorState> {

Future<void> getContributors() async {
emit(const ContributorLoading());

final either = await _repository.getContributors();
either.caseOf(

either.fold(
(error) => emit(ContributorError(error.message)),
(contributors) => emit(ContributorLoaded(contributors)),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/cubits/environment/environment_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class EnvironmentCubit extends Cubit<EnvironmentState> {
Future<void> getConfig() async {
final either = await _configRepository.getEnvironmentType();

either.caseOf(
either.fold(
(error) => emit(EnvironmentError(error.message)),
(env) => emit(EnvironmentLoaded(env: env)),
);
Expand Down
32 changes: 16 additions & 16 deletions lib/cubits/form/form_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:coffeecard/utils/debouncing.dart';
import 'package:coffeecard/utils/either.dart';
import 'package:coffeecard/utils/input_validator.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';

part 'form_event.dart';
Expand All @@ -18,27 +18,27 @@ class FormBloc extends Bloc<FormEvent, FormState> {
(event, emit) async {
final text = event.input.trim();
for (final validator in validators) {
final result = await validator.validate(text);
if (result.isLeft) {
emit(
final either = await validator.validate(text);

either.fold(
(errorMessage) => emit(
state.copyWith(
loading: false,
canSubmit: false,
error: Left(result.left),
error: Left(errorMessage),
shouldDisplayError: validator.forceErrorMessage ? true : null,
),
);
return;
}
),
(_) => emit(
state.copyWith(
loading: false,
text: text,
canSubmit: true,
error: const Right(null),
),
),
);
}
emit(
state.copyWith(
loading: false,
text: text,
canSubmit: true,
error: const Right(null),
),
);
},
transformer: debounce ? debouncing() : null,
);
Expand Down
2 changes: 1 addition & 1 deletion lib/cubits/login/login_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class LoginCubit extends Cubit<LoginState> {

final either = await accountRepository.login(email, encodedPasscode);

either.caseOf(
either.fold(
(error) => emit(LoginError(formatErrorMessage(error.message))),
(user) {
sl<FirebaseAnalyticsEventLogging>().loginEvent();
Expand Down
2 changes: 1 addition & 1 deletion lib/cubits/occupation/occupation_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class OccupationCubit extends Cubit<OccupationState> {
Future<void> getOccupations() async {
final either = await occupationRepository.getOccupations();

either.caseOf(
either.fold(
(error) => emit(OccupationError(error.message)),
(occupations) => emit(OccupationLoaded(occupations: occupations)),
);
Expand Down
29 changes: 0 additions & 29 deletions lib/cubits/opening_hours/opening_hours_cubit.dart

This file was deleted.

2 changes: 1 addition & 1 deletion lib/cubits/products/products_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ProductsCubit extends Cubit<ProductsState> {
emit(const ProductsLoading());
final either = await _repository.getProducts();

either.caseOf(
either.fold(
(error) => emit(ProductsError(error.message)),
(products) {
final ticketProducts = products.where((p) => p.amount > 1);
Expand Down
66 changes: 34 additions & 32 deletions lib/cubits/purchase/purchase_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ class PurchaseCubit extends Cubit<PurchaseState> {
emit(const PurchaseStarted());

final either = await paymentHandler.initPurchase(product.id);
if (either.isRight) {
final Payment payment = either.right;

if (payment.status != PaymentStatus.error) {
emit(PurchaseProcessing(payment));
await paymentHandler.invokePaymentMethod(Uri.parse(payment.deeplink));
} else {
emit(PurchasePaymentRejected(payment));
}
} else {
emit(PurchaseError(either.left.message));
}
either.fold(
(error) => emit(PurchaseError(error.message)),
(payment) async {
if (payment.status != PaymentStatus.error) {
emit(PurchaseProcessing(payment));
await paymentHandler
.invokePaymentMethod(Uri.parse(payment.deeplink));
} else {
emit(PurchasePaymentRejected(payment));
}
},
);
}
}

Expand All @@ -45,28 +46,29 @@ class PurchaseCubit extends Cubit<PurchaseState> {
emit(PurchaseVerifying(payment));
final either = await paymentHandler.verifyPurchase(payment.id);

either.caseOf((error) {
emit(PurchaseError(either.left.message));
}, (status) {
if (status == PaymentStatus.completed) {
sl<FirebaseAnalyticsEventLogging>().purchaseCompletedEvent(payment);
emit(PurchaseCompleted(payment.copyWith(status: status)));
} else if (status == PaymentStatus.reserved) {
// NOTE, recursive call, potentially infinite.
// If payment has been reserved, i.e. approved by user
// we will keep checking the backend to verify payment has been captured
either.fold(
(error) => emit(PurchaseError(error.message)),
(status) {
if (status == PaymentStatus.completed) {
sl<FirebaseAnalyticsEventLogging>().purchaseCompletedEvent(payment);
emit(PurchaseCompleted(payment.copyWith(status: status)));
} else if (status == PaymentStatus.reserved) {
// NOTE, recursive call, potentially infinite.
// If payment has been reserved, i.e. approved by user
// we will keep checking the backend to verify payment has been captured

// Emit processing state to allow the verifyPurchase process again
emit(
PurchaseProcessing(
payment.copyWith(status: status),
),
);
verifyPurchase();
} else {
emit(PurchasePaymentRejected(payment.copyWith(status: status)));
}
});
// Emit processing state to allow the verifyPurchase process again
emit(
PurchaseProcessing(
payment.copyWith(status: status),
),
);
verifyPurchase();
} else {
emit(PurchasePaymentRejected(payment.copyWith(status: status)));
}
},
);
}
}
}
2 changes: 1 addition & 1 deletion lib/cubits/receipt/receipt_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ReceiptCubit extends Cubit<ReceiptState> {
Future<void> fetchReceipts() async {
final either = await _repository.getUserReceipts();

either.caseOf(
either.fold(
(error) => emit(
state.copyWith(
status: ReceiptStatus.failure,
Expand Down
4 changes: 2 additions & 2 deletions lib/cubits/register/register_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ class RegisterCubit extends Cubit<RegisterState> {
occupationId,
);

either.caseOf(
either.fold(
(error) => emit(RegisterError(error.message)),
(user) {
(_) {
emit(RegisterSuccess());
sl<FirebaseAnalyticsEventLogging>().signUpEvent();
},
Expand Down
79 changes: 39 additions & 40 deletions lib/cubits/statistics/statistics_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,52 +19,51 @@ class LeaderboardCubit extends Cubit<StatisticsState> {
Future<void> fetch() async {
final filter = state.filter;

final maybeLeaderboard = await _repo.getLeaderboard(filter);
final maybeUser = await _repo.getLeaderboardUser(filter);

if (maybeUser.isLeft) {
emit(StatisticsError(maybeUser.left.message, filter: filter));
return;
}
maybeUser.fold(
(l) => emit(StatisticsError(l.message, filter: filter)),
(user) async {
final maybeLeaderboard = await _repo.getLeaderboard(filter);

if (maybeLeaderboard.isLeft) {
emit(StatisticsError(maybeLeaderboard.left.message, filter: filter));
return;
}
maybeLeaderboard.fold(
(l) => emit(StatisticsError(l.message, filter: filter)),
(leaderboard) {
var userInLeaderboard = false;
final List<LeaderboardUser> users =
leaderboard.map((leaderboardUser) {
final isCurrentUser = leaderboardUser.id == user.id;

final user = maybeUser.right;
// set the 'found' flag if this is the current user
if (!userInLeaderboard && isCurrentUser) {
userInLeaderboard = true;
}

var userInLeaderboard = false;
final List<LeaderboardUser> leaderboard =
maybeLeaderboard.right.map((leaderboardUser) {
final isCurrentUser = leaderboardUser.id == user.id;
return LeaderboardUser(
id: leaderboardUser.id,
name: leaderboardUser.name,
score: leaderboardUser.score,
highlight: isCurrentUser,
rank: leaderboardUser.rank,
);
}).toList();

// set the 'found' flag if this is the current user
if (!userInLeaderboard && isCurrentUser) {
userInLeaderboard = true;
}
if (!userInLeaderboard) {
users.add(
LeaderboardUser(
id: user.id,
name: user.name,
highlight: true,
score: user.score,
rank: user.rank,
),
);
}

return LeaderboardUser(
id: leaderboardUser.id,
name: leaderboardUser.name,
score: leaderboardUser.score,
highlight: isCurrentUser,
rank: leaderboardUser.rank,
);
}).toList();

if (!userInLeaderboard) {
leaderboard.add(
LeaderboardUser(
id: user.id,
name: user.name,
highlight: true,
score: user.score,
rank: user.rank,
),
);
}

emit(StatisticsLoaded(leaderboard, filter: filter));
emit(StatisticsLoaded(users, filter: filter));
},
);
},
);
}
}
5 changes: 3 additions & 2 deletions lib/cubits/tickets/tickets_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class TicketsCubit extends Cubit<TicketsState> {
emit(TicketUsing(st.tickets));
final either = await _ticketRepository.useTicket(productId);

either.caseOf(
either.fold(
(error) => emit(TicketsUseError(error.message)),
(receipt) => emit(TicketUsed(receipt, st.tickets)),
);
Expand All @@ -32,7 +32,8 @@ class TicketsCubit extends Cubit<TicketsState> {

Future<void> refreshTickets() async {
final either = await _ticketRepository.getUserTickets();
either.caseOf(

either.fold(
(error) => emit(TicketsLoadError(error.message)),
(tickets) => emit(TicketsLoaded(tickets)),
);
Expand Down
Loading

0 comments on commit cbb4ff4

Please sign in to comment.