The code I wrote with rxDart is not readable - flutter

I'm listening to 2 collections on firebase using RxDart. The data is being pulled, but the merge with RxDart does not go into my code. So that part passes. What could be the problem??
User and Conversation collections are listening. I can reach this data and ı can write on the console. But Rx Code passes.
#override
Stream<List<Chat?>> getAllConversation(String userId) async* {
Stream<List<Conversation?>> conversationStream = _firestore
.collection(_conversationCollectionName)
.where(
Conversation.memebersKey,
arrayContains: userId,
)
.orderBy(
Conversation.sentAtKey,
)
.snapshots()
.map(
(event) => event.docs
.map((e) => _getConversationFromDocumentSnapshot(e, e.id))
.toList(),
);
Stream<List<User?>> userStream =
_firestore.collection(_usersCollectionName).snapshots().map(
(event) => event.docs
.map((e) => _getUserFromDocumentSnapshot(
e,
))
.toList(),
);
yield* Rx.combineLatest2(
conversationStream,
userStream,
(List<Conversation?> conversations, List<User?> users) {
print('Rx içinde giriyor');
return conversations.map((conversation) {
if (conversation != null) {
var otherUser = conversation.memebers[0] == userId
? conversation.memebers[1]
: conversation.memebers[0];
bool isOwnMessage =
conversation.lastUserId == userId ? true : false;
User? user = users.firstWhere((user) {
return user!.userId == otherUser;
});
return Chat(
id: conversation.conversationId,
user: user,
message: conversation.lastMessages,
unReadMessageCount: isOwnMessage ? 0 : conversation.unRead,
isOwnMessage: isOwnMessage,
sentAt: conversation.sentAt,
senderId: userId,
);
}
}).toList();
},
);
}

Related

Flutter Algolia search multiple indices

