How do I add a single instance of an item from a List to a Cart List that show the quantity of that particular item using riverpod? - flutter-provider

This image show List of Added items in the CartList, pressing the "--" or "+" does not reflect on the UI This second image shows the quantity of the same Item after I manually rebuild the UI**Say I have two Lists: **
List of Products from API List A [];
Empty CartList where products from List A above can be added to CartList once and increment the quantity if it has already existed in the CartList;
Example
1: List of items from the API [productA, productB, productC....];
2. Add an element from a list in 1 above if it does not exist in CartList before:- CartList = [productA];
3. If it already exist in CartList increase the quantity only and make it Single instance in the CartList like this: CartList = [productA(1),productB(5)...];
This code works fine but it doesn't update the UI except if I leave the page and open it again which not what I want.
This is the code below:
//This Code add the product to the CartList
Consumer(
builder: (BuildContext context, WidgetRef ref, Widget? child) {
return DefaultButton(
onTap: () {
ref.read(cartProvider.notifier).addToCart(product);
context.pushRoute(
const MyCartScreen(),
);
},
text: "Add to cart",
);
},
),
//This increase the count quantity of the product in the CartList
AddRemoveWidget(
count: "$productCount",
productDecrement: () {
ref
.read(cartProvider.notifier)
.removeFromCart(index);
},
productIncrement: () {
ref
.read(cartProvider.notifier)
.addToCart(
cartItems[index].product!);
},
),
//This the StateNotifier
class CartList extends StateNotifier<List<CartItemModel>> {
CartList() : super([]);
//Add Product to Cart
void addToCart(Product product) {
// check it product is int the list
final isInList = state
.where((element) => element.product!.sId == product.sId).isNotEmpty;
if (isInList) {
final p =
state.where((element) => element.product!.sId == product.sId).first;
p.quantity += 1;
// final count = state.where((element) => element.product!.sId == product.sId).length;
// final p = state.where((element) => element.product!.sId == product.sId).first;
// p.quantity = count;
final index = state.indexOf(p);
final items = state;
items[index] = p;
state = items;
} else {
state = [...state, CartItemModel(product: product)];
}
}
void removeFromCart(int index) {
// check it product is int the list
final item = state[index];
if (item.quantity == 1) {
// remove it from the list of cart items
state = state
.where((element) => element.product!.sId != item.product!.sId)
.toList();
} else {
item.quantity -= 1;
state[index] = item;
}
}
}
final cartProvider = StateNotifierProvider<CartList, List<CartItemModel>>(
(ref) => CartList(),
);

Related

Get the guid of the elements grouped in an ifcgroup

Is there a function in the API of IFCJS to get the guid of the elements grouped in an ifcgroup?
for example, if I group a column with a wall
getElementsFromIfcGroup(guidGroup) ---> return [guidWall, guidColumn]
According to the IFC schema, IfcGroup instances group elements together using an indirect relationship object called IfcRelAssignsToGroup. This means that you can retrieve the elements contained within that group like this:
import { IFCRELASSIGNSTOGROUP as REL } from 'web-ifc';
async function getItemsOfGroup(modelID, groupID) {
const manager = ifcLoader.ifcManager;
const relIDs = await manager.getAllItemsOfType(modelID, REL);
for(relID of groupsIDs) {
const groupRel = await manager.getItemProperties(modelID, relID);
if(groupRel.RelatingGroup.value === groupID) {
return groupRel.RelatedObjects;
}
}
return [];
}
based on Antonio's answer, it looks like this:
async function getItemsOfGroup(modelID, groupID) {
const manager = viewer.IFC.loader.ifcManager
// Get all ifcgroups
const relIDs = await manager.getAllItemsOfType(modelID, IFCRELASSIGNSTOGROUP);
let relID, relObj, props;
var guIDs = [];
for(relID of relIDs) {
const groupRel = await manager.getItemProperties(modelID, relID);
// Find the groupID
if(groupRel.GlobalId.value === groupID) {
// Search all related objects
for(relObj of groupRel.RelatedObjects) {
//get object properties
props = await manager.getItemProperties(modelID, relObj.value);
//Add guid to array
guIDs[guIDs.length] = props.GlobalId.value;
}
return guIDs;
}
}
return guIDs;
}

