Retrieve user's email in twitter -react native - twitter

I've used react-native-twitter-signin lib to login through twitter. It works well but doesn't give user's email. It is mentioned that you have to apply for a permission for your app to retrieve user's email
How can I ask for a permission to make it work?
Code:
const Constants = {
TWITTER_COMSUMER_KEY: 'XXXXXXX',
TWITTER_CONSUMER_SECRET: 'XXXXXXXXXXXX',
};
export default class twitterLogin extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
}
this.handleLogout = this.handleLogout.bind(this);
}
_twitterSignIn() {
RNTwitterSignIn.init(Constants.TWITTER_COMSUMER_KEY, Constants.TWITTER_CONSUMER_SECRET);
RNTwitterSignIn.logIn()
.then((loginData)=>{
console.log(loginData);
const { authToken, authTokenSecret } = loginData;
if (authToken && authTokenSecret) {
this.setState({
isLoggedIn: true,
});
}
}).catch((error)=>{
console.log(error);
});
}
handleLogout() {
console.log('logout');
RNTwitterSignIn.logOut();
this.setState({
isLoggedIn: false,
});
}
render() {
const { isLoggedIn } = this.state;
return (
<View style={{flex: 1}}>
{
isLoggedIn
?
<TouchableOpacity
onPress={this.handleLogout}
>
<Text>Log out</Text>
</TouchableOpacity>
:
<Icon.Button name='logo-twitter' size={32} color='white' style={styles.icon} onPress={this._twitterSignIn.bind(this)}>
Login with Twitter
</Icon.Button>
}
</View>
);
}
}

In your permission tab => 'Request email addresses from users' check
and enter 'Privacy Policy URL' & 'Terms of Service URL' it will give user email

Related

Cordova Plugin Purchase - redirect after successful subscription

I am using this plugin (https://github.com/j3k0/cordova-plugin-purchase) to handle a in app subscription.
iap.validator = "https://validator.fovea.cc/v1/validate?appName=XXX";
//initiate initInAppPurchase function
useEffect(() => {
const init = async () => {
await initInAppPurchase();
}
init();
}, []);
//if on an ios or android device, then get product info
const initInAppPurchase = () => {
if ((isPlatform('ios')) || (isPlatform('android'))) {
iap.verbosity = iap.DEBUG;
iap.register({
id: "tpmonthly",
alias: "Monthly",
type: iap.PAID_SUBSCRIPTION
});
iap.ready(() => {
let product = iap.get('Monthly');
setPrice(product.price)
setProduct(product)
})
iap.refresh();
}
}
//if user clicks purchase button
const purchaseProduct = () => {
if (product.owned) {
alert('A subscription is currently active.')
} else {
iap.order('Monthly').then(() => {
iap.when("tpmonthly").approved((p: IAPProduct) => {
p.verify();
});
iap.when("tpmonthly").verified((p: IAPProduct) => {
p.finish();
history.push("/ios-signup/");
});
})
}
}
return (
<Button size="large" variant="outlined" onClick={purchaseProduct}>Subscribe Monthly for {productPrice}</Button>
);
What I am hoping to get is that once the subscription is verified that it then redirects the app to /ios-signup/ .. this is not happening.
Is this code correct? And why would it not redirect after p.finish?

IOS Notification Permission alert does not show

SDK Version: 39.0.0
Platforms(Android/iOS/web/all): All
I am not getting accept or decline notifications permissions alert when loading my app in production.
I have tried clearing certificates and keys and allowing expo to add everything from a clean slate, but still no luck. I am starting to think maybe it’s my code which is the reason why the alert doesn’t get fired.
import Constants from "expo-constants";
import * as Notifications from "expo-notifications";
import { Permissions } from "expo-permissions";
import { Notifications as Notifications2 } from "expo";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false
})
});
export default class LoginScreen extends React.Component {
state = {
email: "",
password: "",
notification: {},
errorMessage: null
};
async componentDidMount() {
this.registerForPushNotificationsAsync();
//Notifications.addNotificationReceivedListener(this._handleNotification);
Notifications2.addListener(data => {
this.setState({ notification: data });
});
Notifications.addNotificationResponseReceivedListener(
this._handleNotificationResponse
);
}
_handleNotification = notification => {
this.setState({ notification: notification });
};
_handleNotificationResponse = response => {
console.log(response);
};
handleLogin = async () => {
try {
const { email, password } = this.state;
const expoPushToken = await Notifications.getExpoPushTokenAsync();
console.log(expoPushToken);
const userinfo = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
console.log("user ID ", userinfo.user.uid);
await firebase
.firestore()
.collection("users")
.doc(userinfo.user.uid.toString())
.update({
expo_token: expoPushToken["data"]
})
.then(function() {
console.log("token successfully updated!");
})
.catch(function(error) {
// The document probably doesn't exist.
console.error("Error updating document: ", error);
});
} catch (error) {
console.log("=======Error in login", error);
this.setState({ errorMessage: error.message });
}
};
registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(
Permissions.NOTIFICATIONS
);
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
const token = await Notifications.getExpoPushTokenAsync();
console.log(token);
//this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C"
});
}
};
import { Permissions } from "expo-permissions";
should of been
import * as Permissions from 'expo-permissions';
Sometimes we all make simple mistakes.

