Internet Connection Listener - javascript

I have an application which scans 2D barcodes then retrieves data from the URLs provided by the codes. In the event that the user loses connection to the internet, the application begins to store the URLs via AsyncStorage. The issue is, I need to implement a listener that upon regaining an internet connection, the application begins a given method. Are there any recommended ways to go about implementing a connection listener such as this?
Edit:
I have tried using a NetInfo EventListener however I am not sure if I'm using it incorrectly, as it always calls the passed function, even when the internet status hasn't changed.
_connectionHandler = (e) => {
this.setState({ cameraActive: false })
NetInfo.getConnectionInfo().then((connectionInfo) => {
if (connectionInfo.type === "none"){
console.log("No internet")
dataArray.push(e.data)
let barcodeData_delta = {
data: dataArray
}
AsyncStorage.mergeItem(STORAGE_KEY, JSON.stringify(barcodeData_delta));
NetInfo.isConnected.addEventListener(
'connectionChange',
this._handleConnectionChange(e.data)
);
this.setState({ cameraActive: true })
} else {
console.log("Internet available -> Going to read barcode now")
this._handleBarCodeRead(e.data);
}
})
}

React Native has a NetInfo documentation, there you can see how to add a listener his connection changes, and do what you want when its called.
Add a Handler to isConnected property
NetInfo.isConnected.addEventListener(
'connectionChange',
_connectionHandler
);
A function that handles the change, just adjust your setState with the camera, I couldn't figure out when to call it.
_connectionHandler = (isConnected) => {
this.setState({ cameraActive: false })
if (!isConnected){
console.log("No internet")
dataArray.push(e.data)
let barcodeData_delta = {
data: dataArray
}
AsyncStorage.mergeItem(STORAGE_KEY, JSON.stringify(barcodeData_delta));
this.setState({ cameraActive: true })
} else {
console.log("Internet available -> Going to read barcode now")
this._handleBarCodeRead(e.data);
}
})
}

Related

I want to pass user View time of a video to server using an api call on visibilityChange event in react

I want to pass the watch time of a video the user has seen when user closes the page,reload the page or navigate to another page. I am using visibilityChange event for this. When i try to navigate to another page, the api call runs perfectly. But the data i am sending to the api is not updated correctly. I am going to provide the code and the output below so you can understand perfectly what my problem is.
useEffect(async () => {
const x = 0;
console.log("use effect is run number ::::", x + 1);
window.addEventListener("visibilitychange", sendViewTime);
return () => {
window.removeEventListener("visibilitychange", sendViewTime);
};
}, []);
I have added the event listener in the useEffect.
the sendViewTime method is the method i want to call on visibility change event. This Method is working perfectly but for some reason the params are not updated even though i have set their states in their relavant hooks.
const sendViewTime = async () => {
if (document.visibilityState === "hidden") {
console.log("the document is hidden");
const value = localStorage.getItem("jwt");
const initialValue = JSON.parse(value);
console.log("the send View Time is :::", played_time);
const params = {
video_id: video_id_url,
viewTime: played_time,
MET: MET_value,
weight: "",
};
console.log("params are :::", params);
await setEffort(params, initialValue).then((res) => {
console.log("set effort api response is ::: ", res);
});
} else {
console.log("the document is back online");
}
};
//This onProgress prop is from react Player. Here i am updating the state of video progress.
onProgress={(time) => {
console.log("the time is :::", time);
const time_1 = Math.round(time.playedSeconds);
const time_2 = JSON.stringify(time_1);
setPlayed_time(time_2);
console.log("the played time is :::", played_time);
}}
//OUTPUT
// the document is hidden.
// the send View Time is :::
//params are ::: {video_id: '23', viewTime: '', MET: undefined, weight: ''}
//set effort api response is ::: {status: 200, message: 'Success', data: {…}, time: '2.743 s'}
//the document is back online
Never mind guys. I found the solution. It seems that i have to pass played_time and met value as a prop to the useEffect.If you want to know how useEffect works please visit this link. In general is it better to use one or many useEffect hooks in a single component?.

Synchronous Meteor Call in React Class Component

