Can redux be used in application that uses Flutter Cloud Firestore? - redux

I am currently doing some experiments with Flutter Redux architecture, I notice that the state of the store changes in a uni direction.
However, in my case, I'm using Cloud firestore, and I am required to listen to the changes to the database on the cloud, then updating the data on UI accordingly. The code below would be in a repository, but I think it's not right to call store.dispatch(updateUIAction) in the repository layer. Is there a way to work around this?
Thanks.
class HikingPlacesRepository {
const HikingPlacesRepository();
List<HikingPlaceEntity> loadHikingPlaces() {
List<HikingPlaceEntity> places = [];
FireStore.instance.collection('hiking_places').snapshots.listen((snapshot) {
print("snapshot ${snapshot.documents[0]['placename']}");
if (snapshot != null) {
places = snapshot.documents.map((doc) => new HikingPlaceEntity(doc['placename'])).toList();
print("places in repo $places");
}
});
return places;
}
}
EDIT
After reading this example, it works when using firestore in the middleware layer, which auto update the ViewModels when store changes.

Related

Firebase Firestore Data Not Visible Issue

I ran into issue where Firestore is not reflecting data on client.
Lets say when I create cart manually from Firebase Console it reflects on client side but when I create Cart from client side it does not reflects, although a empty card appears but its null. Assist me on this
Firestore Rules are Public
Data Calling Method
public async Task<ObservableCollection<T>> GetCollection(string collection)
{
var tcs = new TaskCompletionSource<ObservableCollection<T>>();
await DataStore.Collection(collection).Get()
.AddOnCompleteListener(new OnCollectionCompleteListener<T>(tcs));
return await tcs.Task;
}
Thanks
I resolved this issue on my own. While working with Firestore, I understood that if you keep any field null in Firestore, the data inside the document will not be visible. So make sure to not leave any field empty.

Been trying to set Custom time on a file using Firebase?

I'm trying to set the Custom time attribute in firebase on the front end. Everything is possible to set, like contentDisposition, custom Metadata etc, just can't find any way or any info about setting Custom time.
You can see it referenced here https://cloud.google.com/storage/docs/metadata#custom-time
You can set the custom time on the file manually in the Storage cloud console, but even when you do and you load the file in firebase on the front end, it's missing from the returned object! (makes me feel like it's not possible to achieve this)
var storage = this.$firebase.app().storage("gs://my-files");
var storage2 = storage.ref().child(this.file);
//// Tried this
var md = {
customTime: now.$firebase.firestore.FieldValue.serverTimestamp()
};
//// & Tried this
var md = {
Custom-Time: now.$firebase.firestore.FieldValue.serverTimestamp()
};
storage2.updateMetadata(md).then((metadata) => {
console.log(metadata);
}).catch((err) => {
console.log(err);
});
The reason I ask is I'm trying to push back the lifecycle delete date (which will be based on the custom time) every time the file is loaded. Does anyone know the answer or an alternative way of doing it?
Thanks in advance
The CustomTime metadata is not possible to update using Firebase JavaScript SDK since it is not included in the file metadata properties list mentioned in the documentation. So even if you specify it as customTime: or Custom-Time: the updateMetadata() method does not perform any changes.
I suggest you as a better practice, set the CustomTime metadata from the cloud console and modify the CustomTimeBefore Lifecycle condition from the back-end each time you load the file using the addLifeCycleRule method of the GCP Node.js Client.
// Imports the Google Cloud client library
const {Storage} = require('#google-cloud/storage');
// Creates a client
const storage = new Storage();
//Imports your Google Cloud Storage bucket
const myBucket = storage.bucket('my_bucket');
//-
// Delete object that has a customTime before 2021-05-25.
//-
myBucket.addLifecycleRule({
action: 'delete',
condition: {
customTimeBefore: new Date('2021-05-25')
}
}, function(err, apiResponse) {});

Stream/Bloc/Repository/Firebase data flow Flutter

