React Native: storage only works in simulator - ios

I've tried AsyncStorage, react-native-store, and react-native-simple-store, and they all work in the simulator, but not on a device. I'm using redux and redux-thunk to load the stored state. I call the following function in my root component's componentDidMount method (using react-native-simple-store):
export function loadState() {
return (dispatch, getState) => {
store.get('state').then((state) => {
if (state) {
let action = {
type: LOAD_STATE,
state: fromJS(state),
};
dispatch(action);
} else {
store.save('state', initialState);
}
}).catch((error) => {
console.log(error);
});
};
}
And then in my reducer when the user triggers an update I'll update the state in storage before returning the new state like this:
case SET_UPDATED_DATE:
newState = state.set('updatedDate', action.date);
store.update('state', newState.toJS())
.catch((error) => console.log(error));
return newState;
Is the initializing/updating approach insufficient? Does something special need to be done to set it up for a device? Or is redux-thunk not supported when run on a device – main.jsbundle or with the development server – (putting a log statement at the top of the loadState function's returned function leads me to believe it may not be being called when on a device)?

Following the AsyncStorage docs' example, I've figured out a way to make it work. In my reducer file (my redux state is an Immutable.js object):
var STORAGE_KEY = '#AppName:state';
export function loadState() {
return async (dispatch, getState) => {
try {
var value = await AsyncStorage.getItem(STORAGE_KEY);
if (value !== null) {
dispatch(replaceState(value));
console.log('Recovered selection from disk:');
console.log(value);
} else {
saveState(JSON.stringify(initialState.toJS()));
console.log('No state on disk. Initialized with initialState.');
}
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
};
}
function replaceState(newState) {
return {
type: REPLACE_STATE,
newState: fromJS(JSON.parse(newState)),
};
}
async function saveState(state) {
try {
await AsyncStorage.setItem(STORAGE_KEY, state);
console.log('Saved selection to disk:');
console.log(state);
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}
Then in the reducer function:
case SET_UPDATED_DATE:
newState = state.set('updatedDate', action.date);
saveState(JSON.stringify(newState.toJS()));
return newState;

Related

Capacitor iOS Geolocation watchPostion kCLErrorDomain error 1 while permission granted

I am using an older version of the capacitor geolocation, v1.3.1, and recently switched to the watchPosition implementation but occasionally that created a situation where the position is null or undefined even when the device is showing the location icon being active for the app. I tried to solve that by falling back to the slower getCurrentPosition function but still persists. Has anyone run into this issue before? Here is a gist of the hook.
https://gist.github.com/billpull/8bc6e49872cfee29aa5cef193b59c835
useCurrentPosition.ts
const useCurrentPosition = (): GeoWatchPositionResult => {
const [position, setPosition] = useState<Position>();
const [watchId, setWatchId] = useState("");
const [error, setError] = useState();
const clearWatch = () => {
if (watchId) {
clearPosition({ id: watchId });
setWatchId("");
}
};
const startWatch = async () => {
if (!watchId) {
const id = await watchPosition(async (pos: Position | null, err) => {
if (err) {
setError(err);
}
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(newPosition);
}
});
setWatchId(id);
}
};
useEffect(() => {
startWatch();
return () => clearWatch();
}, []);
return { currentPosition: position, error };
};
Even though the watchPosition is still returning location data on the interval I am getting a kCLErrorDomain error 1. which online says it means the permission was denied but thats not the case the phone was just in sleep mode. Is there a way to catch this error specifically? Should I clear the watch and restart it on this error?
Edit:
One attempt I made was to use a try catch in the watch, but I still have encountered this issue.
const useCurrentPosition = (): GeoWatchPositionResult => {
const [position, setPosition] = useState<Position>();
const [watchId, setWatchId] = useState("");
const [error, setError] = useState();
const clearWatch = () => {
if (watchId) {
clearPosition({ id: watchId });
setWatchId("");
}
};
const startWatch = async () => {
if (!watchId) {
const id = await watchPosition(async (pos: Position | null, err) => {
try {
if (err) {
setError(err);
}
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(newPosition);
}
} catch (ex) {
await requestPermission();
clearWatch();
await startWatch();
}
});
setWatchId(id);
}
};
useEffect(() => {
startWatch();
return () => clearWatch();
}, []);
return { currentPosition: position, error };
};
I think you should use this code snippet to check the current position.
import { Geolocation, Geoposition } from '#ionic-native/geolocation/ngx';
constructor(private geolocation: Geolocation) { }
...
let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
// data can be a set of coordinates, or an error (if an error occurred).
// data.coords.latitude
// data.coords.longitude
});
You can also use the following code snippet to get the current position.
this.geolocation.getCurrentPosition().then((resp) => {
// resp.coords.latitude
// resp.coords.longitude
}).catch((error) => {
console.log('Error getting location', error);
});
If you want to handle the permission denied error, the best way to handle the permission denied error is to check the permission status of the app first before trying to access any location data. This can be done by using the Capacitor Permissions API. If the permission has been granted, you can then proceed to use the watchPosition or getCurrentPosition APIs. If the permission has been denied, you can present a prompt to the user to request permission again.
setState is an asynchronous function, so please pass to it an anonymous function:
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(() => newPosition);
}

