How to play a sound after a dispatch without user interaction - javascript

I'm trying to play a song when an action is being dispatched. There is no user interaction and the action is being triggered by an external API, so the user never needs to do anything, the display of the screen changes automatically.
This is my code :
newAppointmentsCalled.forEach((appointment) => {
const appointmentIsNotYetCalled =
appointmentsCalled.findIndex((a) => a.id === appointment.id) === -1
if (appointmentIsNotYetCalled) {
dispatch({
type: 'PATIENT_CALLED',
payload: appointment,
})
sound.play()
}
})
I'm inside a useEffect, I tried to add a setTimeout but this doesn't change anything, I still get the error :
Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first.
Any help is highly appreciated

Related

firebase severtimestamp delay (w/ vanilla JS)

I am trying to create a live chat with vanilla JS and firebase.
I'm able to add and get the "newest" message render to the DOM with the following codes:
add message
async addChat(message) {
const chat = {
message,
createdAt: serverTimestamp(),
};
return await addDoc(this.chat, chat);
}
get message
getChat(syncChat) {
onSnapshot(this.chat, snapshot => {
snapshot.docChanges().forEach(shot => {
if (shot.type === "added") {
syncChat(shot.doc.data());
}
});
});
}
render message to the DOM
render(data) {
const html = `
<li class="chat-item">
<span class="chat-message">${data.message}</span>
<p class="chat-time">${formatDistanceToNow(data.createdAt.toDate(), {
addSuffix: true,
})}</p>
</li>
`;
this.chatList.innerHTML += html;
}
Like I said, I can render the chat message no problem, but I realize the there's a time delay when message is added to firebase and timestamp is created. So when the first new message is displayed on the DOM, the time actually return null, and I have to refresh the page in order to display the actually time.
I am just wondering if there's anyway to fix that? I tried looking through the firebase doc and got nothing. I also did a little bit of digging on the interest but most of them are using a framework and not vanilla js.
Thanks for the help.
SOLUTION
getChat(showChat) {
const q = query(
this.collection,
where("room", "==", this.room),
orderBy("createdAt", "asc")
);
this.unsub = onSnapshot(q, snapshot => {
snapshot.docChanges().forEach(snap => {
if (snap.type === "added") {
showChat(snap.doc.data({ serverTimestamps: "estimate" }));
}
});
});
}
Thanks for the help! I have decide to use the snapshotoptions, the estimate servertimestamps, and everything works great.
When you perform a write operation on a client, the Firestore SDK immediately invoked all local listeners with the new data - something typically referred to as latency compensation within Firebase.
So when you write a new document with a server-side timestamp, your onSnapshot listener gets called before the request is sent to the server, and at that point the value of the timestamp field will be null, since it hasn't been calculated yet.
Then when the server confirms that the write operation was completed, and what the timestamp value was, your onSnapshot listener gets called again, now with the correct value for the timestamp field. Since this is a change to the document you already got before, it is an event with type changed.
To display the update, your code needs to also handle events where the type is changed, and use that to update the DOM element that it added for the first event.
Alternatively you can ask the Firestore SDK to give you an estimated timestamp on the first event by passing an option to the data() call as shown here: Firebase Cloud Firestore how to set snapShotOptions

query to firestore to check collection and document

