Why navigator.geolocation.watchposition not work in setInterval()? - react-native

I try to get location coordinate every 1 sec. But geolocation.watchposition not work in setInterval
here is my code:
componentDidMount(){
const intervalId = BackgroundTimer.setInterval(() => {
navigator.geolocation.watchPosition((position) => {
console.log(position.coords.latitude);
var lat = parseFloat(position.coords.latitude);
var lng = parseFloat(position.coords.longitude);
console.log("lat :",lat);
console.log("lng :",lng);
});
}, 1000);
BackgroundTimer.clearInterval(intervalId);
}
if I remove BackgroundTimer.setInterval method. It gives me coordinates. But I want everysecond

The watchPosition method will automatically call the success callback as soon as the user's position has changed, so doing this won't work. If you really need to get every second (which you might not need as user's position rarely will change in 1 second and it consumes a lot of battery power) you use the setInterval using along with navigator.geolocation.getCurrentPosition method.

Related

How to increase the range of an array taken from an API query

I have a function for when clicking a button increase the contents of a list.
Content is removed from an API by the following code:
const [data, setData] = useState();
const [maxRange, setMaxRange] = useState(2);
const getAPIinfo = ()=>{
GetEvents(maxRange, 0).then((response) => response.json())
.then(result_events => {
const events = result_events;
setData({events:events});
}).catch(e => setData({events:events}));
}
And my function is this:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
what I'm not able to do is update the maxRange value of the API query to increase the list...
this function should be heavily refactored:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
when you use maxRange here, you are setting new state, while the function itself ir running, the state is not instantly updated, buttonLoadMore is a function in a particular time. it cannot get new maxRange instantly, while running buttonLoadMore does that make sense? Also you cannot update data state just like a regular variable by assigning new variable using = operator, you should refactor this function to something like this:
const buttonLoadMore = ({data})=> {
const newMaxRange = maxRange + 4;
setMaxRange(newMaxRange);
const newData = {events: [...data.events.slice(0, newMaxRange)]};
setData({...newData})
}
also you will get bug here. since your getAPIinfo is setting data state to an object {events: events}. I took the liberty and tried refactoring it here.
There is also a bug in your getAPIinfo in line }).catch(e => setData({events:events})); the events variable you declared in .then function cannot be reached here. It is simply out of scope. unless you know that .catch resolves into data, you will get an error in this line.
take a look at this example here:
const promiseFunction = ()=>{
return new Promise<string>((resolve)=>resolve('i like coca cola'))
}
const getter = () => {
promiseFunction()
.then(response => {
const thenVariable = response;
console.log(thenVariable) // i like coca cola
})
.catch(error=>{
console.log(thenVariable) // Error:Cannot find name 'thenVariable'.
})
}
as you can see .catch() is in different scope than .then() will not be available outside so events cannot be reached by .catch function.
Usually you would use catch for error handling. Maybe show a line on screen, that error has accoured, and data cannot be fetched at this time. etc. There's a very good book that explains all these concepts in detail here: https://github.com/getify/You-Dont-Know-JS
I would strongly recommend for you to switch to typescript because your code is crawling with bugs that should be easily avoided just by type checking, and adding eslint configurations.

Titanium geolocation distanceFilter property is not working as expected

Need a help here for geolocation API - even after using Titanium.Geolocation.distanceFilter = 10; 10 in meter callback function is getting triggered randomly without moving here and there. Any idea what is wrong here ?
function Geolocate() {
var self = this;
// The callback function we should call when location is finally determined
this.locationReceivedCallback = function () {};
// Check if location event is already triggered or not
this.isLocationEventRegister = false;
// The function that unregisters the location event
this.unregisterLocationEvent = function () {
this.isLocationEventRegister = false;
Ti.Geolocation.removeEventListener('location', self.locationReceivedCallback);
};
}
Geolocate.prototype.init = function () {
// Setting up some preference values
Titanium.Geolocation.distanceFilter = 10; //The minimum change of position (in meters) before a 'location' event is fired.
if (deviceDetect.isIos()) {
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; // Using this value on Android enables legacy mode operation, which is not recommended.
} else {
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_LOW; //Using this value on Android enables simple mode operation.
}
};
Geolocate.prototype.getCurrentPosition = function (callback) {
this.isLocationEventRegister = true;
this.locationReceivedCallback = callback;
Titanium.Geolocation.addEventListener("location", this.locationReceivedCallback);
};
Geolocate.prototype.locationServicesAreAvailable = function () {
if (Titanium.Geolocation.locationServicesEnabled) {
return true;
} else {
return false;
}
};
Geolocate.prototype.cancelLocationRequest = function () {
this.unregisterLocationEvent();
};
module.exports = Geolocate;
Actual scenario is whenever I clicked on get location its gives me current location. then i tried to drag it map or image view for nearest place. Suddenly my view go current location. This is happening because of call back ? How to get rid off this ?
It's not a problem to do with your code, it is simply GPS in-accuracy.
GPS accuracy is (almost) never better than 10 meters, meaning it can be 10 meters off. When it recalculates your position it can be 10 meters down the line, so even if you're perfectly still with perfect GPS accuracy, you still might have differences of about 10 meters.
That said, you probably don't have best GPS accuracy when sitting behind a computer in a building, you probably have closer to 30-45 meters accuracy. Meaning every recalculation can easily be 10 meters differently.
Your solution would actually be rate-limit on top of using this property. The property will make sure it will not trigger multiple times per second, and then you can, in your own code, rate limit what you do with it.