Chrome mv3 await sendMessage to background service worker [duplicate]

This question already has answers here:
chrome.runtime.onMessage response with async await
(7 answers)
Closed 1 year ago.
I am having an issue of asynchronicity (I believe). sendResponse() in contentscript.js does not wait for getThumbnails() to return.
I am sending a message in popup.js:
chrome.tabs.sendMessage(tabs[0].id, {message: "get_thumbnails", tabUrl: tabs[0].url},
function (respThumbnails) {
const thumbUrl = respThumbnails.payload;
console.log("payload", thumbUrl)
}
);
Then, in contentscript.js I listen for this message:
chrome.runtime.onMessage.addListener(async function(request,sender,sendResponse) {
if(request.message === "get_thumbnails") {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload:payload});
}
});
async function getThumbnails() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
};
But it is also possible that the problem is coming from my getThumnails() function, because most of the times, payload is null and not undefined. So getThumbnails() might return before it is completely executed.
If this is the case, I have no idea why...
I also tried this code for getThubnails():
async function getThumbnails() {
let x = await function() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
}
return x;
};
But this does not work, it seems to break my code...
The callback of onMessage should return a literal true value (documentation) in order to keep the internal messaging channel open so that sendResponse can work asynchronously.
Problem
Your callback is declared with async keyword, so it returns a Promise, not a literal true value. Chrome extensions API doesn't support Promise in the returned value of onMessage callback until https://crbug.com/1185241 is fixed so it's just ignored, the port is immediately closed, and the caller receives undefined in response.
Solutions
Remove the async keyword from before (request, sender, sendResponse), then...
Solution 1
Call an async function that can be embedded as an IIFE:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "get_thumbnails") {
(async () => {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload});
})();
return true; // keep the messaging channel open for sendResponse
}
});
Solution 2
Declare a separate async function and call it from the onMessage listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.message === "get_thumbnails") {
processMessage(msg).then(sendResponse);
return true; // keep the messaging channel open for sendResponse
}
});
async function processMessage(msg) {
console.log('Processing message', msg);
// .................
return 'foo';
}

Return value from subscribe in Ionic