I'm starting with flutter as I want to port my swift app to Flutter, but I'm getting stuck understanding the pattern Bloc/Repository/Firebase as I'm following the tutorial https://bloclibrary.dev/#/flutterfirestoretodostutorial dough I use the real time database, not Firestore.
My swift app is basically a map where you can add Alerts at your actual coordinates. The Alert get sent to Firebase and the firebase observer on the map updates the map showing the just added alert.
The above tutorial should help me porting my app. I'm just not sure I do understand the logic behind the code.
My concerns are 2:
First. There is an Entity layer between the model object and the firebase object. It is explained that this will facilitate having different Data providers, but I don't really see it facilitating anything. In the Model class there is a toEntity() and a fromEntity() conversion method, and in the Entity class there is a fromSnapshot() and a toDocument() conversion method. I don't see what's the point here. Is it really necessary? What's wrong with doing the conversion directly in the Model class , having different methods for each Data provider?
Second. Inside the TodoBloc I can't follow the logic.
The first event that is sent to the bloc at AppStart is LoadTodos.
BlocProvider<TodosBloc>(
create: (context) {
return TodosBloc(
todosRepository: FirebaseTodosRepository(),
)..add(LoadTodos());
In the mapEventToState() method of TodoBloc that event gets mapped to this Stream:
Stream<TodosState> _mapLoadTodosToState() async* {
_todosSubscription?.cancel();
_todosSubscription = _todosRepository.todos().listen(
(todos) => add(TodosUpdated(todos)),
);
}
So far so good. As I understand this subscribes to the todos() Stream ()
#override
Stream<List<Todo>> todos() {
return todoCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
.toList();
});
}
and this should be the equivalent of the firebase observer in my swift app. It this part inside the listen closure I'm not sure to understand: (todos) => add(TodosUpdated(todos)) .
This sends to itself (TodoBloc) a TodosUpdated event on which the bloc will map this Stream:
Stream<TodosState> _mapTodosUpdatedToState(TodosUpdated event) async* {
yield TodosLoaded(event.todos);
}
which is this:
class TodosLoaded extends TodosState {
final List<Todo> todos;
const TodosLoaded([this.todos = const []]);
#override
List<Object> get props => [todos];
#override
String toString() => 'TodosLoaded { todos: $todos }';
}
Is this the actual list of Firebase objects? Does the todos() Stream return the entire node every time a new object is added in Firebase?
In my swift app the observer returns only the .childAdded after the first download of the node.
Should I use the firebase_database package that has a FirebaseList class(https://pub.dev/documentation/firebase_database/latest/ui_firebase_list/FirebaseList-class.html) that will just return a List on any change on the node as my observers do in my swift app?
Sorry for this very long and messy question, but I'm quite lost here starting with bloc pattern.
Thank you very much for your time and help.
Ok, I I think I understood the logic behind it, but if you see that I didn't get it right please correct me as at this stage of getting into a new paradigm is very important not to carry any misconceptions.
todos() is the Stream coming from Firebase and returns a List<Todo>.
_mapLoadTodosToState() is the bloc method that attach a bloc listener to todos() and in the .listen(onData) callback, it sends to the bloc an TodosUpdated(todos) event containing the latest list.
TodosUpdated(todos) gets mapped to _mapTodosUpdatedToState, which yields
TodosLoaded(event.todos) , the new state that BlocProvider uses to build the UI.
Thank you and I hope this will help others struggling to master BloC pattern at a more complex level.
Cheers

minimize time operation in firebase/firestore

I build react native app with firebase & firestore.
what I'm looking to do is, when user open app, to insert/update his status to 'online' (kind of presence system), when user close app, his status 'offline'.
I did it with firebase.database.onDisconnect(), it works fine.
this is the function
async signupAnonymous() {
const user = await firebase.auth().signInAnonymouslyAndRetrieveData();
this.uid = firebase.auth().currentUser.uid
this.userStatusDatabaseRef = firebase.database().ref(`UserStatus/${this.uid}`);
this.userStatusFirestoreRef = firebase.firestore().doc(`UserStatus/${this.uid}`);
firebase.database().ref('.info/connected').on('value', async connected => {
if (connected.val() === false) {
// this.userStatusFirestoreRef.set({ state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp()},{merge:true});
return;
}
await firebase.database().ref(`UserStatus/${this.uid}`).onDisconnect().set({ state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp() },{merge:true});
this.userStatusDatabaseRef.set({ state: 'online', last_changed: firebase.firestore.FieldValue.serverTimestamp() },{merge:true});
// this.userStatusFirestoreRef.set({ state: 'online',last_changed: firebase.firestore.FieldValue.serverTimestamp() },{merge:true});
});
}
after that, I did trigger to insert data into firestore(because I want to work with firestore), this is the function(works fine, BUT it takes 3-4 sec)
module.exports.onUserStatusChanged = functions.database
.ref('/UserStatus/{uid}').onUpdate((change,context) => {
const eventStatus = change.after.val();
const userStatusFirestoreRef = firestore.doc(`UserStatus/${context.params.uid}`);
return change.after.ref.once("value").then((statusSnapshot) => {
return statusSnapshot.val();
}).then((status) => {
console.log(status, eventStatus);
if (status.last_changed > eventStatus.last_changed) return status;
eventStatus.last_changed = new Date(eventStatus.last_changed);
//return userStatusFirestoreRef.set(eventStatus);
return userStatusFirestoreRef.set(eventStatus,{merge:true});
});
});
then after that, I want to calculate the online users in app, so I did trigger when write new data to node of firestore so it calculate the size of online users by query.(it works fine but takes 4-7 sec)
module.exports.countOnlineUsers = functions.firestore.document('/UserStatus/{uid}').onWrite((change,context) => {
console.log('userStatus')
const userOnlineCounterRef = firestore.doc('Counters/onlineUsersCounter');
const docRef = firestore.collection('UserStatus').where('state','==','online').get().then(e=>{
let count = e.size;
console.log('count',count)
return userOnlineCounterRef.update({count})
})
return Promise.resolve({success:'added'})
})
then into my react native app
I get the count of online users
this.unsubscribe = firebase.firestore().doc(`Counters/onlineUsersCounter`).onSnapshot(doc=>{
console.log('count',doc.data().count)
})
All the operations takes about 12 sec. it's too much for me, it's online app
my firebase structure
what I'm doing wrong? maybe there is unnecessary function or something?
My final goals:
minimize time operation.
get online users count (with listener-each
change, it will update in app)
update user status.
if there are other way to do that, I would love to know.
Cloud Functions go into a 'cold start' mode, where they take some time to boot up. This is the only reason I can think of that it would take that long. Stack Overflow: Firebase Cloud Functions Is Very Slow
But your cloud function only needs to write to Firestore on log out to
catch the case where your user closes the app. You can write to it directly on log in from your client
with auth().onAuthStateChange().
You could also just always read who is logged in or out directly from the
realtime database and use Firestore for the rest of your data.
You can rearrange your data so that instead of a 'UserStatus' collection you have an 'OnlineUsers' collection containing only online users, kept in sync by deleting the documents on log out. Then it won't take a query operation to get them. The query's impact on your performance is likely minimal, but this would perform better with a large number of users.
The documentation also has a guide that may be useful: Firebase Docs: Build Presence in Cloud Firestore

Firebase cloud functions - observe ref

I´m completely new to firebase cloud functions. I want to refer to different refs inside a cloud function and get an array with the values of these.
So for example I want to get all the fruits from ('fruits').
similar to (IOS):
ref.observe(.childAdded ... )
How do I code that?
There is currently no way to only trigger a Cloud Function when a new child node is added. Instead you will have to trigger for every write (with onWrite()) and then filter inside the code.
The Firebase documentation on using previous values has this example:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
// Only edit data when it is first created.
if (event.data.previous.exists()) {
return;
}
// Exit when the data is deleted.
if (!event.data.exists()) {
return;
}
...
Hmmm.... on second reading of your question, you may be looking how to read from other parts of your database inside a Cloud Function. For that you'd use the Firebase Admin SDK, of which you can find an example in this sample in the functions-samples repo:
admin.database().ref('/messages').push({original: original}).then(snapshot => {
...
})

Resources