Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:monumento/domain/entities/monument_entity.dart';
import 'package:monumento/domain/repositories/social_repository.dart';

part 'monument_checkout_event.dart';
part 'monument_checkout_state.dart';


class MonumentCheckoutBloc
extends Bloc<MonumentCheckoutEvent, MonumentCheckoutState> {
final SocialRepository _socialRepository;

MonumentCheckoutBloc(this._socialRepository)
: super(MonumentCheckoutInitial()) {
on<RequestMonumentCheckout>(_onRequestCheckout);
on<ConfirmMonumentCheckout>(_onConfirmCheckout);
on<CheckIfMonumentIsCheckedOut>(_onCheckCheckoutStatus);
}

Future<void> _onRequestCheckout(
RequestMonumentCheckout event,
Emitter<MonumentCheckoutState> emit
) async {
try {
// Check if the monument is currently checked in
bool isCheckedIn = await _socialRepository.checkInStatus(
monumentId: event.monument.id
);

if (!isCheckedIn) {
emit(MonumentCheckoutFailure(
message: "You are not checked in to this monument"
));
return;
}

// Emit a state indicating checkout is ready to be confirmed
emit(MonumentCheckoutRequested(monument: event.monument));
} catch (e) {
emit(MonumentCheckoutFailure(message: e.toString()));
}
}

Future<void> _onConfirmCheckout(
ConfirmMonumentCheckout event,
Emitter<MonumentCheckoutState> emit
) async {
try {
emit(MonumentCheckoutLoading());

// Perform checkout
bool checkoutSuccess = await _socialRepository.monumentCheckOut(
monumentId: event.monument.id
);

if (checkoutSuccess) {
// This state will only be emitted after a new successful checkout action
emit(MonumentCheckedOut());
} else {
emit(MonumentCheckoutFailure(
message: "Failed to check out of the monument"
));
}
} catch (e) {
emit(MonumentCheckoutFailure(message: e.toString()));
}
}

Future<void> _onCheckCheckoutStatus(
CheckIfMonumentIsCheckedOut event,
Emitter<MonumentCheckoutState> emit
) async {
try {
emit(MonumentCheckoutLoading());

// Check checkout status
bool isCheckedIn = await _socialRepository.checkInStatus(
monumentId: event.monument.id
);

if (isCheckedIn) {
emit(MonumentNotCheckedOut());
} else {
// Use a different state for monuments already checked out
// versus monuments that were just checked out by user action
emit(MonumentAlreadyCheckedOut());
}
} catch (e) {
emit(MonumentCheckoutFailure(message: e.toString()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
part of 'monument_checkout_bloc.dart';

sealed class MonumentCheckoutEvent extends Equatable {
const MonumentCheckoutEvent();

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

final class RequestMonumentCheckout extends MonumentCheckoutEvent {
final MonumentEntity monument;

const RequestMonumentCheckout({required this.monument});

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

final class ConfirmMonumentCheckout extends MonumentCheckoutEvent {
final MonumentEntity monument;

const ConfirmMonumentCheckout({required this.monument});

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

final class CheckIfMonumentIsCheckedOut extends MonumentCheckoutEvent {
final MonumentEntity monument;

const CheckIfMonumentIsCheckedOut({required this.monument});

@override
List<Object> get props => [monument];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
part of 'monument_checkout_bloc.dart';

sealed class MonumentCheckoutState extends Equatable {
const MonumentCheckoutState();

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

final class MonumentCheckoutInitial extends MonumentCheckoutState {}

final class MonumentCheckoutLoading extends MonumentCheckoutState {}

final class MonumentCheckoutRequested extends MonumentCheckoutState {
final MonumentEntity monument;

const MonumentCheckoutRequested({required this.monument});

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

// This state is emitted only on a successful checkout action
final class MonumentCheckedOut extends MonumentCheckoutState {}

// This state is emitted during the initial load if monument is already checked out
final class MonumentAlreadyCheckedOut extends MonumentCheckoutState {}

final class MonumentNotCheckedOut extends MonumentCheckoutState {}

final class MonumentCheckoutFailure extends MonumentCheckoutState {
final String message;

const MonumentCheckoutFailure({required this.message});

@override
List<Object> get props => [message];
}
26 changes: 26 additions & 0 deletions lib/data/repositories/firebase_social_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,32 @@ class FirebaseSocialRepository implements SocialRepository {
return true;
}

@override
Future<bool> monumentCheckOut({required String monumentId}) async {
var (userLoggedIn, user) = await authenticationRepository.getUser();
if (!userLoggedIn) {
throw Exception("User not logged in");
}

QuerySnapshot snap = await _database
.collection("users")
.doc(user?.uid)
.collection("checkIns")
.where("monumentId", isEqualTo: monumentId)
.get();

if (snap.docs.isEmpty) {
return false;
}

for (var doc in snap.docs) {
await doc.reference.delete();
}

return true;
}


@override
Future<bool> checkInStatus({required String monumentId}) async {
var (userLoggedIn, user) = await authenticationRepository.getUser();
Expand Down
2 changes: 2 additions & 0 deletions lib/domain/repositories/social_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,7 @@ abstract interface class SocialRepository {

Future<bool> checkInStatus({required String monumentId});

Future<bool> monumentCheckOut({required String monumentId});

Future<List<UserModel>> loadUser(List<String> userConnections);
}
Loading