Trying to reflect changes in Redux State from CurrentUser on React DOM for CurrentUser?

My app is a dashboard that allows the public to view certain items but not CRUD. If a user logs in, full CRUD is accessible. I'm using home spun JWT/Bcrypt auth in Rails backend and React/Redux for the frontend and state management. I'm wondering the best strategy to have the React DOM immediately reflect when a user login/logout and have certain items like create buttons appear/disappear based on login status. Right now, this.props.currentUser coming from the Redux store doesn't seem to help even though the Redux store has updated.
I'm using JSX ternary operators to display certain items based on currentUser state. I've tried this.props.currentUser ? <button>Example</button> : null or this.props.currentUser !== null ? <button>Example</button> : null and this.props.currentUser.length !== 0 ? <button>example</button> : null, none of which I get any consistency (might work for one compnonent but on page refresh no longer works, etc).
Here's an example component:
import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import { Container, Grid, Image } from "semantic-ui-react";
import { connect } from 'react-redux';
import logo from "../../images/logo-2-dashboard";
import { clearCurrentUser } from "../actions/clearCurrentUserAction";
class Header extends Component {
logout = () => {
localStorage.clear();
this.props.clearCurrentUser()
this.props.history.push("/");
}
render() {
return (
<>
<Container fluid>
<Grid divided>
<Grid.Row columns={2}>
<Grid.Column>
<Image
src={logo}
size="large"
style={{ margin: "3px", padding: "2px" }}
></Image>
</Grid.Column>
<Grid.Column>
// Here's the kitchen sink approach lol
{this.props.currentUser && this.props.currentUser === null ||
this.props.currentUser && this.props.currentUser.length === 0 ? (
<Link
to={"/login"}
onClick={this.props.login}
style={{ marginLeft: "200px" }}
>
Login
</Link>
) : (
<Link
to={"/"}
onClick={this.logout}
style={{ marginLeft: "200px" }}
>
Logout
</Link>
)}
// SAME HERE
{this.props.currentUser && this.props.currentUser !== null ||
this.props.currentUser && this.props.currentUser.length !== 0 ? (
<div>Logged in as: {this.props.currentUser.username}</div>
) : null}
</Grid.Column>
</Grid.Row>
</Grid>
</Container>
</>
);
}
}
const mapStateToProps = state => {
return {
currentUser: state.currentUser.currentUser
}
}
const mapDispatchToProps = dispatch => {
return {
clearCurrentUser: () => dispatch(clearCurrentUser())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Header));
Here is my Redux Action Thunk to set CurrentUser back to null on logout (I'm also clearing localHistory):
export const CLEAR_CURRENT_USER = 'CLEAR_CURRENT_USER'
export const clearCurrentUser = () => dispatch => {
return dispatch({ type: 'CLEAR_CURRENT_USER', payload: null })
}
and the Reducer for currentUser:
const initialState = {
currentUser: [],
};
export const currentUserReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_CURRENT_USER":
return { ...state, currentUser: action.payload }
case "GET_CURRENT_USER":
return { currentUser: action.payload }
case "CLEAR_CURRENT_USER":
return { currentUser: action.payload }
default:
return state;
}
};
Perhaphs this is the wrong approach altogether. I'm a junior working on my own at a company.
you are checking if currentUser is truthy but your initialstate is an array. your initialState for currentUser reducer should be null instead of an empty array.
const initialState = {
currentUser: null,
};
export const currentUserReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_CURRENT_USER":
return { ...state, currentUser: action.payload }
case "GET_CURRENT_USER":
return { currentUser: action.payload }
case "CLEAR_CURRENT_USER":
return { currentUser: action.payload }
default:
return state;
}
};

ReactNative IOS Testflight Firebase MapStateToProps Update Issue