Would like to update the firestore database of certain document based on an if statement

I'm trying to update certain documents 'avail' field to true only if the toolCount method I applied is more than 1.
Each document is a different tool
I am counting their quantity based on their toolName meaning if I have 2 Screens in toolName field then ++
Img of firebase:
Code:
#override
Future countTools() async {
try {
FirebaseFirestore.instance.collection('Tools').get().then((value) {
var toolNamesCounted = Map();
List toolNames = [];
value.docs.forEach((data) {
var doc = data.data();
toolNames.add(doc['toolName']);
});
toolNames.forEach((e) {
if (!toolNamesCounted.containsKey(e)) {
toolNamesCounted[e] = 1;
} else {
toolNamesCounted[e] += 1;
}
});
});
} catch (e) {
print(e);
}
}
You need to collect doc ids with the same field name and best way to update multiple docs is using batched writes
FirebaseFirestore.instance.collection('Tools').get().then((value) {
WriteBatch batch = FirebaseFirestore.instance.batch();
List<dynamic> toolNamesCounted = [];
List<dynamic> toolNames = [];
value.docs.asMap().forEach((i, data) {
var doc = data.data();
toolNames.add({
'name': doc['toolName'],
'id': value.docs[i].reference.id,
});
});
// collect ids if same toolName
toolNames.forEach((e) {
final match = toolNamesCounted.indexWhere((element) => element['name'] == e['name']);
// if found collect ids
if (match >= 0) {
toolNamesCounted[match]['ids'].add(e['id']);
} else {
// if not found add first record
toolNamesCounted.add({
'name': e['name'],
'ids': ['${e['id']}'],
});
}
});
toolNamesCounted.forEach((e) {
// if collected ids array more than 2 add batches
if (e['ids'].length >= 2) {
e['ids'].forEach((e) {
batch.update(FirebaseFirestore.instance.collection('Tools').doc(e), {'avail': true});
});
}
});
// update all the docs
batch.commit();
});

Adding all the numbers in the firebase and store it in a variable in Flutter

I have a collections with field of rating and i want to iterate through the collection and add all the values. Finally i want to store it in a variable that can be accessed by all screen in flutter.
Any idea?
Here what i have tried so but failed
void calculateHomeTeamRating(){
int rating=0;
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('SelectedPlayersHome').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final players = snapshot.data.docs;
List<int> homeRating = [];
for(var playerRating in players){
rating = rating + playerRating.get('Rating');
}
String textRating = rating.toString();
homeRating.add(rating);
return null;
},
);
}
}
Here is the collections and the fields that shows the player name and their rating:
You can create a model class to encode and decode incoming data from firestore and can use this model class to store data.
Create class to store player data.
class Player {
String playerName;
int rating;
String timestamp;
Player({this.playerName, this.rating, this.timestamp});
Player.fromJson(Map<String, dynamic> json) {
playerName = json['PlayerName'];
rating = json['Rating'];
timestamp = json['timestamp'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PlayerName'] = this.playerName;
data['Rating'] = this.rating;
data['timestamp'] = this.timestamp;
return data;
}
}
Pass QuerySnapshot to calculateHomeTeamRating function and it will returns the List<Player> and use it accordingly. .
Future<List<Player>> calculateHomeTeamRating(
QuerySnapshot querySnapshot) async {
final List<Player> playerList = [];
final data = querySnapshot.docs;
if (data != null && data.isNotEmpty) {
for (var i = 0; i < data.length; i++) {
var model = Player.fromJson(querySnapshot.docs[i].data());
playerList.add(model);
}
return playerList;
} else {
return null;
}
}
}
use specific value(rating) from list of player
var List<int> ratings;
final players = snapshot.data.docs;
final list = calculateHomeTeamRating(players);
if(list!= null && list.isNotEmpty){
ratings = list.map((player)=> player.rating).toList();
}
if(ratings!= null && ratings.isNotEmpty){
print("Total given ratings: ${ratings.length}");
}

Quickly finding users by phone number with Firebase backend

