implement phone verification (#274)
* implement phone verification * update permissions in buildozer.spec
92
app/package-lock.json
generated
|
@ -3360,6 +3360,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fuse.js": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-2.6.2.tgz",
|
||||||
|
"integrity": "sha1-1dmU/alvVDtaUd84tyzsnMYNneo="
|
||||||
|
},
|
||||||
"gauge": {
|
"gauge": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
|
||||||
|
@ -3431,6 +3436,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
||||||
},
|
},
|
||||||
|
"google-libphonenumber": {
|
||||||
|
"version": "2.0.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-2.0.19.tgz",
|
||||||
|
"integrity": "sha512-kwtbruT+eyiof081cxT1tltMTxgTOq3CQhUoEYBROC+vNf+COPqzfKJtVnDvgXQe4SzfbnAYkP8KoSpbJBIlSg=="
|
||||||
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.1.11",
|
"version": "4.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
||||||
|
@ -3980,21 +3990,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lbryinc": {
|
"lbryinc": {
|
||||||
"version": "github:lbryio/lbryinc#678c5098e2099dd1560b2fefa2795f38ca3ce07b",
|
"version": "github:lbryio/lbryinc#7910b565d7edda16be1c9d291f296982261ba60a",
|
||||||
"from": "github:lbryio/lbryinc",
|
"from": "github:lbryio/lbryinc#phone-verification",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lbry-redux": "github:lbryio/lbry-redux#421321a78397251589e5a890f4caa95e79975e2b",
|
"lbry-redux": "github:lbryio/lbry-redux#31f7afa8a37f5741dac01fc1ecdf153f3bed95dc",
|
||||||
"reselect": "^3.0.0"
|
"reselect": "^3.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"lbry-redux": {
|
|
||||||
"version": "github:lbryio/lbry-redux#421321a78397251589e5a890f4caa95e79975e2b",
|
|
||||||
"from": "github:lbryio/lbry-redux",
|
|
||||||
"requires": {
|
|
||||||
"proxy-polyfill": "0.1.6",
|
|
||||||
"reselect": "^3.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lcid": {
|
"lcid": {
|
||||||
|
@ -4098,6 +4098,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||||
},
|
},
|
||||||
|
"lodash.toarray": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||||
|
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
|
||||||
|
},
|
||||||
"lodash.unset": {
|
"lodash.unset": {
|
||||||
"version": "4.5.2",
|
"version": "4.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.unset/-/lodash.unset-4.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.unset/-/lodash.unset-4.5.2.tgz",
|
||||||
|
@ -4564,6 +4569,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
||||||
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
||||||
},
|
},
|
||||||
|
"node-emoji": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-+ktMAh1Jwas+TnGodfCfjUbJKoANqPaJFN0z0iqh41eqD8dvguNzcitVSBSVK1pidz0AqGbLKcoVuVLRVZ/aVg==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.toarray": "^4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "1.7.3",
|
"version": "1.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||||
|
@ -5200,6 +5213,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-country-picker-modal": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-country-picker-modal/-/react-native-country-picker-modal-0.6.2.tgz",
|
||||||
|
"integrity": "sha1-upcRi+Q3O+DBHNUeRF5r1Eji8co=",
|
||||||
|
"requires": {
|
||||||
|
"fuse.js": "2.6.2",
|
||||||
|
"lodash": "4.12.0",
|
||||||
|
"node-emoji": "1.8.1",
|
||||||
|
"prop-types": "15.6.0",
|
||||||
|
"react-native-safe-area-view": "^0.7.0",
|
||||||
|
"world-countries": "1.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.12.0",
|
||||||
|
"resolved": "http://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz",
|
||||||
|
"integrity": "sha1-K9bcRqBA9Z5obJcu0h2T3FkFMlg="
|
||||||
|
},
|
||||||
|
"prop-types": {
|
||||||
|
"version": "15.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz",
|
||||||
|
"integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=",
|
||||||
|
"requires": {
|
||||||
|
"fbjs": "^0.8.16",
|
||||||
|
"loose-envify": "^1.3.1",
|
||||||
|
"object-assign": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-native-safe-area-view": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-SjLdW/Th0WVMhyngH4O6yC21S+O4U4AAG3QxBr7fZ2ftgjXSpKbDHAhEpxBdFwei6HsnsC2h9oYMtPpaW9nfGg==",
|
||||||
|
"requires": {
|
||||||
|
"hoist-non-react-statics": "^2.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-native-dismiss-keyboard": {
|
"react-native-dismiss-keyboard": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz",
|
||||||
|
@ -5263,6 +5314,16 @@
|
||||||
"react-native-image-pan-zoom": "^2.1.2"
|
"react-native-image-pan-zoom": "^2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-phone-input": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-phone-input/-/react-native-phone-input-0.2.1.tgz",
|
||||||
|
"integrity": "sha1-rGhSoeo32NWP+D3tUtGNe2MD5mc=",
|
||||||
|
"requires": {
|
||||||
|
"google-libphonenumber": "^2.0.9",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"prop-types": "^15.5.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-native-safe-area-view": {
|
"react-native-safe-area-view": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.9.0.tgz",
|
||||||
|
@ -6837,6 +6898,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
||||||
},
|
},
|
||||||
|
"world-countries": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/world-countries/-/world-countries-1.8.0.tgz",
|
||||||
|
"integrity": "sha1-F/SOfoRwrFohNq1pON/GVvwry5U="
|
||||||
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base-64": "^0.1.0",
|
"base-64": "^0.1.0",
|
||||||
"lbry-redux": "lbryio/lbry-redux",
|
"lbry-redux": "lbryio/lbry-redux",
|
||||||
"lbryinc": "lbryio/lbryinc",
|
"lbryinc": "lbryio/lbryinc#phone-verification",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
"react": "16.2.0",
|
"react": "16.2.0",
|
||||||
"react-native": "0.55.3",
|
"react-native": "0.55.3",
|
||||||
|
"react-native-country-picker-modal": "^0.6.2",
|
||||||
"react-native-fast-image": "^5.0.3",
|
"react-native-fast-image": "^5.0.3",
|
||||||
"react-native-fetch-blob": "^0.10.8",
|
"react-native-fetch-blob": "^0.10.8",
|
||||||
"react-native-image-zoom-viewer": "^2.2.5",
|
"react-native-image-zoom-viewer": "^2.2.5",
|
||||||
|
"react-native-phone-input": "^0.2.1",
|
||||||
"react-native-vector-icons": "^5.0.0",
|
"react-native-vector-icons": "^5.0.0",
|
||||||
"react-native-video": "lbryio/react-native-video#exoplayer-lbry-android",
|
"react-native-video": "lbryio/react-native-video#exoplayer-lbry-android",
|
||||||
"react-navigation": "^2.12.1",
|
"react-navigation": "^2.12.1",
|
||||||
|
|
|
@ -268,7 +268,7 @@ class AppWithNavigationState extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('toast' === currentDisplayType) {
|
if ('toast' === currentDisplayType) {
|
||||||
ToastAndroid.show(message, ToastAndroid.SHORT);
|
ToastAndroid.show(message, ToastAndroid.LONG);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(doHideNotification());
|
dispatch(doHideNotification());
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { doNotify } from 'lbry-redux';
|
|
||||||
import DeviceIdRewardSubcard from './view';
|
|
||||||
|
|
||||||
const perform = dispatch => ({
|
|
||||||
notify: data => dispatch(doNotify(data))
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(null, perform)(DeviceIdRewardSubcard);
|
|
|
@ -1,48 +0,0 @@
|
||||||
// @flow
|
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
ActivityIndicator,
|
|
||||||
AsyncStorage,
|
|
||||||
NativeModules,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
TouchableOpacity,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
|
||||||
import Button from '../button';
|
|
||||||
import Colors from '../../styles/colors';
|
|
||||||
import Constants from '../../constants';
|
|
||||||
import Link from '../link';
|
|
||||||
import rewardStyle from '../../styles/reward';
|
|
||||||
|
|
||||||
class DeviceIdRewardSubcard extends React.PureComponent {
|
|
||||||
onAllowAccessPressed = () => {
|
|
||||||
if (!NativeModules.UtilityModule) {
|
|
||||||
return notify({
|
|
||||||
message: 'The device ID could not be obtained due to a missing module.',
|
|
||||||
displayType: ['toast']
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeModules.UtilityModule.requestPhoneStatePermission();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<View style={rewardStyle.subcard}>
|
|
||||||
<Text style={rewardStyle.subtitle}>Pending action: Device ID</Text>
|
|
||||||
<Text style={[rewardStyle.bottomMarginMedium, rewardStyle.subcardText]}>
|
|
||||||
The app requires the phone state permission in order to identify your device for reward eligibility.
|
|
||||||
</Text>
|
|
||||||
<Button
|
|
||||||
style={rewardStyle.actionButton}
|
|
||||||
text={"Allow Access"}
|
|
||||||
onPress={this.onAllowAccessPressed}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceIdRewardSubcard;
|
|
|
@ -86,9 +86,10 @@ class EmailRewardSubcard extends React.PureComponent {
|
||||||
value={this.state.email}
|
value={this.state.email}
|
||||||
onChangeText={text => this.handleChangeText(text)} />
|
onChangeText={text => this.handleChangeText(text)} />
|
||||||
{!this.state.verifyStarted && <Button style={rewardStyle.actionButton}
|
{!this.state.verifyStarted && <Button style={rewardStyle.actionButton}
|
||||||
text={"Send Verification Email"}
|
text={"Send verification email"}
|
||||||
onPress={this.onSendVerificationPressed} />}
|
onPress={this.onSendVerificationPressed} />}
|
||||||
{this.state.verifyStarted && emailNewPending && <ActivityIndicator size={"small"} color={Colors.LbryGreen} />}
|
{this.state.verifyStarted && emailNewPending &&
|
||||||
|
<ActivityIndicator size={"small"} color={Colors.LbryGreen} style={rewardStyle.loading} />}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
28
app/src/component/phoneNumberRewardSubcard/index.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doNotify } from 'lbry-redux';
|
||||||
|
import {
|
||||||
|
doUserPhoneNew,
|
||||||
|
doUserPhoneVerify,
|
||||||
|
selectPhoneNewErrorMessage,
|
||||||
|
selectPhoneNewIsPending,
|
||||||
|
selectPhoneToVerify,
|
||||||
|
selectPhoneVerifyIsPending,
|
||||||
|
selectPhoneVerifyErrorMessage
|
||||||
|
} from 'lbryinc';
|
||||||
|
import PhoneNumberRewardSubcard from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
phoneVerifyErrorMessage: selectPhoneVerifyErrorMessage(state),
|
||||||
|
phoneVerifyIsPending: selectPhoneVerifyIsPending(state),
|
||||||
|
phone: selectPhoneToVerify(state),
|
||||||
|
phoneNewErrorMessage: selectPhoneNewErrorMessage(state),
|
||||||
|
phoneNewIsPending: selectPhoneNewIsPending(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)),
|
||||||
|
verifyPhone: (verificationCode) => dispatch(doUserPhoneVerify(verificationCode)),
|
||||||
|
notify: data => dispatch(doNotify(data)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(PhoneNumberRewardSubcard);
|
253
app/src/component/phoneNumberRewardSubcard/view.js
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
AsyncStorage,
|
||||||
|
DeviceEventEmitter,
|
||||||
|
NativeModules,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
import Button from '../button';
|
||||||
|
import Colors from '../../styles/colors';
|
||||||
|
import Constants from '../../constants';
|
||||||
|
import CountryPicker from 'react-native-country-picker-modal';
|
||||||
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
|
import Link from '../link';
|
||||||
|
import PhoneInput from 'react-native-phone-input';
|
||||||
|
import rewardStyle from '../../styles/reward';
|
||||||
|
|
||||||
|
class PhoneNumberRewardSubcard extends React.PureComponent {
|
||||||
|
phoneInput = null;
|
||||||
|
|
||||||
|
picker = null;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
canReceiveSms: false,
|
||||||
|
cca2: 'US',
|
||||||
|
codeVerifyStarted: false,
|
||||||
|
codeVerifySuccessful: false,
|
||||||
|
countryCode: null,
|
||||||
|
newPhoneAdded: false,
|
||||||
|
number: null,
|
||||||
|
verificationCode: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
DeviceEventEmitter.addListener('onReceiveSmsPermissionGranted', this.receiveSmsPermissionGranted);
|
||||||
|
DeviceEventEmitter.addListener('onVerificationCodeReceived', this.receiveVerificationCode);
|
||||||
|
|
||||||
|
const { phone } = this.props;
|
||||||
|
if (phone && String(phone).trim().length() > 0) {
|
||||||
|
this.setState({ newPhoneAdded: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NativeModules.UtilityModule) {
|
||||||
|
NativeModules.UtilityModule.canReceiveSms().then(canReceiveSms => this.setState({ canReceiveSms }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
DeviceEventEmitter.removeListener('onReceiveSmsPermissionGranted', this.receiveSmsPermissionGranted);
|
||||||
|
DeviceEventEmitter.removeListener('onVerificationCodeReceived', this.receiveVerificationCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
phoneVerifyIsPending,
|
||||||
|
phoneVerifyErrorMessage,
|
||||||
|
notify,
|
||||||
|
phoneNewErrorMessage,
|
||||||
|
phoneNewIsPending,
|
||||||
|
onPhoneVerifySuccessful
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (!phoneNewIsPending && (phoneNewIsPending !== prevProps.phoneNewIsPending)) {
|
||||||
|
if (phoneNewErrorMessage) {
|
||||||
|
notify({ message: String(phoneNewErrorMessage), displayType: ['toast'] });
|
||||||
|
} else {
|
||||||
|
this.setState({ newPhoneAdded: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!phoneVerifyIsPending && (phoneVerifyIsPending !== prevProps.phoneVerifyIsPending)) {
|
||||||
|
if (phoneVerifyErrorMessage) {
|
||||||
|
notify({ message: String(phoneVerifyErrorMessage), displayType: ['toast'] });
|
||||||
|
this.setState({ codeVerifyStarted: false });
|
||||||
|
} else {
|
||||||
|
notify({ message: 'Your phone number was successfully verified.', displayType: ['toast'] });
|
||||||
|
this.setState({ codeVerifySuccessful: true });
|
||||||
|
if (onPhoneVerifySuccessful) {
|
||||||
|
onPhoneVerifySuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveSmsPermissionGranted = () => {
|
||||||
|
this.setState({ canReceiveSms: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveVerificationCode = (evt) => {
|
||||||
|
if (!this.state.newPhoneAdded || this.state.codeVerifySuccessful) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { verifyPhone } = this.props;
|
||||||
|
this.setState({ codeVerifyStarted: true });
|
||||||
|
verifyPhone(evt.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAllowAccessPressed = () => {
|
||||||
|
if (!NativeModules.UtilityModule) {
|
||||||
|
return notify({
|
||||||
|
message: 'The required permission could not be obtained due to a missing module.',
|
||||||
|
displayType: ['toast']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeModules.UtilityModule.requestReceiveSmsPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendTextPressed = () => {
|
||||||
|
const { addUserPhone, notify } = this.props;
|
||||||
|
|
||||||
|
if (!this.phoneInput.isValidNumber()) {
|
||||||
|
return notify({
|
||||||
|
message: 'Please provide a valid telephone number.',
|
||||||
|
displayType: ['toast']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const countryCode = this.phoneInput.getCountryCode();
|
||||||
|
const number = this.phoneInput.getValue().replace('+' + countryCode, '');
|
||||||
|
this.setState({ countryCode, number });
|
||||||
|
addUserPhone(number, countryCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
onVerifyPressed = () => {
|
||||||
|
if (this.state.codeVerifyStarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { verifyPhone } = this.props;
|
||||||
|
this.setState({ codeVerifyStarted: true });
|
||||||
|
verifyPhone(this.state.verificationCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressFlag = () => {
|
||||||
|
if (this.picker) {
|
||||||
|
this.picker.openModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCountry(country) {
|
||||||
|
this.phoneInput.selectCountry(country.cca2.toLowerCase());
|
||||||
|
this.setState({ cca2: country.cca2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChangeText = (text) => {
|
||||||
|
this.setState({ verificationCode: text });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
phoneVerifyIsPending,
|
||||||
|
phoneVerifyErrorMessage,
|
||||||
|
phone,
|
||||||
|
phoneErrorMessage,
|
||||||
|
phoneNewIsPending
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (this.state.codeVerifySuccessful) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={rewardStyle.subcard}>
|
||||||
|
<Text style={rewardStyle.subtitle}>Pending action: Verify Phone Number</Text>
|
||||||
|
{!this.state.canReceiveSms &&
|
||||||
|
<View style={rewardStyle.smsPermissionContainer}>
|
||||||
|
<Text style={[rewardStyle.bottomMarginMedium, rewardStyle.subcardText]}>
|
||||||
|
You can grant access to the receive SMS permission in order to verify phone number. Alternatively, you can enter the verification code manually.
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
style={rewardStyle.actionButton}
|
||||||
|
text={"Allow access"}
|
||||||
|
onPress={this.onAllowAccessPressed}
|
||||||
|
/>
|
||||||
|
</View>}
|
||||||
|
<View style={rewardStyle.phoneVerificationContainer}>
|
||||||
|
{!this.state.newPhoneAdded &&
|
||||||
|
<View>
|
||||||
|
<Text style={[rewardStyle.bottomMarginMedium, rewardStyle.subcardText]}>Please enter your phone number to continue.</Text>
|
||||||
|
<PhoneInput
|
||||||
|
ref={(ref) => { this.phoneInput = ref; }}
|
||||||
|
style={StyleSheet.flatten(rewardStyle.phoneInput)}
|
||||||
|
textProps={{ placeholder: '(phone number)' }}
|
||||||
|
textStyle={StyleSheet.flatten(rewardStyle.phoneInputText)}
|
||||||
|
onPressFlag={this.onPressFlag} />
|
||||||
|
{!phoneNewIsPending &&
|
||||||
|
<Button
|
||||||
|
style={[rewardStyle.actionButton, rewardStyle.topMarginMedium]}
|
||||||
|
text={"Send verification text"}
|
||||||
|
onPress={this.onSendTextPressed} />}
|
||||||
|
{phoneNewIsPending &&
|
||||||
|
<ActivityIndicator
|
||||||
|
style={[rewardStyle.loading, rewardStyle.topMarginMedium]}
|
||||||
|
size="small"
|
||||||
|
color={Colors.LbryGreen} />}
|
||||||
|
</View>}
|
||||||
|
{this.state.newPhoneAdded &&
|
||||||
|
<View>
|
||||||
|
{!phoneVerifyIsPending && !this.codeVerifyStarted &&
|
||||||
|
<View>
|
||||||
|
<Text style={[rewardStyle.bottomMarginSmall, rewardStyle.subcardText]}>
|
||||||
|
Please enter the verification code.
|
||||||
|
</Text>
|
||||||
|
<TextInput
|
||||||
|
style={rewardStyle.verificationCodeInput}
|
||||||
|
keyboardType="numeric"
|
||||||
|
placeholder="0000"
|
||||||
|
underlineColorAndroid="transparent"
|
||||||
|
value={this.state.verificationCode}
|
||||||
|
onChangeText={text => this.handleChangeText(text)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={[rewardStyle.actionButton, rewardStyle.topMarginSmall ]}
|
||||||
|
text={"Verify"}
|
||||||
|
onPress={this.onVerifyPressed} />
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
{phoneVerifyIsPending &&
|
||||||
|
<View>
|
||||||
|
<Text style={rewardStyle.subcardText}>Verifying your phone number...</Text>
|
||||||
|
<ActivityIndicator
|
||||||
|
color={Colors.LbryGreen}
|
||||||
|
size="small"
|
||||||
|
style={[rewardStyle.loading, rewardStyle.topMarginMedium]} />
|
||||||
|
</View>}
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<CountryPicker
|
||||||
|
ref={(picker) => { this.picker = picker; }}
|
||||||
|
cca2={this.state.cca2}
|
||||||
|
filterable={true}
|
||||||
|
onChange={value => this.selectCountry(value)}
|
||||||
|
showCallingCode={true}
|
||||||
|
translation="eng">
|
||||||
|
<View />
|
||||||
|
</CountryPicker>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PhoneNumberRewardSubcard;
|
|
@ -13,24 +13,14 @@ class RewardSummary extends React.Component {
|
||||||
const { user } = this.props;
|
const { user } = this.props;
|
||||||
let actionsLeft = 0;
|
let actionsLeft = 0;
|
||||||
if (!user || !user.has_verified_email) {
|
if (!user || !user.has_verified_email) {
|
||||||
actionsLeft++;
|
actionsLeft++;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ actionsLeft }, () => {
|
if (!user || !user.is_identity_verified) {
|
||||||
if (NativeModules.UtilityModule) {
|
actionsLeft++;
|
||||||
NativeModules.UtilityModule.canAcquireDeviceId().then(canAcquire => {
|
}
|
||||||
if (!canAcquire) {
|
|
||||||
this.setState({ actionsLeft: this.state.actionsLeft + 1 });
|
this.setState({ actionsLeft });
|
||||||
return;
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
this.setState({ actionsLeft: this.state.actionsLeft + 1 });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// unable to retrieve device ID because the native module is not present.
|
|
||||||
this.setState({ actionsLeft: this.state.actionsLeft + 1 });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
DeviceEventEmitter,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
NativeModules,
|
NativeModules,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
|
@ -11,7 +10,7 @@ import {
|
||||||
import { doInstallNew } from 'lbryinc';
|
import { doInstallNew } from 'lbryinc';
|
||||||
import Colors from '../../styles/colors';
|
import Colors from '../../styles/colors';
|
||||||
import Link from '../../component/link';
|
import Link from '../../component/link';
|
||||||
import DeviceIdRewardSubcard from '../../component/deviceIdRewardSubcard';
|
import PhoneNumberRewardSubcard from '../../component/phoneNumberRewardSubcard';
|
||||||
import EmailRewardSubcard from '../../component/emailRewardSubcard';
|
import EmailRewardSubcard from '../../component/emailRewardSubcard';
|
||||||
import PageHeader from '../../component/pageHeader';
|
import PageHeader from '../../component/pageHeader';
|
||||||
import RewardCard from '../../component/rewardCard';
|
import RewardCard from '../../component/rewardCard';
|
||||||
|
@ -19,33 +18,21 @@ import rewardStyle from '../../styles/reward';
|
||||||
|
|
||||||
class RewardsPage extends React.PureComponent {
|
class RewardsPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
canAcquireDeviceId: false,
|
|
||||||
isEmailVerified: false,
|
isEmailVerified: false,
|
||||||
|
isIdentityVerified: false,
|
||||||
isRewardApproved: false,
|
isRewardApproved: false,
|
||||||
verifyRequestStarted: false,
|
verifyRequestStarted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
DeviceEventEmitter.addListener('onPhoneStatePermissionGranted', this.phoneStatePermissionGranted);
|
|
||||||
|
|
||||||
this.props.fetchRewards();
|
this.props.fetchRewards();
|
||||||
|
|
||||||
const { user } = this.props;
|
const { user } = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
isEmailVerified: (user && user.primary_email && user.has_verified_email),
|
isEmailVerified: (user && user.primary_email && user.has_verified_email),
|
||||||
|
isIdentityVerified: (user && user.is_identity_verified),
|
||||||
isRewardApproved: (user && user.is_reward_approved)
|
isRewardApproved: (user && user.is_reward_approved)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (NativeModules.UtilityModule) {
|
|
||||||
const util = NativeModules.UtilityModule;
|
|
||||||
util.canAcquireDeviceId().then(canAcquireDeviceId => {
|
|
||||||
this.setState({ canAcquireDeviceId });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
DeviceEventEmitter.removeListener('onPhoneStatePermissionGranted', this.phoneStatePermissionGranted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -67,13 +54,24 @@ class RewardsPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderVerification() {
|
renderVerification() {
|
||||||
if (!this.state.isRewardApproved) {
|
if (!this.state.isEmailVerified || !this.state.isIdentityVerified) {
|
||||||
return (
|
return (
|
||||||
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
||||||
<Text style={rewardStyle.title}>Humans Only</Text>
|
<Text style={rewardStyle.title}>Humans Only</Text>
|
||||||
<Text style={rewardStyle.text}>Rewards are for human beings only. You'll have to prove you're one of us before you can claim any rewards.</Text>
|
<Text style={rewardStyle.text}>Rewards are for human beings only. You'll have to prove you're one of us before you can claim any rewards.</Text>
|
||||||
{!this.state.canAcquireDeviceId && <DeviceIdRewardSubcard />}
|
|
||||||
{!this.state.isEmailVerified && <EmailRewardSubcard />}
|
{!this.state.isEmailVerified && <EmailRewardSubcard />}
|
||||||
|
{!this.state.isIdentityVerified && <PhoneNumberRewardSubcard />}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.isEmailVerified && this.state.isIdentityVerified && !this.state.isRewardApproved) {
|
||||||
|
return (
|
||||||
|
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
||||||
|
<Text style={rewardStyle.title}>Manual Reward Verification</Text>
|
||||||
|
<Text style={rewardStyle.text}>
|
||||||
|
You need to be manually verified before you can start claiming rewards. Please request to be verified on the <Link style={rewardStyle.textLink} href="https://discordapp.com/invite/Z3bERWA" text="LBRY Discord server" />.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -81,28 +79,6 @@ class RewardsPage extends React.PureComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
phoneStatePermissionGranted = () => {
|
|
||||||
const { install, notify } = this.props;
|
|
||||||
if (NativeModules.UtilityModule) {
|
|
||||||
const util = NativeModules.UtilityModule;
|
|
||||||
|
|
||||||
// Double-check just to be sure
|
|
||||||
util.canAcquireDeviceId().then(canAcquireDeviceId => {
|
|
||||||
this.setState({ canAcquireDeviceId });
|
|
||||||
if (canAcquireDeviceId) {
|
|
||||||
util.getDeviceId(false).then(deviceId => {
|
|
||||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
|
||||||
doInstallNew(`android-${appVersion}`, deviceId);
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
|
||||||
notify({ message: error, displayType: ['toast'] });
|
|
||||||
this.setState({ canAcquireDeviceId: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderUnclaimedRewards() {
|
renderUnclaimedRewards() {
|
||||||
const { claimed, fetching, rewards, user } = this.props;
|
const { claimed, fetching, rewards, user } = this.props;
|
||||||
|
|
||||||
|
@ -123,7 +99,8 @@ class RewardsPage extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<View style={rewardStyle.busyContainer}>
|
<View style={rewardStyle.busyContainer}>
|
||||||
<Text style={rewardStyle.infoText}>
|
<Text style={rewardStyle.infoText}>
|
||||||
{(claimed && claimed.length) ? "You have claimed all available rewards! We're regularly adding more so be sure to check back later." :
|
{(claimed && claimed.length) ?
|
||||||
|
"You have claimed all available rewards! We're regularly adding more so be sure to check back later." :
|
||||||
"There are no rewards available at this time, please check back later."}
|
"There are no rewards available at this time, please check back later."}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -157,13 +134,11 @@ class RewardsPage extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={rewardStyle.container}>
|
<View style={rewardStyle.container}>
|
||||||
{this.renderVerification()}
|
<ScrollView style={rewardStyle.scrollContainer} contentContainerStyle={rewardStyle.scrollContentContainer}>
|
||||||
<View style={rewardStyle.rewardsContainer}>
|
{this.renderVerification()}
|
||||||
<ScrollView style={rewardStyle.scrollContainer} contentContainerStyle={rewardStyle.scrollContentContainer}>
|
{this.renderUnclaimedRewards()}
|
||||||
{this.renderUnclaimedRewards()}
|
{this.renderClaimedRewards()}
|
||||||
{this.renderClaimedRewards()}
|
</ScrollView>
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,12 @@ const rewardStyle = StyleSheet.create({
|
||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
marginBottom: 2
|
marginBottom: 2
|
||||||
},
|
},
|
||||||
|
topMarginSmall: {
|
||||||
|
marginTop: 8
|
||||||
|
},
|
||||||
|
topMarginMedium: {
|
||||||
|
marginTop: 16
|
||||||
|
},
|
||||||
bottomMarginSmall: {
|
bottomMarginSmall: {
|
||||||
marginBottom: 8
|
marginBottom: 8
|
||||||
},
|
},
|
||||||
|
@ -96,6 +102,9 @@ const rewardStyle = StyleSheet.create({
|
||||||
fontFamily: 'Metropolis-Regular',
|
fontFamily: 'Metropolis-Regular',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
|
textLink: {
|
||||||
|
color: Colors.LbryGreen
|
||||||
|
},
|
||||||
leftCol: {
|
leftCol: {
|
||||||
width: '15%',
|
width: '15%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -153,6 +162,35 @@ const rewardStyle = StyleSheet.create({
|
||||||
fontFamily: 'Metropolis-Regular',
|
fontFamily: 'Metropolis-Regular',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 22
|
lineHeight: 22
|
||||||
|
},
|
||||||
|
phoneVerificationContainer: {
|
||||||
|
paddingLeft: 4,
|
||||||
|
paddingRight: 4
|
||||||
|
},
|
||||||
|
phoneInput: {
|
||||||
|
marginLeft: 8
|
||||||
|
},
|
||||||
|
phoneInputText: {
|
||||||
|
fontFamily: 'Metropolis-Regular',
|
||||||
|
fontSize: 16,
|
||||||
|
letterSpacing: 1.3
|
||||||
|
},
|
||||||
|
verifyingText: {
|
||||||
|
fontFamily: 'Metropolis-Regular',
|
||||||
|
fontSize: 14,
|
||||||
|
marginLeft: 12,
|
||||||
|
alignSelf: 'flex-start'
|
||||||
|
},
|
||||||
|
verificationCodeInput: {
|
||||||
|
fontFamily: 'Metropolis-Regular',
|
||||||
|
fontSize: 24,
|
||||||
|
letterSpacing: 12
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
alignSelf: 'flex-start'
|
||||||
|
},
|
||||||
|
smsPermissionContainer: {
|
||||||
|
marginBottom: 32
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ fullscreen = 0
|
||||||
#android.presplash_color = #FFFFFF
|
#android.presplash_color = #FFFFFF
|
||||||
|
|
||||||
# (list) Permissions
|
# (list) Permissions
|
||||||
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
|
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,RECEIVE_SMS,WRITE_EXTERNAL_STORAGE
|
||||||
|
|
||||||
# (int) Android API to use
|
# (int) Android API to use
|
||||||
android.api = 27
|
android.api = 27
|
||||||
|
|
|
@ -86,7 +86,7 @@ fullscreen = 0
|
||||||
#android.presplash_color = #FFFFFF
|
#android.presplash_color = #FFFFFF
|
||||||
|
|
||||||
# (list) Permissions
|
# (list) Permissions
|
||||||
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
|
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,RECEIVE_SMS,WRITE_EXTERNAL_STORAGE
|
||||||
|
|
||||||
# (int) Android API to use
|
# (int) Android API to use
|
||||||
android.api = 27
|
android.api = 27
|
||||||
|
|
|
@ -86,7 +86,7 @@ fullscreen = 0
|
||||||
#android.presplash_color = #FFFFFF
|
#android.presplash_color = #FFFFFF
|
||||||
|
|
||||||
# (list) Permissions
|
# (list) Permissions
|
||||||
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
|
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,RECEIVE_SMS,WRITE_EXTERNAL_STORAGE
|
||||||
|
|
||||||
# (int) Android API to use
|
# (int) Android API to use
|
||||||
android.api = 27
|
android.api = 27
|
||||||
|
|
|
@ -8,10 +8,10 @@ jarsigner -verbose -sigalg SHA1withRSA \
|
||||||
-digestalg SHA1 \
|
-digestalg SHA1 \
|
||||||
-keystore lbry-android.keystore \
|
-keystore lbry-android.keystore \
|
||||||
-storepass $KEYSTORE_PASSWORD \
|
-storepass $KEYSTORE_PASSWORD \
|
||||||
bin/browser-$version-release-unsigned.apk lbry-android \
|
bin/browser-$version-release-unsigned.apk lbry-android > /dev/null \
|
||||||
&& mv bin/browser-$version-release-unsigned.apk bin/browser-$version-release-signed.apk
|
&& mv bin/browser-$version-release-unsigned.apk bin/browser-$version-release-signed.apk
|
||||||
~/.buildozer/android/platform/android-sdk-23/build-tools/26.0.1/zipalign -v 4 \
|
~/.buildozer/android/platform/android-sdk-23/build-tools/26.0.1/zipalign -v 4 \
|
||||||
bin/browser-$version-release-signed.apk bin/browser-$version-release.apk \
|
bin/browser-$version-release-signed.apk bin/browser-$version-release.apk > /dev/null \
|
||||||
&& rm bin/browser-$version-release-signed.apk
|
&& rm bin/browser-$version-release-signed.apk
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import android.provider.Settings;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.app.NotificationManagerCompat;
|
import android.support.v4.app.NotificationManagerCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.telephony.SmsMessage;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -25,7 +26,9 @@ import com.facebook.react.common.LifecycleState;
|
||||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||||
import com.facebook.react.ReactRootView;
|
import com.facebook.react.ReactRootView;
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||||
|
@ -52,10 +55,14 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
||||||
|
|
||||||
private static final int PHONE_STATE_PERMISSION_REQ_CODE = 202;
|
private static final int PHONE_STATE_PERMISSION_REQ_CODE = 202;
|
||||||
|
|
||||||
private BroadcastReceiver stopServiceReceiver;
|
private static final int RECEIVE_SMS_PERMISSION_REQ_CODE = 203;
|
||||||
|
|
||||||
private BroadcastReceiver backgroundMediaReceiver;
|
private BroadcastReceiver backgroundMediaReceiver;
|
||||||
|
|
||||||
|
private BroadcastReceiver smsReceiver;
|
||||||
|
|
||||||
|
private BroadcastReceiver stopServiceReceiver;
|
||||||
|
|
||||||
private ReactRootView mReactRootView;
|
private ReactRootView mReactRootView;
|
||||||
|
|
||||||
private ReactInstanceManager mReactInstanceManager;
|
private ReactInstanceManager mReactInstanceManager;
|
||||||
|
@ -96,6 +103,9 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
||||||
// Register the stop service receiver (so that we close the activity if the user requests the service to stop)
|
// Register the stop service receiver (so that we close the activity if the user requests the service to stop)
|
||||||
registerStopReceiver();
|
registerStopReceiver();
|
||||||
|
|
||||||
|
// Register SMS receiver for handling verification texts
|
||||||
|
registerSmsReceiver();
|
||||||
|
|
||||||
// Start the daemon service if it is not started
|
// Start the daemon service if it is not started
|
||||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||||
if (!serviceRunning) {
|
if (!serviceRunning) {
|
||||||
|
@ -159,6 +169,48 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
||||||
registerReceiver(backgroundMediaReceiver, backgroundMediaFilter);
|
registerReceiver(backgroundMediaReceiver, backgroundMediaFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerSmsReceiver() {
|
||||||
|
if (!hasPermission(Manifest.permission.RECEIVE_SMS, this)) {
|
||||||
|
// don't create the receiver if we don't have the read sms permission
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntentFilter smsFilter = new IntentFilter();
|
||||||
|
smsFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
|
||||||
|
smsReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
// Get the message
|
||||||
|
Bundle bundle = intent.getExtras();
|
||||||
|
if (bundle != null) {
|
||||||
|
Object[] pdus = (Object[]) bundle.get("pdus");
|
||||||
|
if (pdus != null && pdus.length > 0) {
|
||||||
|
SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdus[0]);
|
||||||
|
String text = sms.getMessageBody();
|
||||||
|
if (text == null || text.trim().length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve verification code from the text message if it contains
|
||||||
|
// the strings "lbry", "verification code" and the colon (following the expected format)
|
||||||
|
text = text.toLowerCase();
|
||||||
|
if (text.indexOf("lbry") > -1 && text.indexOf("verification code") > -1 && text.indexOf(":") > -1) {
|
||||||
|
String code = text.substring(text.lastIndexOf(":") + 1).trim();
|
||||||
|
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||||
|
if (reactContext != null) {
|
||||||
|
WritableMap params = Arguments.createMap();
|
||||||
|
params.putString("code", code);
|
||||||
|
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||||
|
.emit("onVerificationCodeReceived", params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
registerReceiver(smsReceiver, smsFilter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
|
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
|
||||||
|
@ -212,6 +264,27 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
||||||
"No permission granted to read your device state. Rewards cannot be claimed.", Toast.LENGTH_LONG).show();
|
"No permission granted to read your device state. Rewards cannot be claimed.", Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RECEIVE_SMS_PERMISSION_REQ_CODE:
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Permission granted. Emit an onPhoneStatePermissionGranted event
|
||||||
|
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||||
|
if (reactContext != null) {
|
||||||
|
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||||
|
.emit("onReceiveSmsPermissionGranted", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the receiver
|
||||||
|
if (smsReceiver == null) {
|
||||||
|
registerSmsReceiver();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Permission not granted. Simply show a message.
|
||||||
|
Toast.makeText(this,
|
||||||
|
"No permission granted to receive your SMS messages. You may have to enter the verification code manually.",
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +365,11 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
||||||
backgroundMediaReceiver = null;
|
backgroundMediaReceiver = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (smsReceiver != null) {
|
||||||
|
unregisterReceiver(smsReceiver);
|
||||||
|
smsReceiver = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (stopServiceReceiver != null) {
|
if (stopServiceReceiver != null) {
|
||||||
unregisterReceiver(stopServiceReceiver);
|
unregisterReceiver(stopServiceReceiver);
|
||||||
stopServiceReceiver = null;
|
stopServiceReceiver = null;
|
||||||
|
@ -358,6 +436,15 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkReceiveSmsPermission(Context context) {
|
||||||
|
// Request read phone state permission
|
||||||
|
checkPermission(Manifest.permission.RECEIVE_SMS,
|
||||||
|
RECEIVE_SMS_PERMISSION_REQ_CODE,
|
||||||
|
"LBRY requires access to be able to read a verification text message for rewards.",
|
||||||
|
context,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isServiceRunning(Class<?> serviceClass) {
|
private boolean isServiceRunning(Class<?> serviceClass) {
|
||||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||||
|
|
|
@ -109,9 +109,9 @@ public class UtilityModule extends ReactContextBaseJavaModule {
|
||||||
}
|
}
|
||||||
} catch (SecurityException ex) {
|
} catch (SecurityException ex) {
|
||||||
// Maybe the permission was not granted? Try to acquire permission
|
// Maybe the permission was not granted? Try to acquire permission
|
||||||
if (requestPermission) {
|
/*if (requestPermission) {
|
||||||
requestPhoneStatePermission();
|
requestPhoneStatePermission();
|
||||||
}
|
}*/
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// id could not be obtained. Display a warning that rewards cannot be claimed.
|
// id could not be obtained. Display a warning that rewards cannot be claimed.
|
||||||
promise.reject(ex.getMessage());
|
promise.reject(ex.getMessage());
|
||||||
|
@ -126,20 +126,16 @@ public class UtilityModule extends ReactContextBaseJavaModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void canAcquireDeviceId(final Promise promise) {
|
public void canReceiveSms(final Promise promise) {
|
||||||
if (isEmulator()) {
|
promise.resolve(MainActivity.hasPermission(Manifest.permission.RECEIVE_SMS, MainActivity.getActivity()));
|
||||||
promise.resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.resolve(MainActivity.hasPermission(Manifest.permission.READ_PHONE_STATE, MainActivity.getActivity()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void requestPhoneStatePermission() {
|
public void requestReceiveSmsPermission() {
|
||||||
MainActivity activity = (MainActivity) MainActivity.getActivity();
|
MainActivity activity = (MainActivity) MainActivity.getActivity();
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
// Request for the READ_PHONE_STATE permission
|
// Request for the RECEIVE_SMS permission
|
||||||
MainActivity.checkPhoneStatePermission(activity);
|
MainActivity.checkReceiveSmsPermission(activity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 494 B |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 451 B |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 225 B |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 683 B |
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 332 B |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 265 B |
After Width: | Height: | Size: 437 B |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 523 B |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 370 B |
After Width: | Height: | Size: 540 B |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 560 B |
After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 7 KiB |
After Width: | Height: | Size: 645 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 421 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 481 B |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 540 B |
After Width: | Height: | Size: 610 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 588 B |