i am a little bit new to firebase / firestore. I am using the stripe api.
Once the user hits start trial on the prebuilt stripe checkout page, then it should go to firestore and create a new collection called subscriptions with all the users information. It seems to be doing this, however, I created a page called successPage, and it basically checks to make sure that it created it.
please find the code below:
const successPage = props => {
firebase.auth().onAuthStateChanged((user) => {
if(user) {
console.log("calling success page : " + user.uid)
//checking if user is paying for subscription
firestore.collection('customers').doc(user.uid).collection('subscriptions')
.where('status', 'in', ['trialing', 'active']).get()
.then(activeSubscriptions => {
// if this is true, the user has no active subscription.
if (activeSubscriptions.empty === true) {
console.log(user.uid)
firestore.collection('customers').doc(user.uid)
.get().then(
doc => {
if (doc.exists) {
firestore.collection('customers').doc(user.uid).collection('subscriptions').get().
then(sub => {
if (sub.docs.length > 0) {
var activityStatus = "canceled"
createCheckoutSession(activityStatus)
console.log('subcollection exists');
} else {
alert("Your account has been created, but your payment details we're not successfully created. You will now be redirected to the checkout page")
createCheckoutSession()
console.log(user.uid)
console.log("does not exist!")
}
});
}
});
} else if (activeSubscriptions.size > 1){
alert("you have more then one active subscription. please manage your subscriptions and cancel one of your subscriptions to access the application")
} else {
firestore.collection("profiledata").doc(user.uid).update({
accountStatus: "active"
}).then (() => {
firestore
.collection("roger#x.ca")
.add({
to: user.email,
message: {
},
})
.then(() => console.log("email out for delivery!"));
props.history.push('/clients')
})
}
});
}
})
return (
<input type="hidden"></input>
)
}
it checks the subscriptions collection where status = to either trialing, or active, and then it checks everything inside subscriptions to see what is going on, but it for some reason it keeps redirecting to the stripe page (createCheckoutSession) even though the subscriptions collection has been created. is this a timing issue?
Stripe triggers a Webhook to your server/cloud functions when a new subscription is created and after that the document is created in Firestore. This process might take some time and meanwhile your user may have been redirected to the success page. If the document has not been created yet then you won't be able to show the transaction status.
Here's a workaround that you can do:
While creating a Stripe Checkout session on your server, you can actually create the subscriptions document but set a field called "stripe_response" to false and also add the new subscription document ID as a query parameter in the stripe success_url. So you url maybe looks something like: https://domain.ext/paymentSuccess?id=<that_document_id>,
Now when the user is on the success page, look for that specific subscription document with the ID mentioned in the query parameter. If the "stripe_response" is still false, then maybe the webhook has not done it's job yet. Just retry the request after 5 seconds and then show the status to user. Make sure you set the stripe_response to true in the webhook.
To simply step 2, you can just attach a realtime listener on the document so when the status is updated you get the response asap and don't need to rely on polling.
Coming to the 'keeps redirecting' part, can you please specify what the exact redirect situation? Where is it redirecting and all that?
It's definitely a race condition but if you follow the steps above it should take care of it. Please let me know if you need more assistance fixing this issue.

How multi-tab logout can be managed in reactJS?

window.addEventListener('storage', e => {
if(e.key === 'access_token' && e.oldValue && !e.newValue) {
store.dispatch(userSignOut());
}
})
If this is a suitable solution, then where(lifecycle event) should i paste this?
The best way to do that is by using the BrodcastChannel feature in Javascript. The Broadcast Channel API allows simple communication between browsing contexts (that is windows, tabs, frames, or iframes) with the same origin (usually pages from the same site).
For example:
// Connecting to a broadcast channel
const userChannel = new BroadcastChannel('user');
function signOut() {
// and after that, we have to broadcast a message to the user channel which user has signed out from the application as below:
userChannel.postMessage({
userId: "", // If the user opened your app in multi-tabs and signed-in with multi accounts, you need to put the userId here to identify which account has signed out exactly
payload: {
type: "SIGN_OUT"
}
});
}
}
So we created the user's BrodcastChannel but we need to observe on sent messages by user's channel and do the right actions by payload type.
userChannel.onmessage = data => {
if(data.payload.type === "SIGN_OUT") {
// As I talked before about multi-accounts, we need to check the current user id with the sent userId by the userChannel and if they were the same, we have to dispatch the userSignOut action.
store.dispatch(userSignOut());
}
}
enter link description here
Try with focus Listener isFocused
Also check
React Navigation emits events to screen components that subscribe to them:
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition
completed)
willBlur - the screen will be unfocused
didBlur - the screen unfocused (if there was a transition, the transition
completed)
I suppose you should create an action, something like CHECK_CREDENTIALS, and dispatch it on every API call. This way, if you are removing user credentials from localStorage, every tab will be logged out on 1st call.
However, this is something your server should be capable of. When the token is expired/deleted, it is logical that you should get some comprehensive error.