Inside my Algolia application I have to indices which both contain users. Both of them must have a uid and a username (along other fields). I managed to implement the search for on of the indices. But how can I search in both indices?
I followed this tutorial but it does not say anything about searching multiple indices.
I found this about searching multiple indices but how can I do that in Flutter in combination with `Hits Searcher and infinite_scrolling_pagination.
This is my setup:
Stream<SearchMetadata> get _searchUsersMetadata =>
_userSearcher.responses.map(
SearchMetadata.fromResponse,
);
final PagingController<int, AlgoliaUser> _pagingController =
PagingController(firstPageKey: 0);
#override
void initState() {
super.initState();
_searchPage.listen((page) {
if (page.pageKey == 0) {
_pagingController.refresh();
}
_pagingController.appendPage(page.items, page.nextPageKey);
}).onError((error) => _pagingController.error = error);
_pagingController.addPageRequestListener(
(pageKey) => _userSearcher.applyState(
(state) => state.copyWith(page: pageKey),
),
);
}
final _userSearcher = HitsSearcher(
applicationID: 'myAppID',
apiKey: 'myApiKey',
indexName: 'users',
);
Stream<HitsPage> get _searchPage => _userSearcher.responses.map(
HitsPage.fromResponse,
);
_onSearchChanged(String query) {
if (_debounce?.isActive ?? false) {
_debounce?.cancel();
}
_debounce = Timer(const Duration(milliseconds: 500), () {
_userSearcher.applyState(
(state) => state.copyWith(
query: _searchTextEditingController.text,
page: 0,
),
);
});
}

Flutter : button won't invoke function without a second click

I have an elevated button which is used for sign in, and it won't do anything till I press the button again, I don't know what Iam doing wrong! , I have never encountered such an issue! , I want to know what the problem is and what caused it .
here is the button code :
code :
onPressed: () async {
print("pressed");
if (formKey.value.currentState!.validate()) {
ref
.read(
signInWithEmailAndPasswordUseCaseProvider)
.execute(
SignInWithEmailAndPasswordUseCaseInput(
email: patient.value.email,
password: password.text))
.then((value) => value.fold((l) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
duration: const Duration(
seconds: 3),
content: Text(l
.toString() ==
"ApiFailures.authFailed()"
? "Please check your email and password again"
: l.toString() ==
"ApiFailures.noConnection()"
? "Please check your internet connection"
: "pleaase contact us")));
}, (r) {
if (r.account_type == "Doctors") {
context.router.replaceAll([
const DoctorPanelScreen()
]);
} else {
token1.value = r.api_token!;
data.when(
data: (data) {
data.fold(
(l) => print("helo1"),
(r) async {
print(r);
docid.value = r.id!;
patient.value = patient
.value
.copyWith(
name: r.name,
aliid: r.id,
appointments: r
.patient_appointments,
fcmtoken:
token);
print(patient.value);
doc.maybeWhen(
orElse: () => print(
"getpatienterror"),
data: (data) {
print(data);
if (data
.isEmpty) {
ref
.read(
firebasesignUpWithEmailAndPasswordUseCaseProvider)
.execute(
FirebaseSignUpWithEmailAndPasswordUseCaseInput(
user: patient
.value,
password:
password
.text,
))
.then((value) => value.fold(
(l) => print(l
.toString()),
(r) => print(
"done")
// context.router.replaceAll([
// const HomeScreen()
// ])
));
} else {
print(
"firebased");
context.router
.replaceAll([
const HomeScreen()
]);
}
});
});
},
error: (error, _) {},
loading: () {
});
}
}));
}
}
it prints pressed in the first time, and that's weird, any suggestions ?

Filtering on Flutter Dart Firestore

How do i apply the filter Bloc output of:
var genders = state.filter.genderFilters
.where((filter) => filter.value)
.map((filter) => filter.gender.gender)
.toList();
To this search repository:
Future<User> getFilteredUser(userId) async {
User _user = User();
List<String> chosenList = await getChosenList(userId);
User currentUser = await getUserInterests(userId);
await _firestore.collection('users').getDocuments().then((users) {
for (var user in users.documents) {
if ((!chosenList.contains(user.documentID)) &&
(user.documentID != userId) &&
(user['gender'] == ) //???????
)
{
_user.uid = user.documentID;
_user.name = user['name'];
_user.photo = user['photoUrl'];
_user.age = user['age'];
_user.location = user['location'];
_user.gender = user['gender'];
break;
}
}
});
return _user;
}
I am trying to do somthing like this but on the firestore query:
List<User> filteredusers = User.filteredUsers
.where(
(user) => genders.any(
(gender) =>
user.genderCategory.contains(gender),
),
)
.toList();
Thanks in advance

Flutter unit testing streams that do map

I did stuck with testing streams that do some transformations before returning the value. I am using Firestore as my data storage and have a separated data layer that performs some mapping from DocumentSnapshot<T> to my model. Here is an example of the repo:
class RequestsRepository {
final CollectionReference<Request> _requestsCollection =
getIt<FirebaseFirestore>().collection('requests').withConverter<Request>(
fromFirestore: (snapshot, _) => Request.fromJson(snapshot.data()!),
toFirestore: (request, _) => request.toJson());
Stream<Request?> observe(String id) {
return _requestsCollection
.doc(id)
.snapshots()
.map((snapshot) => snapshot.exists ? snapshot.data() : null);
});
}
}
And now I'd like to cover observe(id) function with tests. Here is the solution I came to and below I'll explain why:
test('request exists', () async {
// WHEN
final stream = repository.observe(request.id);
// expected events
final events = [null, request, request..completed = true, null];
var eventIndex = 0;
// THEN
stream.listen(expectAsync1((value) async {
await Future.delayed(Duration(milliseconds: 100));
expect(value, events[eventIndex]);
eventIndex++;
}, max: -1));
// GIVEN
await firestore
.collection('requests')
.doc(request.id)
.set(request.toJson());
request.completed = true;
await firestore
.collection('requests')
.doc(request.id)
.set(request.toJson());
// reset request data
request.completed = false;
});
I tried emitsInOrder stream matcher however it fails, because I'm doing mapping inside the observe(id) function and that result in actually not request object but _MapStream<DocumentSnapshot<Request?>, Request?> instead because of the logic that stream map function follows:
Stream<S> map<S>(S convert(T event)) {
return new _MapStream<T, S>(this, convert);
}
The solution I came to did work for this case but it did not for other tests and also the solution is smelly. Any suggestions how to tests these kind of streams?
UPDATE:
Per comment from #pskink here is the demo test that fails with the same reason:
class Test {
Stream<B> observe() {
return Stream.periodic(Duration(milliseconds: 500), (i) => A(i * 10))
.map((i) => B('s${i.a}'));
}
}
class A {
final int a;
A(this.a);
bool operator ==(o) => o is A && a == o.a;
int get hashCode => a.hashCode;
}
class B {
final String b;
B(this.b);
bool operator ==(o) => o is B && b == o.b;
int get hashCode => b.hashCode;
}
void main() {
test('mapped stream: A > B', () async {
final stream = Test().observe();
expect(stream, emitsInOrder([B('s0'), B('s10'), B('s20'), B('s30')]));
});
}
It passes with the test so far.
Thanks to #pskink so far to identifying that the issue is not with stream conversion (even though logs look pretty unreadable). I had to make my Request object Equatable so it can now compare all fields appropriately. And I had to remove the await from firestore calls so that events occur really asynchronously.
Here is an example of working test:
test('request exists', () async {
// GIVEN
final timestamp = DateTime.now();
final request = Request(
'49a257e1-c0e3-4cc1-9053-aaf55197f897',
user.id,
"${user.firstName} ${user.lastName}",
user.phone ?? "",
'center',
'comment',
timestamp,
false,
false);
await firestore
.collection('requests')
.doc(request.id)
.set(request.toJson());
// WHEN
final stream = repository.observe('id', request.id);
// THEN
expect(
stream,
emitsInOrder([
Request(
'49a257e1-c0e3-4cc1-9053-aaf55197f897',
user.id,
"${user.firstName} ${user.lastName}",
user.phone ?? "",
'center',
'comment',
timestamp,
false,
false),
Request(
'49a257e1-c0e3-4cc1-9053-aaf55197f897',
user.id,
"${user.firstName} ${user.lastName}",
user.phone ?? "",
'center',
'comment',
timestamp,
false,
true),
]));
firestore
.collection('requests')
.doc(request.id)
.set({"completed": true}, SetOptions(merge: true));
});

Sorting Items By User Generated Map Flutter

When a user signs up for my app, they enter their preferences and the items are sorted by a property in their profile called "recommended" which comes from the api: "[Breakfast, Dinner, Desserts, Dine In]"
I have a tab bar where the user can tab through main preferences ie: [All options, food type, beverages] and when the user gets to each preference they get a subset that allows them to sort further that comes from Tabcategories. These are the working examples and the one that doesn't work:
Data comes in via my future:
var productsInHeadings =
_getProductsInHeadings(filteredProducts)
.where((e) => e.products.length != 0)
.toList();
Then gets filtered into buckets within the tabs.
enum FilterTabs { Breakfast,Rec, Lunch, Dinner, Dessert, All }
typedef void FilterTabCallback(FilterTabs tab);
List<ProductsInHeading> _getProductsInHeadings(List<Product> items) {
switch (selectedTab) {
case FilterTabs.Breakfast:
final Map<String, List<Product>> allBreakfast =
Map.fromEntries(TabCategories.breakfast.map((e) => MapEntry(e, [])));
Map<String, List<Product>> headingItems =
items.fold(allBreakfast, (breakfast, element) {
if (!breakfast.containsKey(element.food)) { //food would be classified breakfast, lunch, or dinner
return breakfast;
}
return breakfast
..update(element.food, (value) => value..add(element));
});
productList = headingItems;
print("headingItems: $headingItems");
return headingItems.entries
.map((e) => ProductsInHeading(e.key, e.value..sort()))
.toList()
..sort()
..where((e) => e.products.length != 0);
break;
}}
For each tab in the bucket, then displayed in a separate class
itemBuilder: (context, index) {
currentProducts = snapshot.data;
return HeaderAndListing(
productsInHeading: _currentCat != -1
? productsInHeadings[_currentCat]
: productsInHeadings[index],
showHeading: selectedTab != FilterTabs.All,
restaurant: restaurant,
starCount: starCount,
rating: rating,
filterNum: _currentCat,
currentIndex: _tabDetails[selectedTab.index]);
},
),
Center(child: _subSort(productsInHeadings)),
]);
This one does not:
case FilterTabs.Rec:
final Map<String, List<Product>> allRecommended =
Map.fromEntries(recCategories.map((e) => MapEntry(e, [])));
Map<String, List> headingItems =
items.fold(allRecommended, (rec, element) {
if (!rec.containsKey(userProfile.recommended)) {
return rec;
}
return rec..update(userProfile.recommended, (value) => value..add(element));
});
productList = headingItems;
print("headingItems: $headingItems");
return headingItems.entries
.map((e) => ProductsInHeading(e.key, e.value..sort()))
.toList()
..sort()
..where((e) => e.products.length != 0);
break;
Why does the first sort work but, the second one does not?