I’ve been experimenting around with this for quite a while but I haven’t been able to quite figure this out. I am trying to get a synchronous Meteor method call going in a React class component, as part of a registration form I’m doing up. Basically I continually check if a username is taken every time the username input field changes (onChange) and update a visual indicator. I know this isn't ideal design as once you scale it out, it's costly database calls, so I will change this/throttle it as needed, but that's after I get this basic functionality down.
Here is my Meteor method on the server:
checkUsername({ username }) {
var result = false;
if (username != "") {
if (Accounts.findUserByUsername(username)) {
result = true;
}
}
return result;
},
Here is my function in a React class component:
async checkUsername(username) {
Session.set("usernameValid", true);
var syncCall = await Meteor.call(
"checkUsername",
{
username,
}
);
// Shouldn't the line of code below run only AFTER the call above completes?
if (syncCall === false) Session.set("usernameValid", false);
return Session.get("usernameValid");
}
I understand that this shouldn't need to be async, nor should I need await. Furthermore I know this code is wrong since syncCall will always be undefined. This is where I check for username availability:
if (username === "") {
this.state.username = false;
this.setFeedback("username", "", false);
}
else if (this.checkUsername(username)) {
// Username is valid and available
console.log("Username is available!");
this.state.username = username;
this.setFeedback("username", "", true);
}
else {
// Username is invalid and nonempty
console.log("Username is invalid/unavailable!");
this.state.username = false;
this.setFeedback("username", "Username is invalid/unavailable :-(", false);
}
The issue is that the checkUsername function returns before the Meteor call completes, and hence the state isn't updated in time and I can't check that either.
How do I go about forcing this to be synchronous? I read about Meteor.wrapAsync() but I couldn't nail how to incorporate it properly (or even if it's the right way to go about it). Any help is appreciated, thank you!
Meteor method calls on the client do not return a Promise, so you can't await them. You need to provide a callback function:
checkUsername(username) {
Session.set("usernameValid", true);
Meteor.call("checkUsername", {username},
(isTaken) => isTaken && Session.set("usernameValid", false));
}
However with React I wouldn't use session variables. React has it's own support for reactive state variables via useState and it's much better integrated:
const [usernameValid, setUsernameValid] = useState(false);
...
checkUsername(username) {
Meteor.call("checkUsername", {username}, setUsernameValid);
}

WebRTC switch camera

I am currently working for WebRTC multipeer connection. I want to implement feature to switch camera from front to back while on call.
This is the code I am using to switch cameras
async function changevideo() {
const audioSource = audioInputSelect.value;
const videoSource = videoSelect.options[videoSelect.selectedIndex].value;
var tempconstraints ={
video: {
deviceId: videoSource ? { exact: videoSource } : undefined,
width: { max: 320 },
height: { max: 240 }
},
audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
};
var newstream = await navigator.mediaDevices.getUserMedia(tempconstraints);
if (connections[socketId]) {
Promise.all(connections[socketId].getSenders().map(function (sender) {
debugger;
return sender.replaceTrack(newstream.getTracks().find(function (track) {
debugger;
return track.kind === sender.track.kind;
})).then(data =>
{
console.log(data);
});;
}));
var track = localStream.getTracks().find(function (track) { return track.kind == videoTrack.kind });
localStream.removeTrack(track);
localStream.addTrack(videoTrack);
connections[tempsocketid].onnegotiationneeded = function () {
connections[tempsocketid].createOffer().then(function (offer) {
return connections[tempsocketid].setLocalDescription(offer);
}).then(function () {
socket.emit('signal', socketId, JSON.stringify({ 'sdp': connections[tempsocketid].localDescription, 'room': roomNumber }), roomNumber);
}).catch(e => console.log(e));
}
}
}
Here connections contains the RTCpeerconnection details of all type of connections connected.
socketId is the id of main user on which I want to switch camera. So, connections[socketId] gives me the RTCPeerConnection details of user with socketId.
newstream is the stream after switching camera.
If I directly update src of video to newstream then my camera changes only on my device.
I have searched alot but everywhere I am getting solution to use replaceTrack but it is not wokring in my case. Everytime I use it nothing happens on screen and I am also not getting any error in console.
Update
I have used the onnegotiationneeded with remove and add track.
tempsocketid is the socketId of another user who is connected.
So I have 2 users one have socketid stored in socketId and another having socketid stored in tempsocketid. So currently I am trying to switch camera of user with socketid socketId
and when negotiation is called then I am getting error in another users console.
DOMException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate
You are probably being unable to cause a renegotiaton so change to the facingMode of your camera cannot affect the other peers, but you do not use this explicitely as I see but replaceTracks. But still you may not being able to trigger a renegotiation. Checkout things that cause renegotiation: https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack#Usage_notes
Changing facingMode setting by applying constraints with applyConstraints may be a solution without using replaceTracks.
A strange idea coming to my mind to dispatch negotiationneeded event yourself, but I would try this after chasing the reason not being able to casue renegotiation itself by replacing track, or changing camera, and everything else.
About another reason: As for the reasons that casues renegotiation, your back camera resolution is most probably higher than the front camera, so it looks like a reason. If you start from the back camera first and then switch to front it might have been a no reason. I am suspicious about your max constraints of width nad height. They might be very lower for the both camera hence resulting in the same size, resoltion and so a no reason according to the list in the linked page above. I suggest removing them.
Also the replaceTracks returns a promise but the map function it is called from does not return anything, hence undefined. You are supposed to use the promises inside the array argument to Promise.all. I would suggest return those promises inside the map function.
I have fixed the issue the problem was with socketId I was sending socketId of current user with different user.
As replace track was not working so I have used removeTrack and addTrack to force negotiation.
Here is my working code
if (connections[socketId]) {
localStream.getVideoTracks()[0].enabled = false;
var track = localStream.getTracks().find(function (track) { return track.kind == videoTrack.kind });
localStream.removeTrack(track);
localStream.addTrack(videoTrack);
connections[tempsocketid].onnegotiationneeded = function () {
console.log('negotiationstarted');
connections[tempsocketid].createOffer().then(function (offer) {
return connections[tempsocketid].setLocalDescription(offer);
}).then(function () {
console.log('negotiation signal sent');
socket.emit('signal', tempsocketid, JSON.stringify({ 'sdp': connections[tempsocketid].localDescription, 'room': roomNumber }), roomNumber);
}).catch(e => console.log(e));
}
localStream.getVideoTracks()[0].enabled = true;
}