How to deal with situations where loading is interrupted in react-redux applications

I have the following example to illustrate how I update the redux state while loading data from the database in a react-redux application. learningActionDataOperationBegin sets loading (in the reducer) to true, then once the data is fetched from the database, fetchLearningActionsSuccess sets loading back to false. Inside the component, where the database records are displayed, FetchLearningActions is called only when initially loading is false. I believe this is the common practice with react-redux applications.
However, for example if the browser is closed or if the internet gets disconnected while the data is being loaded, the loading stays as true, which is a problem since in the next visit to the page FetchLearningActions will not get called (because loading is still true) and the page will always display Loading.. text on the screen. I wonder how I should handle this. Any ideas or suggestions?
export function FetchLearningActions(submissionId) {
var url = 'api/LearningAction/FetchLearningActions';
return dispatch => {
dispatch(learningActionDataOperationBegin());
axios.get(url, { params: { submissionId } })
.then(response => {
console.log('Learning actions are loaded.');
const learningActions = new schema.Entity('learningActions');
const normalizedData = normalize(response.data, [learningActions]);
dispatch(fetchLearningActionsSuccess(normalizedData.entities.learningActions))
})
.catch(error => { learningActionDataOperationFailure(error) });
}
}
You need to wrap learningActionDataOperationFailure(error) with dispatch. That action should set isLoading to false and maybe set an error flag. You now can differentiate between states where your app is making a request and whether it finished successfully or not.

Reloading current user doesn't refresh emailVerified state in Firebase Authentication

I've been trying to fix the problem for a whole day and couldn't make it work. I'm getting irritated, firebase docs are such a mess it's insane..
So I'm trying to implement email verification on my React app. I wen't with the docs, and google do send the email, I can click it, it's all good.
But, the email verified state doesn't change at all, and believe me I've went through all the stackoverflow topics.
firebase.auth().doSignInWithEmailAndPassword(email, password)
.then(() => {
console.log(firebase.auth().currentUser);
firebase.auth().currentUser.reload()
.then(() => {
console.log(firebase.auth().currentUser);
})
})
So I've found i need to reload the user to see the changes applied, tho they just won't work no matter what I do.
Both console logs return email.verified = false at all times.
I'm hopeless, anyone have any idea on how to make this work?
I was wondering whether me setting up custom domain as a verification link has to do anything with that? I'm testing on localhost, link is linking to live website.
Please help :(
The method you should call is signInWithEmailAndPassword, so remove the "do" in the function call:
firebase.auth().signInWithEmailAndPassword(email, password)
.then((credential) => {
const currentUser = credential.user;
console.log(firebase.auth().currentUser);
firebase.auth().currentUser.reload()
.then(() => {
console.log(firebase.auth().currentUser);
})
}).catch((err) => {console.error("Problem with sign in ", err);}
Use the credential returned from this function and assign its user value to currentUser to validate that the user has been authenticated.
Lastly, remember to add a catch at the end whenever you use then. In this case, err will contain a property err.code that will tell you why the user could not be authenticated.
I had the same problem. You need to reload your current user using a timer and then listen to user changes (not auth changes) in a Streambuilder. As soon as the user clicks the link they will be redirected to your main page.
Use a Streambuilder to listen to changes to the user:
StreamBuilder<User?>(
stream: FirebaseAuth.instance
.userChanges(), // monitor the changes to the user
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return LoadingScreen(); // show loading screen while loading
if (snapshot.hasData && !snapshot.data!.emailVerified) {
return VerifyEmailScreen();
} // if not verified show the verify email screen
if (snapshot.hasData) {
return MainScreen();
} // if logged in already show main screen
return OnboardingScreen(); // if not logged in already show the auth screen
},
),
In your VerifyEmailScreen() create a timer in it's initState like this:
void initState() {
Timer.periodic(Duration(seconds: 5), (timer) {
FirebaseAuth.instance.currentUser?.reload();
});
super.initState();
}
I hope it helps your problem.
Cheers

Categories

Resources