Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile: Prepare demo app POC with hardcoded data #960

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
9 changes: 7 additions & 2 deletions recipients_app/lib/core/cubits/auth/auth_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ class AuthCubit extends Cubit<AuthState> {
if (user != null) {
try {
final recipient = await userRepository.fetchRecipient(user);
Organization? organization;

if (recipient?.organizationRef != null) {
organization = await organizationRepository.fetchOrganization(recipient!.organizationRef!);
}
KarinBerg marked this conversation as resolved.
Show resolved Hide resolved

emit(
AuthState(
status: AuthStatus.authenticated,
firebaseUser: user,
recipient: recipient,
organization: organization,
),
);
} on Exception catch (ex, stackTrace) {
Expand Down Expand Up @@ -59,8 +65,7 @@ class AuthCubit extends Cubit<AuthState> {
Organization? organization;

if (recipient?.organizationRef != null) {
organization = await organizationRepository
.fetchOrganization(recipient!.organizationRef!);
organization = await organizationRepository.fetchOrganization(recipient!.organizationRef!);
}

emit(
Expand Down
4 changes: 3 additions & 1 deletion recipients_app/lib/core/cubits/auth/auth_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,21 @@ class AuthState extends Equatable {
});

@override
List<Object?> get props => [status, firebaseUser, recipient, exception];
List<Object?> get props => [status, firebaseUser, recipient, exception, organization];

AuthState copyWith({
AuthStatus? status,
User? firebaseUser,
Recipient? recipient,
Exception? exception,
Organization? organization,
}) {
return AuthState(
status: status ?? this.status,
firebaseUser: firebaseUser ?? this.firebaseUser,
recipient: recipient ?? this.recipient,
exception: exception ?? this.exception,
organization: organization ?? this.organization,
);
}
}
149 changes: 149 additions & 0 deletions recipients_app/lib/data/datasource/demo/demo_user.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import "package:firebase_auth/firebase_auth.dart";

class DemoUser implements User {
@override
String? get displayName => "demo user";

@override
String? get email => null;

@override
bool get emailVerified => true;

@override
Future<String?> getIdToken([bool forceRefresh = false]) {
throw UnimplementedError();
}

@override
Future<void> delete() {
throw UnimplementedError();
}

@override
Future<IdTokenResult> getIdTokenResult([bool forceRefresh = false]) {
throw UnimplementedError();
}

@override
bool get isAnonymous => throw UnimplementedError();

@override
Future<UserCredential> linkWithCredential(AuthCredential credential) {
throw UnimplementedError();
}

@override
Future<ConfirmationResult> linkWithPhoneNumber(String phoneNumber, [RecaptchaVerifier? verifier]) {
throw UnimplementedError();
}

@override
Future<UserCredential> linkWithPopup(AuthProvider provider) {
throw UnimplementedError();
}

@override
Future<UserCredential> linkWithProvider(AuthProvider provider) {
throw UnimplementedError();
}

@override
Future<void> linkWithRedirect(AuthProvider provider) {
throw UnimplementedError();
}

@override
UserMetadata get metadata => throw UnimplementedError();

@override
MultiFactor get multiFactor => throw UnimplementedError();

@override
String? get phoneNumber => throw UnimplementedError();

@override
String? get photoURL => throw UnimplementedError();

@override
List<UserInfo> get providerData => throw UnimplementedError();

@override
Future<UserCredential> reauthenticateWithCredential(AuthCredential credential) {
throw UnimplementedError();
}

@override
Future<UserCredential> reauthenticateWithPopup(AuthProvider provider) {
throw UnimplementedError();
}

@override
Future<UserCredential> reauthenticateWithProvider(AuthProvider provider) {
throw UnimplementedError();
}

@override
Future<void> reauthenticateWithRedirect(AuthProvider provider) {
throw UnimplementedError();
}

@override
String? get refreshToken => throw UnimplementedError();

@override
Future<void> reload() {
throw UnimplementedError();
}

@override
Future<void> sendEmailVerification([ActionCodeSettings? actionCodeSettings]) {
throw UnimplementedError();
}

@override
String? get tenantId => throw UnimplementedError();

@override
String get uid => throw UnimplementedError();
Comment on lines +107 to +108
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement required uid property

The uid property is critical for user identification and should return a consistent demo value instead of throwing.

- String get uid => throw UnimplementedError();
+ String get uid => "demo-user-id";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@override
String get uid => throw UnimplementedError();
@override
String get uid => "demo-user-id";


@override
Future<User> unlink(String providerId) {
throw UnimplementedError();
}

@override
Future<void> updateDisplayName(String? displayName) {
throw UnimplementedError();
}

@override
Future<void> updateEmail(String newEmail) {
throw UnimplementedError();
}

@override
Future<void> updatePassword(String newPassword) {
throw UnimplementedError();
}

@override
Future<void> updatePhoneNumber(PhoneAuthCredential phoneCredential) {
throw UnimplementedError();
}

@override
Future<void> updatePhotoURL(String? photoURL) {
throw UnimplementedError();
}

@override
Future<void> updateProfile({String? displayName, String? photoURL}) {
throw UnimplementedError();
}

@override
Future<void> verifyBeforeUpdateEmail(String newEmail, [ActionCodeSettings? actionCodeSettings]) {
throw UnimplementedError();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import "package:cloud_firestore/cloud_firestore.dart";

// ignore: subtype_of_sealed_class
class NoOpDocumentReference implements DocumentReference<Map<String, dynamic>> {
const NoOpDocumentReference();

@override
CollectionReference<Map<String, dynamic>> collection(String collectionPath) {
// TODO: implement collection
KarinBerg marked this conversation as resolved.
Show resolved Hide resolved
throw UnimplementedError();
}

@override
Future<void> delete() {
// TODO: implement delete
throw UnimplementedError();
}

@override
// TODO: implement firestore
FirebaseFirestore get firestore => throw UnimplementedError();

@override
Future<DocumentSnapshot<Map<String, dynamic>>> get([GetOptions? options]) {
// TODO: implement get
throw UnimplementedError();
}

@override
// TODO: implement id
String get id => throw UnimplementedError();

@override
// TODO: implement parent
CollectionReference<Map<String, dynamic>> get parent => throw UnimplementedError();

@override
// TODO: implement path
String get path => throw UnimplementedError();

@override
Future<void> set(Map<String, dynamic> data, [SetOptions? options]) {
// TODO: implement set
throw UnimplementedError();
}

@override
Stream<DocumentSnapshot<Map<String, dynamic>>> snapshots(
{bool includeMetadataChanges = false, ListenSource source = ListenSource.defaultSource,}) {
// TODO: implement snapshots
throw UnimplementedError();
}

@override
Future<void> update(Map<Object, Object?> data) {
// TODO: implement update
throw UnimplementedError();
}

@override
DocumentReference<R> withConverter<R>(
{required FromFirestore<R> fromFirestore, required ToFirestore<R> toFirestore,}) {
// TODO: implement withConverter
throw UnimplementedError();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "package:app/data/datasource/organization_data_source.dart";
import "package:app/data/models/models.dart";
import "package:cloud_firestore/cloud_firestore.dart";

class OrganizationDemoDataSource implements OrganizationDataSource {
final Organization _organization = _generateOrganization();

static Organization _generateOrganization() {
return const Organization(name: "Demo organization", contactName: "Demo manager", contactNumber: "+232 123456789");
}

@override
Future<Organization?> fetchOrganization(DocumentReference<Object?> organizationRef) async {
return _organization;
}
Comment on lines +13 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error simulation capabilities

The demo implementation should simulate real-world scenarios, including error cases.

   @override
   Future<Organization?> fetchOrganization(DocumentReference<Object?> organizationRef) async {
+    // Simulate network delay
+    await Future.delayed(const Duration(milliseconds: 500));
+    
+    // Simulate errors for specific refs (optional)
+    if (organizationRef.id == 'error_case') {
+      throw FirebaseException(plugin: 'demo', message: 'Simulated error');
+    }
+    
     return _organization;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Future<Organization?> fetchOrganization(DocumentReference<Object?> organizationRef) async {
return _organization;
}
Future<Organization?> fetchOrganization(DocumentReference<Object?> organizationRef) async {
// Simulate network delay
await Future.delayed(const Duration(milliseconds: 500));
// Simulate errors for specific refs (optional)
if (organizationRef.id == 'error_case') {
throw FirebaseException(plugin: 'demo', message: 'Simulated error');
}
return _organization;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import "dart:math";

import "package:app/data/datasource/payment_data_source.dart";
import "package:app/data/models/models.dart";
import "package:cloud_firestore/cloud_firestore.dart";

const String paymentCollection = "payments";

class PaymentDemoDataSource implements PaymentDataSource {
List<SocialIncomePayment> payments = initData();

static List<SocialIncomePayment> initData() {
final List<SocialIncomePayment> payments = <SocialIncomePayment>[];

final nowDate = DateTime.now();
final random = Random();

final confirmedPaymentsCount = random.nextInt(12) + 1;
final notConfirmedPaymentsCount = random.nextInt(2) + 1;

for (int i = 0; i < confirmedPaymentsCount; i++) {
final currentDateTime = DateTime(
nowDate.year,
nowDate.month - confirmedPaymentsCount - notConfirmedPaymentsCount + i,
15,
);
payments.add(
SocialIncomePayment(
id: "${currentDateTime.year}-${currentDateTime.month}",
paymentAt: Timestamp.fromDate(currentDateTime),
currency: "SLE",
amount: 700,
status: PaymentStatus.confirmed,
),
);
}

for (int i = 0; i < notConfirmedPaymentsCount; i++) {
final currentDateTime = DateTime(
nowDate.year,
nowDate.month - notConfirmedPaymentsCount + i,
15,
);
payments.add(
SocialIncomePayment(
id: "${currentDateTime.year}-${currentDateTime.month}",
paymentAt: Timestamp.fromDate(currentDateTime),
currency: "SLE",
amount: 700,
status: PaymentStatus.paid,
),
);
}

payments.sort((a, b) => a.id.compareTo(b.id));

return payments;
}

@override
Future<List<SocialIncomePayment>> fetchPayments({
required String recipientId,
}) async {
return payments;
}

/// This updates the payment status to confirmed
/// and also sets lastUpdatedAt and lastUpdatedBy to the
/// current time and recipient
@override
Future<void> confirmPayment({
required Recipient recipient,
required SocialIncomePayment payment,
}) async {
final updatedPayment = payment.copyWith(
status: PaymentStatus.confirmed,
updatedBy: recipient.userId,
);

final indexWhere = payments.indexWhere((element) => element.id == updatedPayment.id);
payments[indexWhere] = updatedPayment;
Comment on lines +80 to +81
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds checking for payment updates

The array access could throw if payment ID is not found. Add error handling:

   final indexWhere = payments.indexWhere((element) => element.id == updatedPayment.id);
+  if (indexWhere == -1) {
+    throw Exception('Payment not found: ${updatedPayment.id}');
+  }
   payments[indexWhere] = updatedPayment;

Also applies to: 96-97

}

@override
Future<void> contestPayment({
required Recipient recipient,
required SocialIncomePayment payment,
required String contestReason,
}) async {
final updatedPayment = payment.copyWith(
status: PaymentStatus.contested,
comments: contestReason,
updatedBy: recipient.userId,
);

final indexWhere = payments.indexWhere((element) => element.id == updatedPayment.id);
payments[indexWhere] = updatedPayment;
}
}
Loading
Loading