I’m working on an app with a Firebase backend. During sign up I would like to let new users see which of their contacts are already on the app to add them as friends. So basically, use phone numbers to match users with contacts.
I am having a big performance headache when querying the database to find users.
Since Firestore does not support OR queries, I run two queries per phone number (one to check national format, the other for international format), and if any returns a document, set that document as the found user:
findUserByPhoneNumber = (number, callback) => {
//utility function to, well, sanitize phone numbers
sanitizeNumber = (str) => {
if (str) {
var num = str.match(/\d/g);
num = num.join("");
return num;
} else {
return null
}
}
var foundUser = null
Promise.all([
usersRef.where('phoneNumbers.nationalFormat', '==', sanitizeNumber(number)).get()
.then(snapshot => {
if (snapshot.docs.length > 0 && snapshot.docs[0].data()) {
// console.log('nationalFormat result: ', snapshot.docs[0]);
foundUser = snapshot.docs[0].data()
}
return foundUser
}),
usersRef.where('phoneNumbers.internationalFormat', '==', sanitizeNumber(number)).get()
.then(snapshot => {
if (snapshot.docs.length > 0 && snapshot.docs[0].data()) {
// console.log('internationalFormat result: ', snapshot.docs[0]);
foundUser = snapshot.docs[0].data()
}
return foundUser
})
])
.then(results => {
res = results.filter(el => { return el != null })
if (results.length > 0) {
callback(res[0])
}
})
}
findUserByPhoneNumber runs for each contact in a loop. When testing on my phone with 205 contacts, the whole process takes about 30 seconds, which is about 29 seconds longer than I would like, especially given the test database has only 8 records...
getContacts = () => {
getCs = () => {
// Declare arrays
const contactsWithAccount = []
const contactsWithNoAccount = []
// Get contacts from user's phone
Contacts.getAll((err, contacts) => {
if (err) throw err
// For each contact, iterate
for (var i = 0; i < contacts.length; i++) {
const item = contacts[i]
if (item.phoneNumbers && item.phoneNumbers.length > 0) {
const phone = item.phoneNumbers[0].number
// If the sanitized phone number is different from the current user's phone number (saved in DB), run the following logic
if (this.state.user.phoneNumbers.nationalFormat != sanitizeNumber(phone)
&& this.state.user.phoneNumbers.internationalFormat != sanitizeNumber(phone)
) {
findUserByPhoneNumber(phone, (fu) => {
contactObject = {
key: item.recordID,
name: item.givenName,
normalizedName: item.givenName.toLowerCase(),
phoneNumber: phone,
user: this.state.user,
hasAccount: null,
friendId: null,
isFriend: null
}
const foundUser = fu
// if found user, push in contactsWithAccount, otherwise push in contactsWithNoAccount
if (foundUser && foundUser._id != this.state.user._id) {
contactObject.hasAccount = true
contactObject.friendId = foundUser._id
if (this.state.user.friends && this.state.user.friends.includes(foundUser._id)) {
contactObject.isFriend = true
}
contactsWithAccount.push(contactObject)
}
else {
contactsWithNoAccount.push(contactObject)
}
// if the two arrays are filled up, run the callback
// NOTE_1: we use the two lengths +1 to account for the current
// user's document that we skip and dont add to any of the arrays
// NOTE_2: this bizare method was the only way to handle the results
// coming in asynchronously
if (contactsWithAccount.length + contactsWithNoAccount.length + 1 == contacts.length) {
console.log('finished');
sortCs(contactsWithAccount, contactsWithNoAccount)
}
})
}
}
}
})
}
// sorts the two arrays alphabetically
sortCs = (withAccount, withNoAccount) => {
compare = (a,b) => {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
}
withAccount.sort(compare)
withNoAccount.sort(compare)
this.setState({ withAccount, withNoAccount })
}
// unleash the monster
getCs(sortCs)
}
I am sure the process could be optimized in various ways. Maybe:
different database structure
bundling all queries into one
better use
of async
starting the process at an earlier step in the signup flow
Whatsapp, HouseParty and a bunch of other apps have this feature in place and it loads instantly. I’m not trying to reach that level of perfection yet but there must be some better way…
Any help/suggestions would be greatly appreciated.

Firebase deleted data still there