So I want to return a value from a subscribe function like this:
async obtenerListadoClases(categoria) {
var clasesDB = await this.getClases(categoria)
.subscribe((data: any) => {
clasesDB = data.clasesDB // **Want to return this**
console.log(clasesDB,'clasesDB'); // **Getting Value**
})
console.log(clasesDB, 'outside'); // **Not Getting Value**
return clasesDB;
}
Also, I want to use this function in another place like this:
var listaClases = await this.claseServicio.obtenerListadoClases(categoria); // Not getting the correct info
// console.log(listaClases , 'listado clases');
What Im doing wrong? Or how can I fix it? Thanks in advance!
You can only subscribe to observables.
The Observable way
getClases(categoria): Observable<any> {
return new Observable(observer => {
// logic to return data
observer.next(data);
observer.complete()
// logic when error
observer.error(error);
});
}
Return the getClases() function
obtenerListadoClases(categoria): Observable<any>{
return this.getClases(categoria);
}
Use the function where you want:
this.obtenerListadoClases(categoria)
.subscribe(
result => {
// what you want to do with the result
},
error => {
// what you want to do with the error
});
The Promise way
getClases(categoria): Promise<any> {
return new Promise((resolve, reject) => {
// logic to return data
resolve(data);
// logic when error
reject(error);
});
}
Return the getClases() function
obtenerListadoClases(categoria): Promise<any>{
return this.getClases(categoria);
}
Use the function where you want:
this.obtenerListadoClases(categoria)
.then(result => {
// what you want to do with the result
})
.catch(error => {
// what you want to do with the error
});
You should be using promises with the .subscribe(). Only observables use .subcribe()
Also, stay away from promises in the angular world. Time to think reactive.
Is this returning an observable? this.getClases(categoria) post the code please.

How to prevent multiple instances in Electron

I do not know if this is possible but I might as well give it a chance and ask.
I'm doing an Electron app and I'd like to know if it is possible to have no more than a single instance at a time.
I have found this gist but I'm not sure hot to use it. Can someone shed some light of share a better idea ?
var preventMultipleInstances = function(window) {
var socket = (process.platform === 'win32') ? '\\\\.\\pipe\\myapp-sock' : path.join(os.tmpdir(), 'myapp.sock');
net.connect({path: socket}, function () {
var errorMessage = 'Another instance of ' + pjson.productName + ' is already running. Only one instance of the app can be open at a time.'
dialog.showMessageBox(window, {'type': 'error', message: errorMessage, buttons: ['OK']}, function() {
window.destroy()
})
}).on('error', function (err) {
if (process.platform !== 'win32') {
// try to unlink older socket if it exists, if it doesn't,
// ignore ENOENT errors
try {
fs.unlinkSync(socket);
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
}
net.createServer(function (connection) {}).listen(socket);;
});
}
There is a new API now: requestSingleInstanceLock
const { app } = require('electron')
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
}
})
// Create myWindow, load the rest of the app, etc...
app.on('ready', () => {
})
}
Use the makeSingleInstance function in the app module, there's even an example in the docs.
In Case you need the code.
let mainWindow = null;
//to make singleton instance
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
if (isSecondInstance) {
app.quit()
}

breeze observableArray binding - are properties observable?