I am having an extremely weird issue where I don't even know where to begin to debug since it only happens when I get the app into test flight. Basically I am trying to load channels based on geolocation. Some automatically load and then some are loaded if less than 100 miles from a mountain (lets call these PUBLIC and PRIVATE channels- both of which are in the same list). I have these 2 firebase calls in my action creator and push them into an array and then call dispatch after. I have an issue with the FlatList where it loads the PUBLIC channels and only when I scroll do the PRIVATE channels. There are a bunch of things I have tried including the most common from that specific github issue (flatlist updating) 'removeClippedSubviews={false}', extra data, pure components, etc, but none have worked.
Instead I found a way to get around this (I know it isn't the best, but I just want a solution that works for now) by just setting a timeout and waiting for the channels before dispatching the action:
setTimeout(function(){ dispatch(loadPublicChannelsSuccess(data)); }, 10);
Unfortunately, now is when the crazy issue comes in. Basically, this works for debug, release/ everything I have tried with XCode, but when it gets to my device the render method basically sits at a loading indicator until I switch tabs with react navigation. This makes no sense to me since it doesn't happen always (maybe 80%) of the time, and only in test flight so far. I had never seen this before setting the timeout either so not really sure where to begin:
render() {
const {loadPublicChannels, loading, publicChannels, checkedInMountain, selectedMountain} = this.props;
return !loadPublicChannels && publicChannels && !loading
? (
<MessagePanelPublic publicChannels={publicChannels} selectedMountain={selectedMountain}/>
) : (
<LoadingAnimation />
);
}
actions
export const getUserPublicChannels = () => {
return (dispatch, state) => {
dispatch(loadPublicChannels());
// get all mountains within distance specified
let mountainsInRange = state().session.mountainsInRange;
// get the user selected mountain
let selectedMountain = state().session.selectedMountain;
// see if the selected mountain is in range to add on additional channels
let currentMountain;
mountainsInRange
? (currentMountain =
mountainsInRange.filter(mountain => mountain.id === selectedMountain)
.length === 1
? true
: false)
: (currentMountain = false);
// mountain public channels (don't need to be within distance)
let currentMountainPublicChannelsRef = FIREBASE_REF_CHANNEL_INFO.child(
"Public"
)
.child(`${selectedMountain}`)
.child("Public");
// mountain private channels- only can see if within range (geolocation)
let currentMountainPrivateChannelsRef = FIREBASE_REF_CHANNEL_INFO.child(
"Public"
)
.child(`${selectedMountain}`)
.child("Private");
// get public channels
return currentMountainPublicChannelsRef
.orderByChild("key")
.once("value")
.then(snapshot => {
let publicChannelsToDownload = [];
snapshot.forEach(channelSnapshot => {
let channelId = channelSnapshot.key;
let channelInfo = channelSnapshot.val();
// add the channel ID to the download list
publicChannelsToDownload.push({ id: channelId, info: channelInfo });
});
// if mountain exists then get private channels/ if in range
if (currentMountain) {
currentMountainPrivateChannelsRef
.orderByChild("key")
.once("value")
.then(snapshot => {
snapshot.forEach(channelSnapshot => {
let channelId = channelSnapshot.key;
let channelInfo = channelSnapshot.val();
publicChannelsToDownload.push({
id: channelId,
info: channelInfo
});
});
});
}
return publicChannelsToDownload;
})
.then(data => {
setTimeout(function(){ dispatch(loadPublicChannelsSuccess(data)); }, 10);
});
};
};
reducer related to public channels
case types.LOAD_PUBLIC_CHANNELS:
return {
...state,
loadPublicChannels: true
};
case types.LOAD_PUBLIC_CHANNELS_SUCCESS:
console.log("PUBLIC");
console.log(action.publicChannels);
console.log(action);
return {
...state,
publicChannels: action.publicChannels,
loadPublicChannels: false,
messages: {}
};
case types.LOAD_PUBLIC_CHANNELS_ERROR:
return {
...state,
channelsPublicError: action.error,
loadPublicChannels: false
};
container which calls mapStateToProps and mapDispatchToProps
class MessagePanelPublicContainer extends Component {
constructor(props) {
super(props);
}
// get public and private channels from redux
async componentDidMount() {
this.props.getUserPrivateChannels();
this.props.loadCurrentUser();
// this.props.getUserPublicChannels();
}
componentDidUpdate(prevProps) {
if (this.props.mountainsInRange && prevProps.mountainsInRange !== this.props.mountainsInRange || prevProps.selectedMountain !== this.props.selectedMountain) {
this.props.getUserPublicChannels();
}
}
lessThan12HourAgo = (date) => {
return moment(date).isAfter(moment().subtract(12, 'hours'));
}
render() {
const {loadPublicChannels, loading, publicChannels, checkedInMountain, selectedMountain} = this.props;
return !loadPublicChannels && publicChannels && !loading
? (
<MessagePanelPublic publicChannels={publicChannels} selectedMountain={selectedMountain}/>
) : (
<LoadingAnimation />
);
}
}
const mapStateToProps = state => {
return {
publicChannels: state.chat.publicChannels,
loadPublicChannels: state.chat.loadPublicChannels,
currentUser: state.chat.currentUser,
loading: state.chat.loadCurrentUser,
mountainsInRange: state.session.mountainsInRange,
selectedMountain: state.session.selectedMountain,
};
}
const mapDispatchToProps = {
loadCurrentUser,
getUserPublicChannels,
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(MessagePanelPublicContainer);
component
import React, { Component } from "react";
import { View, Text, FlatList, ImageComponent } from "react-native";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { ListItem, Icon, Button } from "react-native-elements";
import { withNavigation } from "react-navigation";
import LinearGradient from "react-native-linear-gradient";
import styles from "./Styles";
import moment from 'moment';
import FastImage from 'react-native-fast-image';
class MessagePanelPublicComponent extends Component {
render() {
// rendering all public channels
const renderPublicChannels = ({ item }) => {
return (
<ListItem
leftAvatar={{
source: { uri: item.info.ChannelPicture },
rounded: false,
overlayContainerStyle: { backgroundColor: "white" },
ImageComponent: FastImage
}}
title={item.info.Name}
titleStyle={styles.title}
chevron={true}
bottomDivider={true}
id={item.Name}
containerStyle={styles.listItemStyle}
/>
);
};
const renderText = () => {
return (
<View style={styles.extraTextContainer}>
<Text style={styles.extraText}>
Get within 100 miles from resort or select a closer resort to see more channels...
</Text>
<Icon
name="map-marker"
type="font-awesome"
size={40}
iconStyle={styles.extraIcon}
/>
</View>
);
};
return (
<View style={styles.container}>
<View style={styles.channelList}>
<FlatList
data={this.props.publicChannels}
renderItem={renderPublicChannels}
// keyExtractor={item => item.Name}
keyExtractor={(item, index) => index.toString()}
extraData={this.props.publicChannels}
removeClippedSubviews={false}
/>
{this.props.publicChannels.length < 3 ? renderText() : null}
</View>
</View>
);
}
}

App not authenticated with Firebase. Permission Denied error

I have been following this guide to add and retrieve items from firebase using React Native. If I set my rules on firebase to public, everything works but if I set it to the following, I get a permission denied error.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
I have added all of my config data properly in my ios.js. Is there some basic step that I am missing here?
index.ios.js:
// Initialize Firebase
const firebaseConfig = {
apiKey: 'myapikey',
authDomain: 'myauthdomain',
databaseURL: 'https://url.firebaseio.com',
projectId: 'myProjectId',
storageBucket: 'projectid.appspot.com',
messagingSenderId: 'myMessagingSenderId'
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
class MyNewAppreactold extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
};
this.itemsRef = this.getRef().child('items');
}
getRef() {
return firebaseApp.database().ref();
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
// get children as an array
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
dataSource: this.state.dataSource.cloneWithRows(items)
});
});
}
// componentWillMount() {
// firebase.initializeApp(firebaseConfig);
// }
componentDidMount() {
this.listenForItems(this.itemsRef);
}
render() {
return (
<View style={styles.container}>
<StatusBar title="Grocery List" />
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderItem.bind(this)}
enableEmptySections={true}
style={styles.listview}/>
<ActionButton onPress={this._addItem.bind(this)} title="Add" />
</View>
)
}
_addItem() {
AlertIOS.prompt(
'Add New Item',
null,
[
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
{
text: 'Add',
onPress: (text) => {
this.itemsRef.push({ title: text })
}
},
],
'plain-text'
);
}
_renderItem(item) {
const onPress = () => {
AlertIOS.alert(
'Complete',
null,
[
{text: 'Complete', onPress: (text) => this.itemsRef.child(item._key).remove()},
{text: 'Cancel', onPress: (text) => console.log('Cancelled')}
]
);
};
return (
<ListItem item={item} onPress={onPress} />
);
}
}
AppRegistry.registerComponent('MyNewAppreactold', () => MyNewAppreactold);
The code you shared doesn't authenticate the user. Since your security rules require that the user is authenticated to be allowed access to the data, they reject the unauthenticated user of your app.
To solve this problem, you'll need to authenticate the user. The simplest way to do that is to sign the user in anonymously:
firebase.auth().signInAnonymously();
Then attach your database listeners after the user is authenticated:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
this.listenForItems(this.itemsRef);
} else {
// User is signed out.
// ...
}
// ...
});

Resources