Web BLE Characteristic startNotifications sometimes doesn't bind

I'm using web BLE. I have based my code according to the example of the heart rate measurement.
Everything is working fine most of the time. But sometimes, even if the connection is successfully made, when I try to bind to the notification, it doesn't work.
The link is made in this function :
_startNotifications(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
console.log(characteristic);
return characteristic.startNotifications().then(() => characteristic);
}
When everything is OK, I can see in the console that BluetoothRemoteGATTCharacteristic has a value : DataView(2) {}
Otherwise, when it's not working it has a value : null
I would like to be able to retry automatically, if I detect that the value is null. But I'm not familiar with Promise (I think this is it) and console.log(characteristic.value) doesn't work here.
How would you approach this ?
What I ended up doing is "bypass" the issue. So it's a more algorithmic resolution than a pure Javascript one.
I didn't change the connection function, so it is still called like this :
device._startNotifications(some_uuid).then(handleHeartRateMeasurement)
I check everything in the handleHeartRateMeasurement function :
var ready = false;
function handleHeartRateMeasurement(heartRateMeasurement) {
console.log("Hold on...");
heartRateMeasurement.addEventListener("characteristicvaluechanged", event => {
// Everytime the value change, this should be triggered
// If it did not, variable "ready" will stay false
ready = true;
var value = device.parseValue(event.target.value);
// Do something with value here
});
var check = function(){
// If we have received data from characteristic, we are ready to go !
if(ready === false){
console.log("Device connected but not receiving data");
// Stop the current notification subscription
device.stopNotificationsHeartRateMeasurement();
// Start a new one
device._startNotifications(some_uuid).then(handleHeartRateMeasurement);
setTimeout(check, 1000); // check again in a 1s
}
else{
console.log("Device connected and receiving data");
}
}
setTimeout(() => {
check();
}, 500);
}

Can't change Vue Instance data values

I'm building a custon social login page for my web application, and I'm stuck with a bug I can't find why it's hapenning .
Basically, I want to call a function called "connectFb" and then if all the Facebook API calls are successful, I would like to change a bunch of data in my vue instance in order to render other elements . (those are rendred conditionally via v-if)
Here's the part of my code responsible for this :
app = new Vue({
el : "#social-auth",
data: {
showTwitter : false,
showFb: true,
showPages: false,
fb_state: "unconnected",
continue_auth: false,
pages_fb: []
},
methods : {
connectFb: function() {
FB.login(function(response) {
if (response.authResponse) {
alert('You are logged in & cookie set!');
fb_token = response.authResponse.accessToken
FB.api('/me/accounts','get',{access_token: fb_token},function(pages){
if(pages["error"] !== undefined){
console.log('EROR')
}
else{
console.log("Got a list of pages");
console.log(pages);
this.pages_fb = pages.data;
this.showFb = false;
this.showPages = true;
this.continue_auth = true;
}
})
} else {
alert('User cancelled login or did not fully authorize.');
}
},{scope: 'public_profile,manage_pages'});
return false;
}
How The Code Works :
Basically, after the user is logged in to fb, it will get a list of his pages, this is not the problem, the problem is in the success callback after it (the callback related to the function fetching pages) . using the debugger I could see that the variable pages contains all the data I need and pages.data return an array of those pages info .
After this I'm trying to attribute it to my instance variable called pages_fb . when this code run pages_fb is always empty even though pages.data is not .
The problem is not only with pages_fb but also with all my instance variable that should change in the callback they are the same after the callback run .
I'm getting mad at this problem, so please help me understand what's wrong .
Extremely common mistake. this defined in your FB.login callback is not the Vue. Use an arrow function, closure, or bind to make it correct.
FB.api('/me/accounts','get',{access_token: fb_token}, pages => {
...
})
See How to access the correct this inside a callback?
When you use this. in a callback it isn't pointing to your Vue instance anymore. You can user => functions to bind this the way you want. Try this:
FB.api('/me/accounts','get',{access_token: fb_token},(pages) => {
if(pages["error"] !== undefined){
console.log('EROR')
}
else{
console.log("Got a list of pages");
console.log(pages);
this.pages_fb = pages.data;
this.showFb = false;
this.showPages = true;
this.continue_auth = true;
}
})

Categories