I have a viewmodel which consists of a list(foreach loop) of DoctorPrices and when clicking on an item in the list it open up a CRUD form on the side. However when i update the values on the CRUD the observableArray that is bound to the foreach is not refreshing? (although the values are updates in the DB correctly)
From my data access module i call the following query.
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
return manager.executeQueryLocally(query);
}
In my viewmodel i have the following code:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
services is bound using a foreach loop (not posting here as the code is simple and works)
When i click on a one of the DoctorPrices it gets the data as follows and places it in an observable:
this.selectedPrice = function (data, event) {
self.currentService(data);
self.showEdit(true);
};
I then bind selectPrice to a simple form that has the properties on it to be modified by the user. I then call manager.SaveChanges().
This results in the following problem: the value is being updated correctly but the GUI / Original List that is bound in the foreach is not being updated? Are the properties in breeze not observables? What is the best way to work with something like this.
I thought of a workaround and changing the code with something like this:
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
});
But i feel that clearing the array in that way is sloppy and not the right way of doing things specially with long lists.
Can someone please point me in the right direction on how to bind observableArray properties properly so they are updated?
Additional code my VM Component:
function services() {
var self = this;
this.showForm = ko.observable(false);
this.currentService = ko.observable();
this.services = ko.observableArray(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
this.title = ko.observable();
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
self.showDetails(false);
});
this.show = function (value) {
self.showForm(value);
};
this.showDetails = ko.observable(false);
this.addNewService = function() {
self.currentService(doctorServices.createService(doctorList.viewModel.instance.currentDoctorID()));
console.log(self.currentService().entityAspect.entityState);
self.showDetails(true);
};
this.showDelete = ko.computed(function() {
if (self.currentService() == null)
return false;
else if (self.currentService().entityAspect.entityState.isDetached()) {
self.title('Add new service');
return false;
} else {
self.title('Edit service');
return true;
}
});
this.deleteService = function() {
self.currentService().entityAspect.setDeleted();
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
};
this.closeDetails = function () {
doctorServices.manager.rejectChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.showDetails(false);
};
this.selectService = function (data, event) {
self.currentService(data);
self.showDetails(true);
};
this.saveChanges = function () {
console.log(self.currentService().entityAspect.entityState);
if (self.currentService().entityAspect.entityState.isDetached()) {
doctorServices.attachEntity(self.currentService());
}
console.log(self.currentService().entityAspect.entityState);
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.currentService.notifySubscribers();
self.showDetails(true);
};
}
return {
viewModel: {
instance: new services()
},
template: servicesTemplate,
};
Below is my Breeze Data Class:
define('data/doctorServices', ['jquery', 'data/dataManager', 'knockout','mod/medappBase', 'breeze', 'breeze.savequeuing'], function ($, manager, ko,base, breeze, savequeuing) {
var services = ko.observableArray([]);
return {
attachEntity:attachEntity,
getServices: getServices,
services: services,
manager:manager,
getDoctorServices: getDoctorServices,
getServiceById: getServiceById,
createService:createService,
hasChanges: hasChanges,
saveChanges: saveChanges
};
function getServices() {
var query = breeze.EntityQuery.from("DoctorPrices");
return manager.executeQuery(query).then(function (data) {
services(data.results);
}).fail(function (data) {
console.log('fetch failed...');
console.log(data);
});;
}
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
var set = manager.executeQueryLocally(query);
return set;
}
function getServiceById(serviceId) {
return manager.createEntity('DoctorPrice', serviceId);
//return manager.getEntityByKey('DoctorPrice', serviceId);
}
function handleSaveValidationError(error) {
var message = "Not saved due to validation error";
try { // fish out the first error
var firstErr = error.innerError.entityErrors[0];
message += ": " + firstErr.errorMessage;
base.addNotify('error', 'Could not save.', message);
} catch (e) { /* eat it for now */ }
return message;
}
function hasChanges() {
return manager.hasChanges();
}
function attachEntity(entity) {
manager.addEntity(entity);
}
function createService(doctorId) {
return manager.createEntity('DoctorPrice', { DoctorPricingID: breeze.core.getUuid(), DoctorID:doctorId }, breeze.EntityState.Detached);
};
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
base.addNotify('success', 'Saved.', 'Your updates have been saved.');
}
function saveFailed(error) {
var reason = error.message;
var detail = error.detail;
if (error.innerError.entityErrors) {
reason = handleSaveValidationError(error);
} else if (detail && detail.ExceptionType &&
detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
// Concurrency error
reason =
"Another user, perhaps the server, " +
"may have deleted one or all of the settings." +
" You may have to restart the app.";
} else {
reason = "Failed to save changes: " + reason +
" You may have to restart the app.";
}
console.log(error);
console.log(reason);
}
}
});
Please note this is my frist attempt at both a data class and VM. At the moment i am relying heavily on clearing the array ([]) and using notifySubscribers to make the array refresh :(
I bet you're missing an observable somewhere. I can't tell because you keep hopping from property to property whose definition is not shown.
For example, I don't know how you defined this.currentService.
I'm confused by this:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
Why is it a ko.computed? Why not just make it an observable array.
self.service = ko.observableArray();
// ... later replace the inner array in one step ...
self.service(doctorServices.getDoctorServices(
doctorList.viewModel.instance.currentDoctorID()));
I urge you to follow the observability trail, confident that your Breeze entity properties are indeed observable.
vm.selectedPrice = ko.dependentObservable(function () {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
}, vm);
vm is ur model on which u applied bindings , try this it will work.

Resources