I'm somewhat new to using react native and I'm trying to implement Facebook login with my app using the react native fbsdk.
I've gotten it to the point where it will ask for permissions from the user's Facebook profile, then return to the app with a success alert message. Instead of displaying the alert onLoginFinished, I want to push to a new screen.
This is my login button code in the main constructor class:
constructor(props){
super(props);
this.goToNewPage = this.goToNewPage.bind(this);
const infoRequest = new GraphRequest(
'/me',
{parameters: {
fields: {
string: 'email,first_name,last_name,id,gender' // what you want to get
}
}},
this._responseInfoCallback,
);
Login = React.createClass({
render: function() {
return (
<View >
<LoginButton
navigator={this.props.navigator}
style = {{marginLeft:55, marginRight:55, height:50, width:300}}
onLoginFinished={
(error, result) => {
if (error) {
alert("Login failed with error: " + result.error);
} else if (result.isCancelled) {
alert("Login was cancelled");
} else {
alert("Login was successful with permissions: " + result.grantedPermissions)
new GraphRequestManager().addRequest(infoRequest).start();
this.goToNewPage;
}
}
}
onLogoutFinished={() => alert("User logged out")}
/>
</View>
);
}
});
} //closes constructor
_responseInfoCallback(error: ?Object, result: ?Object) {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
alert('Success fetching data: ' + result.toString());
console.log(result);
}
}
goToNewPage(){
console.log("Hello from go to new page function");
this.props.navigator.push({
id: 'newPage',
name: 'Going To New Page',
});
}
But after the success alerts are displayed, it does not push to the new page. It seems like this.props.navigator is not recognized in my Login class, and I'm not sure why. Any help at all would be greatly appreciated
Turns out the problem was that I declared the login class in my constructor. Moving it to a componentWillMount function fixed it for me!
Currently you are calling goToNewpage() method in error, result block of LoginButton component and in any kind of block we can't access current class reference "this" directly because it loses its reachability.
Then you should create a global variable for this component as
var _this;
and assign current reference of class in component constructor like:
_this = this;
and should use _this.props.navigator.push in place of this.props.navigator.push within your goToNewpage() method. Thanks!
Related
I am using a quick action on case object to close the case with the help of aura component.
In aura component I am using the below code to get the work items related to agent and close them:
omniAPI.getAgentWorks().then(function(result) {
var works = JSON.parse(result.works);
console.log('Works : ',works);
//how to get current case's work ID
var work = works[0];
console.log('Works[0] : ',work);
// Update and add condition
//add if condition
omniAPI.closeAgentWork({workId: work.workId}).then(function(res) {
if (res) {
console.log("Closed work successfully");
var workspaceAPI = cmp.find("workspace");
workspaceAPI.getFocusedTabInfo().then(function(response) {
var focusedTabId = response.tabId;
workspaceAPI.closeTab({tabId: focusedTabId});
})
.catch(function(error) {
console.log(error);
});
} else {
console.log("Close work failed");
}
}).catch(function(error) {
console.log(error);
});
});
here getAgentWork is retrieving all the work item related to the agent,how do I figure out the current case's workID? so that I can close only that workitem using omniAPI.closeAgentWork({workId: work.workId}).
I have a SPA PWA React app.
It is installed and running in standalone mode on the mobile device (Android+Chrome).
Let's say the app lists people and then when you click on a person it diplays details using /person route.
Now, I'm sending push notifications from the server and receiving them in the service worker attached to the app. The notification is about a person and I want to open that person's details when the user clicks on the notification.
The question is:
how do I activate the /person route on my app from the service worker
and pass data (e.g. person id, or person object)
without reloading the app
From what I understand, from the service worker notificationclick event handler I can:
focus on the app (but how do I pass data and activate a route)
open an url (but /person is not a physical route, and either way - I want avoid refreshing the page)
You can listen for click event for the Notification which you show to the user. And in the handler, you can open the URL for the corresponding person which comes from your server with push event.
notification.onclick = function(event) {
event.preventDefault();
// suppose you have an url property in the data
if (event.notification.data.url) {
self.clients.openWindow(event.notification.data.url);
}
}
Check these links:
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event
https://developer.mozilla.org/en-US/docs/Web/API/Clients/openWindow
To answer my own question: I've used IndexedDB (can't use localStorage as it is synchronous) to communicate between SW and PWA, though I'm not too happy about it.
This is roughly how my service worker code looks (I'm using idb library):
self.addEventListener('notificationclick', function(event) {
const notif = event.notification;
notif.close();
if (notif.data) {
let db;
let p = idb.openDB('my-store', 1, {
upgrade(db) {
db.createObjectStore(OBJSTORENAME, {
keyPath: 'id'
});
}
}).then(function(idb) {
db = idb;
return db.clear(OBJSTORENAME);
}).then(function(rv) {
return db.put(OBJSTORENAME, notif.data);
}).then(function(res) {
clients.openWindow('/');
}).catch(function(err) {
console.log("Error spawning notif", err);
});
event.waitUntil(p);
}
});
and then, in the root of my react app ie in my AppNavBar component I always check if there is something to show:
componentWillMount() {
let self = this;
let db;
idb.openDB('my-store', 1)
.then(function (idb) {
db = idb;
return db.getAll(OBJSTORENAME);
}).then(function (items) {
if (items && items.length) {
axios.get(`/some-additional-info-optional/${items[0].id}`).then(res => {
if (res.data && res.data.success) {
self.props.history.push({
pathname: '/details',
state: {
selectedObject: res.data.data[0]
}
});
}
});
db.clear(OBJSTORENAME)
.then()
.catch(err => {
console.log("error clearing ", OBJSTORENAME);
});
}
}).catch(function (err) {
console.log("Error", err);
});
}
Have been toying with clients.openWindow('/?id=123'); and clients.openWindow('/#123'); but that was behaving strangely, sometimes the app would stall, so I reverted to the IndexedDB approach.
(clients.postMessage could also be the way to go though I'm not sure how to plug that into the react framework)
HTH someone else, and I'm still looking for a better solution.
I had a similar need in my project. Using your's postMessage tip, I was able to get an event on my component every time a user clicks on service worker notification, and then route the user to the desired path.
service-worker.js
self.addEventListener("notificationclick", async event => {
const notification = event.notification;
notification.close();
event.waitUntil(
self.clients.matchAll({ type: "window" }).then(clientsArr => {
if (clientsArr[0]) {
clientsArr[0].focus();
clientsArr[0].postMessage({
type: "NOTIFICATION_CLICK",
ticketId: notification.tag,
});
}
})
);
});
On your react component, add a new listener:
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.addEventListener("message", message => {
if (message.data.type === "NOTIFICATION_CLICK") {
history.push(`/tickets/${message.data.ticketId}`);
}
});
}
}, [history]);
I'm fairly new to React and stuck regarding a very minor problem. I wrote an UI that essentially calls a service that returns the responses in the form of an array. Now, I need those responses from the service to be displayed in the form of a nested menu. For e. g., one of my axios calls returns a response of [1,2,3,4] and the other axios call returns [1.1,1.2,1.3,..]. I want these responses to be aligned in the form of
1
1.1
1.2
1.3
2
2.1
etc.,
i. e. the UI should show 1,2,3,4 .. and when the user clicks on 1, then 1.1,1.2 etc. should be displayed.
I'm using React, material-ui's components and redux for this.
I have a function to do the above mentioned.. but I'm not sure if I'm doing it right.
handleMenuData() {
var applist = this.props.menuData;
var appNames = [];
var moduleNames = [];
applist.forEach(app => {
app.moduleNames.forEach(module => {
try {
return axios.get(
'service url' + app.name + '/' + module,
);
} catch (error) {
console.error(error);
}
});
appNames.push({
name: app.name,
moduleNames: moduleNames,
});
moduleNames = [];
});
this.setState({
appNames: appNames,
});
}
and in my state,
this.state = {
appList: [],
appNames: [],
moduleNames: [],
};
app names are 1,2,3 and module names are 1.1,1.2 and I was thinking of using ListItemText component from material UI.
I think what you are doing is incorrect. axios.get is an asynchronous function and you are not waiting for the response to come from the server. That's why you get all arrays as empty. Try calling a recursive function like this.
const getModuleNames = (i) => {
axios.get('service url' + applist[i].name + '/' + module)
.then((response) => {
if(i < applist.length){
applist[i].moduleNames = response.data;
appNames.push({
name: applist[i].name,
moduleNames: applist[i].moduleNames
});
getModuleNames(i++);
}
else {
// code you want to execute after adding data to appNames
}
}).catch((err) => {
// error handling
});
}
getModuleNames(0);
I am using below meteor method to create a new user:
Meteor.methods({
"users.add": function(newUser) {
let cResult = Accounts.createUser({
username: newUser.username,
email: newUser.email
password: newUser.password,
profile: {
first_name: newUser.first_name,
last_name: newUser.last_name,
clinic: newUser.clinic
}
});
for (var index in newUser.roles) {
Roles.addUsersToRoles(cResult, newUser.roles[index].label);
}
return true;
},
"users.addRole": function(userId, newRole) {
Roles.addUsersToRoles(userId, newRole);
return true;
}
});
And the below code to subscribe to the mongo collection:
export default withTracker(() => {
let usersSubscription = Meteor.subscribe("allUsers");
let rolesSubscriptioin = Meteor.subscribe("allRoles");
const eachReady = usersSubscription.ready() &&
rolesSubscriptioin.ready();
const loading = usersSubscription ? !eachReady : true;
return {
loading,
users: Meteor.users.find().fetch(),
roles: Meteor.roles.find().fetch()
};
})(Users);
I am using react to render the information. When I am creating a new user, I am able to get the newly created user except for the roles information. Leading to "undefined of map" error in the react component.
The component name is user and the repository is at this link:
I being new to meteor any help is good.
After trying multiple solutions like getDerviedStateFromProps lifecycle method and everything else, I made a simple check for the undefined state when doing render which solved the problem without going into any lifecycle method. It might be incorrect so still open to suggestions.
I want to make an option to "Select Image From Gallery or Camera". I have tried many modules but they are only providing access to the gallery directly. I am using expo tool for creating a react native application. First I want a popup to open then then the user has to pick an option then the user is redirected according to that option. If you have any suggestion, please help me.
I´ve seen it done with React Native Image Picker, look for it in github:
https://github.com/react-community/react-native-image-picker
Add dependencies:
dependencies {
compile project(':react-native-image-picker')
}
Add permissions:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Usage:
var ImagePicker = require('react-native-image-picker');
// More info on all the options is below in the README...just some common use cases shown here
var options = {
title: 'Select Avatar',
customButtons: [
{name: 'fb', title: 'Choose Photo from Facebook'},
],
storageOptions: {
skipBackup: true,
path: 'images'
}
};
/**
* The first arg is the options object for customization (it can also be null or omitted for default options),
* The second arg is the callback which sends object: response (more info below in README)
*/
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
}
else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
let source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
});
}
});
If you would like to directly start just the camera or the gallery, use it like this:
// Launch Camera:
ImagePicker.launchCamera(options, (response) => {
// Same code as in above section!
});
// Open Image Library:
ImagePicker.launchImageLibrary(options, (response) => {
// Same code as in above section!
});
Hope it helps.