Decrease response time in Firebase Vue app when liking a post - firebase

I have an app with different 'procedures' (think posts or pages), which one can like. Currently the process works: Tap like => run method "likeProcedure" => run dispatch action "likeProcedure" => update UI. It usually happens almost immediately, but sometimes there's a lag that gives this a "non-native" feel. Is there some sort of way that I could return feedback immediately, while stile holding single origin of truth on the firebase database?
Thank you!
Page Code:
<v-icon
v-if="!userProfile.likedProcedures || !userProfile.likedProcedures[procedure.id]"
color="grey lighten-1"
#click="likeProcedure({ id: procedure.id })"
>
mdi-star-outline
</v-icon>
and
computed: {
...mapState(["userProfile"]),
procedures() {
return this.$store.getters.getFilteredProcedures();
},
},
Vuex code:
async likeProcedure({ dispatch }, postId) {
const userId = fb.auth.currentUser.uid;
// update user object
await fb.usersCollection.doc(userId).update({
[`likedProcedures.${postId.id}`]: true,
});
dispatch("fetchUserProfile", { uid: userId });
},
Side note: I'm trying to remove the dispatch("fetchUserProfile") command, but this doesn't work, because then I'm calling dispatch without using it. And I cannot remove dispatch because then the object calling it is empty. And I cannot remove the object, because then the argument ('postId') isn't working. So if anyone knows how to deal with that, that would be extremely helpful.
Thank you :)

So this is the best solution I've come up yet. It kind of destroys the idea of a single source of truth, but at least it provides an immediate UI update:
async likeProcedure({ dispatch, state }, postId) {
console.log("likeProcedure");
const userId = fb.auth.currentUser.uid;
// line below provides immediate update to state and hence to the UI
state.userProfile.likedProcedures[postId.id] = true;
// line below updates Firebase database
await fb.usersCollection.doc(userId).update({
[`likedProcedures.${postId.id}`]: state.userProfile.likedProcedures[
postId.id
],
});
// line below then fetches the updated profile from Firebase and updates
// the profile in state. Kind of useless, but ensures that client and
// Firebase are in-sync
dispatch("fetchUserProfile", { uid: userId });
},
async fetchUserProfile({ commit }, user) {
// fetch user profile
const userProfile = await fb.usersCollection.doc(user.uid).get();
// set user profile in state
commit("setUserProfile", userProfile.data());
// change route to dashboard
if (router.currentRoute.path === "/login") {
router.push("/");
}
},

Related

Unable to catch the 'auth' event in Hub.listen while calling Auth.federatedSignIn

I am using SolidJS and building a SPA (no server rendering). For authentication, I use the #aws-amplify/core and #aws-amplify/auth packages. At the application root I call the Hub.listen function:
Hub.listen('auth', ({ payload }) => console.log(payload));
In the SignUp component I call Auth.federatedSignIn:
const SignUp = () => {
return (
<button onClick={() => {
Auth.federatedSignIn({ provider: CognitoHostedUIIdentityProvider.Google });
}}>
Sign up
</button>
);
}
I have configured the Amplify as such:
Amplify.configure({
Auth: {
region: import.meta.env.VITE_AWS_REGION,
userPoolId: import.meta.env.VITE_AWS_POOL_ID,
userPoolWebClientId: import.meta.env.VITE_AWS_POOL_CLIENT_ID,
oauth: {
domain: import.meta.env.VITE_AUTH_URL,
responseType: 'code',
redirectSignIn: location.origin + '/account/external',
redirectSignOut: location.origin + '/my',
},
},
});
When I click on the button I am redirected to the import.meta.env.VITE_AUTH_URL (simply outside of my app), choose an account, and then return back to the /account/external page. At that time I expect a consoled payload object in Web tools, but there is nothing. I get it when I call Auth.signOut(), so I assume that I configured Amplify correctly and Hub is subscribed to the auth channel.
My thoughts were that Hub cannot catch any events because after returning the application basically renders again in a new context and Hub simply isn't able to catch anything (events aren't sent from AWS?). I tried to declare the urlOpener function under the oauth property in the config and Google's sign page opened in a new tab, but even then I couldn't get any events in the preserved old page (from which I called Auth.federatedSignIn).
Questions:
How should I organize the code to get the signIn and signUp events?
Can I pass some data into the Auth.federatedSignIn to get it back in the Hub.listen, so I will be able to join the CognitoUser with the data that existed at the time of starting Sign in/Sign up (I want to add a new login type to existed user)?
Here is an example regarding the first question. Just check that your listener is set before you call the Auth.federatedSignIn() method.
export default class SignInService {
constructor(private landingFacade: LandingFacade) {
this.setupAuthListeners(); // Should be called at the top level.
}
private setupAuthListeners() {
Hub.listen('auth', ({ payload: { event, data } }) => {
switch (event) {
case 'signIn':
this.landingFacade.signInSuccess();
break;
case 'signIn_failure':
console.log('Sign in failure', data);
break;
case 'configured':
console.log('the Auth module is configured', data);
}
});
}
public async signIn(): Promise<void> {
await Auth.federatedSignIn();
}
}
For the second one: I'll use a local state and set/query the object you need.

Firebase Functions failing Run the latest Code

I have a firebase function that runs based on a trigger to create a chat room. The function was running for a long time correctly until I updated it to add a new field on the document it is creating. That is archived: false.
After adding the field on the function, it sometimes adds the field when it runs but at times fails to add the field. So I think that firebase at times runs the updated code and sometimes runs the only code when the function is triggered because all other fields are created apart from the archived: false which is created and sometimes not created.
So I fail to understand why this happens yet in the code, the field archived is not dependent on any other variable.
Below is the function.
// firestore trigger to crete a chat room
exports.createChatRooms = functions.firestore.document("/jobs/{id}").onUpdate((change, context) => {
const job = change.after.data();
if (job.status === "Accepted") {
const roomId = context.params.id;
const room = admin.firestore().collection("rooms").doc(roomId);
return room.set({
name: job.title,
jobId: roomId,
createdAt: new Date().getTime(),
agent: job.agent,
archived: false,
user: job.user,
members: [job.user.id, job.agent.id],
});
} else {
return null;
}
});
Kindly help me understand why this is happening.