Ionic 3 infinite-scroll simulate in e2e test jasmine/protractor

If you go here: http://ionicframework.com/docs/api/components/infinite-scroll/InfiniteScroll/
Inspect the demo and click the last item on the list:
Then in the console type: $0.scrollIntoView()
Infinite Scroll is never triggered.
Is there a way to programmatically trigger infinite-scroll in protractor context?
The implementation of the scroll in your example rely on the speed/velocity of the scroll which I guess falls far from the expected range when scrollIntoView is called.
One workaround is to simulates a smooth scroll by emitting multiple scroll events over a reasonable time. The idea is to reproduce as close as possible the behavior of a real user.
Some browsers already provides the option via scrollIntoView (supported by Chrome 62) :
$0.scrollIntoView({behavior: "smooth", block: "end"});
Using the accepted answer, in my case, I used ion-infinite-scroll as the argument.
Complete test to check if more content is loaded in Ionic:
describe('Scroll', () => {
it('should load more when reached end', async () => {
let list = getList();
let currentCount = await list.count();
const refresher = element(by.tagName('ion-infinite-scroll')).getWebElement();
let count = 0;
while(true){
browser.executeScript(`arguments[0].scrollIntoView({behavior: "smooth", block: "end"});`, refresher);
browser.sleep(1000); // wait for data to be loaded from api
list = getList();
let newCount = await list.count();
expect(newCount).toBeGreaterThanOrEqual(currentCount)
expect(newCount).toBeLessThanOrEqual(currentCount * 2)
if(newCount === currentCount){
break;
}
currentCount = newCount;
count++;
}
expect(count).toBeGreaterThan(0);
})
});
function getList() {
return element(by.className(pageId + ' list')).all(by.tagName('ion-item'));
}

How to throttle background task based on InteractionManager?

I have a list of projects in a Redux store. When new location data arrives from the GPS I'm updating the distance to each project in the list. Currently I'm throttling the update to 5 second intervals.
If the update coincides exactly with a navigation transition (using Ex-navigation) I get a non-smooth transition.
I would like to throttle based on a time interval and on data from InteractionManager.
Here is the current code:
let throttledProjectUpdate = _.throttle((position) => {
dispatch(ProjectState.projectNewLongLat(position.coords.longitude, position.coords.latitude))
}, 5000)
let watchId = navigator.geolocation.watchPosition((position) => {
dispatch(sensorGeoNewPositionAction(position))
throttledProjectUpdate(position)
});
I would like to do something like this:
let throttledProjectUpdate = _.throttle((position) => {
InteractionManager.runAfterInteractions(() =>
dispatch(ProjectState.projectNewLongLat(position.coords.longitude, position.coords.latitude))
);
}, 5000)
let watchId = navigator.geolocation.watchPosition((position) => {
dispatch(sensorGeoNewPositionAction(position))
throttledProjectUpdate(position)
});
But a few things are stopping me:
Is this really possible? I.e is InteractionManager a singleton which I can require from everywhere?
It feels wrong to mix GUI stuff with my background state updating
How should I solved this?

backbone view in router lost after creation

When I try to associate my router's public variable this.currentView to a newly created view, the view gets lost, the public variable is null instead of containing the newly created view.
var self=this;
var watchListsCollection = new WatchlistCollection;
watchListsCollection.url = "watchlists";
user.fetch().done(function() {
watchListsCollection.fetch().done(function () {
loggedUser.fetch().done(function () {
self.currentView = new UserView(user, watchListsCollection,loggedUser);
});
});
});
alert(this.currentView); //null
The fetch() calls you do are firing asynchronous AJAX requests, meaning the code in your done handlers are not going to be executed untill the server calls return. Once you've executed user.fetch() the browser will fire off a request and then continue running your program and alert this.currentView without waiting for the requests to finish.
The sequence of events is basically going to be
call user.fetch()
alert this.currentView
call watchListsCollection.fetch()
call loggedUser.fetch()
set the value of self.currentView
You will not be able to see the value of your currentView before the last server request have completed.
If you change your code to
var self=this;
var watchListsCollection = new WatchlistCollection;
watchListsCollection.url = "watchlists";
user.fetch().done(function() {
watchListsCollection.fetch().done(function () {
loggedUser.fetch().done(function () {
self.currentView = new UserView(user, watchListsCollection,loggedUser);
alertCurrentView();
});
});
});
function alertCurrentView() {
alert(this.currentView);
}
You should see the correct value displayed. Now, depending on what you intend to use your this.currentView for that might or might not let you fix whatever issue you have, but there's no way you're not going to have to wait for all the requests to complete before it's available. If you need to do something with it straight away you should create your UserView immediately and move the fetch() calls into that view's initialize().
fetch() is asynchronous, but you check your variable right after you've started your task. Probably these tasks, as they supposed to be just reads, should be run in parallel. And forget making a copy of this, try _.bind instead according to the Airbnb styleguide: https://github.com/airbnb/javascript
var tasks = [];
tasks.push(user.fetch());
tasks.push(watchListsCollection.fetch());
tasks.push(loggedUser.fetch());
Promise.all(tasks).then(_.bind(function() {
this.currentView = new UserView(user, watchListsCollection, loggedUser);
}, this));
or using ES6 generators:
function* () {
var tasks = [];
tasks.push(user.fetch());
tasks.push(watchListsCollection.fetch());
tasks.push(loggedUser.fetch());
yield Promise.all(tasks);
this.currentView = new UserView(user, watchListsCollection, loggedUser);
}