i have a behavior that I can't understand.
I delete a node on firebase database and I still receive the data during observing .value. But in firebase database the node is deleted.
I have a node called users_shoppinglists. Here are all id's of the users nodes to observe stored. Then I iterate all the id's to observe and call a function that observes each ID.
When I need to delete a list I update a node called status on the shoppinglists node and delete all to this list related data via cloud functions.
But the data is still received during observe. It seems I receive the data again before it is completely deleted.
Iterate all id's:
func ObserveAllList() -> Void{
if currentUser == nil { return }
self.ShowActivityIndicator()
ref.child("users_shoppinglists").child(currentUser!.id!).observe(.value, with: { (usersListsSnap) in
if usersListsSnap.value is NSNull { self.HideActivityIndicator(); return }
for listSnap in usersListsSnap.children {
let list = listSnap as! DataSnapshot
self.ObserveSingleList(listID: list.key)
}
}) { (error) in
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
}
}
Call function to observe each ID:
func ObserveSingleList(listID:String) -> Void {
self.ShowActivityIndicator()
ref.child("shoppinglists").child(listID).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value is NSNull { self.HideActivityIndicator(); return }
//Read listData
var newList = ShoppingList()
newList.id = snapshot.key
newList.name = snapshot.childSnapshot(forPath: "listName").value as? String
newList.owneruid = snapshot.childSnapshot(forPath: "owneruid").value as? String
newList.relatedStore = snapshot.childSnapshot(forPath: "relatedStore").value as? String
//Read List items
self.ref.child("listItems").child(listID).observe(.value, with: { (itemSnap) in
var newItems = [ShoppingListItem]()
for items in itemSnap.children {
let item = items as! DataSnapshot
var newItem = ShoppingListItem()
newItem.id = item.key
newItem.listID = listID
newItem.isSelected = item.childSnapshot(forPath: "isSelected").value as? Bool
newItem.itemName = item.childSnapshot(forPath: "itemName").value as? String
newItem.sortNumber = item.childSnapshot(forPath: "sortNumber").value as? Int
newItems.append(newItem)
}
newList.items = newItems
//Read List members
self.ref.child("shoppinglist_member").child(listID).observe(.value, with: { (memberSnap) in
var newMembers = [ShoppingListMember]()
for members in memberSnap.children {
let member = members as! DataSnapshot
var m = ShoppingListMember()
m.memberID = member.key
m.status = member.value as? String
newMembers.append(m)
}
newList.members = newMembers
DispatchQueue.main.async {
if let index = allShoppingLists.index(where: { $0.id == listID }){
allShoppingLists[index] = newList
} else {
allShoppingLists.append(newList)
}
self.HideActivityIndicator()
NotificationCenter.default.post(name: Notification.Name.ShoppingBuddyListDataReceived, object: nil, userInfo: nil)
}
}, withCancel: { (error) in
self.HideActivityIndicator()
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
})
}, withCancel: { (error) in
self.HideActivityIndicator()
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
})
}) { (error) in
self.HideActivityIndicator()
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
}
}
Cloud function:
//****************************************************************************************************************/
// Handles an action when status value changed in users_shoppinglists node
//****************************************************************************************************************/
exports.handle_ListStatusUpdate = functions.database.ref('/shoppinglists/{listID}').onUpdate(event => {
var listData = event.data.val()
console.log('Status', listData.status)
//handle deleted by owner
if (String(listData.status) == 'deleted by owner') {
//Get all members to delete the list on their users_shoppinglists node
return admin.database().ref('shoppinglist_member').child(event.params.listID).once('value').then(listMember => {
var promises = []
listMember.forEach(function (member) {
promises.push(admin.database().ref('users_shoppinglists').child(member.key).child(event.params.listID).set(null).then(() => {
return admin.database().ref('shoppinglist_member').child(event.params.listID).set(null).then(() => {
// delete the original shopping list
return admin.database().ref('shoppinglists').child(event.params.listID).set(null).then(() => {
return admin.database().ref('listItems').child(event.params.listID).set(null).then(() => {
})
})
})
}))
})
})
}
});/*********************************************************************************************************** */
Had this issue on Simulator. It was not only .value but .childRemoved and .childChanged were not triggered at all (only .childAdded was working).
Tried on iPhone and it worked. Then I made "Erase All Content And Settings..." to Simulator and it started to work again on Simulator too.
My bet is that firebase cache gets dirty during development, while you add or remove observers in code and probably change structure in database and at some point it just stops reacting appropriately.

Resources