Data function not working react native firestore

I got stuck in this puzzle which doesn't seem to wanna be solved, I am kinda sure I am forgetting something since I just started learning react-native.
I have this code :
async componentDidMount() {
let user = await UserRepository.getUserRef(firebase.auth().currentUser.uid);
await firebase
.firestore()
.collection("reminder")
.where("user", "==", user)
.get()
.then((remindersRecord) => {
remindersRecord.forEach((reminderDoc) => {
console.log(reminderDoc.data());
});
});
I am trying to get the "reminders" data of the connected user, the query works since we got reminderDoc which contain a bunch of objects, and inside there is the data I want but when I call data() nothing changes, I don't get the document it returns the same object.
Reminder collection :
Any help is much appreciated!
I tried to replicate this on my side and I think this is working fine. I think that result that you get is related with fields boss and user which I guess are reference type in firestore. If you log to console such fields give results like this:
{
reference: DocumentReference {
_firestore: Firestore {
_settings: [Object],
_settingsFrozen: true,
_serializer: [Serializer],
_projectId: <PROJECT_ID>,
registeredListenersCount: 0,
bulkWritersCount: 0,
_backoffSettings: [Object],
_clientPool: [ClientPool]
},
_path: ResourcePath { segments: [Array] },
_converter: {
toFirestore: [Function: toFirestore],
fromFirestore: [Function: fromFirestore]
}
},
text_field: 'test',
...
}
So for presented example you will get 2 such fields and for those fields you will not see as a string. BTW the timestamp field will not be shown properly as well.
To avoid this issue you can use example path property of document reference or when it comes to timestamp you can use toDate() method. I have created small example to show the fields properly (looping over all the object fields):
remindersRecord.forEach((reminderDoc) => {
for (const [key, value] of Object.entries(reminderDoc.data())) {
if (key == 'boss' || key == 'user') console.log(`${key}: ${value.path}`)
else if (key == 'startAt') console.log(`${key}: ${value.toDate()}`)
else console.log(`${key}: ${value}`)
});
I tested this in nodejs directly, but it should work in componentDidMount as well.

Binding VuexFire to a collection filtered with a query

I'm unsuccessfully trying to bind a Vuex state attribute to a queried collection in FireStore. I was wondering if anyone with more experience could point me in the right direction. This is what I'm currently doing:
In a Vuex Module called auth I'm declaring the following bind to userArticles
export const bindUserArticles = firestoreAction(({ bindFirestoreRef }, id) => {
return bindFirestoreRef('userArticles', userCollectionRef('articles', id))
})
This in turn points to a firebase method for querying the data (which works)
export const userCollectionRef = (collectionName, id) => {
return firestore().collection(collectionName).where("author.idAuthor", "==", id)
}
And I'm importing and dispatching the method in my Vue file in the following way
computed: {
...mapGetters('user', ['currentUser']),
},
methods: {
...mapActions('articles', ['bindUserArticles']),
},
watch: {
currentUser () {
this.bindUserArticles(this.currentUser.id)
}
}
So when the currentUser is updated upon login the method is triggered. The method is triggered and the right id is being sent, I've tested it with console.log. There is no error being displayed. When I try for example to modify the idAuthor of an existing article in the database, the list userArticles does not update. When I try adding or deleting an article from the database that has the specific idAuthor, the list userArticles does not update. I've also tried placing the this.bindUserArticles(this.currentUser.id) in the created() and mounted() life-cycle, to no avail.Does anyone have a clue where I'm going wrong about this?
Thanks in advance

How to update the UI on firebase realtime database "push" in offline mode

I'm using react-native-firebase in my app. The problem i'm facing is how to handle the UI updates when user tries to push data when offline.
If the user is online we can use the on() method to get realtime updates but what to do when they are offline. We know that the pushed data is stored in the cache and pushed when user is online again. Can this cached data be used to do what i aim at achieving?
Here's the code i used to receive realtime updates:
var ref333 = firebase.database().ref(`/user-posts/${uid}/`)
ref333.on('value',function (snap) {
var s = snap.val();
console.log("NEW POSTS "+JSON.stringify(s))
})
The code i use to push the data.
var postData = {
uid: uid,
body: 'body',
title: 'title',
starCount: 0
};
// Get a key for a new Post.
var newPostKey = firebase.database().ref().child('posts').push().key;
var ref222 = firebase.database().ref(`/posts/${newPostKey}`)
var ref333 = firebase.database().ref(`/user-posts/${uid}/${newPostKey}`)
ref222.push(postData, function (onComplete) {
console.log("COMPLETED")
ref333.push(postData,function (onComplete) {
console.log("NEXT COMPLETED")
}, function (error) {
console.log("ERROR IN ",error)
})
}, function (error) {
console.log("error == "+error)
})
The .on snspashot listener should be triggered even if in offline mode.
According to the docs:
https://firebase.google.com/docs/database/web/read-and-write
You can use the value event to read a static snapshot of the contents
at a given path, as they existed at the time of the event. This method
is triggered once when the listener is attached and again every time
the data, including children, changes.
This should work in offline mode as well. If you are not receiving updates - something else is wrong.
This problem was solved by adding this lines of code to your native code:
https://rnfirebase.io/docs/v5.x.x/core/default-app#Enable-Database-Persistence

Resources