ReactJS: In App Purchase using in-app-purchase-2 not working - ios

I have:
import { InAppPurchase2 as iap, IAPProduct } from "#ionic-native/in-app-purchase-2";
import { isPlatform } from '#ionic/react';
const IOS: React.FC = () => {
let history = useHistory();
const [productPrice, setPrice] = useState('')
const [productPriceA, setPriceA] = useState('')
const [product, setProduct] = useState([])
const [productA, setProductA] = useState([])
iap.validator = "https://validator.fovea.cc/v1/validate?###";
//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
}, {
id: "tpannual",
alias: "Annual",
type: iap.PAID_SUBSCRIPTION
},
]);
iap.ready(() => {
let product = iap.get('Monthly');
let productA = iap.get('Annual');
setPrice(product.price)
setProduct(product)
setPriceA(productA.price)
setProductA(productA)
})
iap.refresh();
}
}
//if user clicks purchase button
const purchaseProduct = () => {
if (product.owned) {
alert('A subscription is currently active.')
} else {
iap.order("tpmonthly").then(() => {
iap.when("tpmonthly").approved((p: IAPProduct) => {
p.verify();
p.finish();
});
history.push("/ios-signup/");
})
}
p.refresh();
}
//if user clicks purchase button
const purchaseProductA = () => {
if (product.owned) {
alert('A subscription is currently active.')
} else {
iap.order("tpannual").then(() => {
iap.when("tpannual").approved((p: IAPProduct) => {
p.verify();
p.finish();
});
history.push("/ios-signup/");
})
}
p.refresh();
}
return (
<>
<div className="wrapper" style={{ textAlign: 'center' }}>
<br/><br/>
<Button size="large" style={{ backgroundColor: '#202020', color: '#fff', width: '100%' }} variant="contained" onClick={purchaseProduct}>Subscribe Monthly for {productPrice}</Button><br/><br/>
<Button size="large" style={{ backgroundColor: '#202020', color: '#fff', width: '100%' }} variant="contained" onClick={purchaseProductA}>Subscribe Annually for {productPriceA}</Button><br/><br/>
</div>
</>
);
};
It displays the price of each subscription in the Button and when clicking asks me for my Apple login - but after completing this I get an error in Xcode with :
[store.js] ERROR: ios -> ERROR 6777010: An unknown error occurred - {"productId":"tpmonthly"}
Followed by:
[store.js] state: tpmonthly -> valid
So the subscription does not go through and does not show up in fovea.
Am pulling my hair out - is there anything I can do?

Related

ReactNative ble manager is not reading data from peripheral on IOS

I am building a ReactNative application for both IOS and Android platforms. My app needs to read data from another device via BLE communication. I am using this package for implementing BLE communication, https://github.com/innoveit/react-native-ble-manager. I am having a problem with receiving characteristic data on IOS even though it is working as expected on the Android platform.
I have added the following to the info.plist file:
Privacy - Bluetooth Always Usage Description: App needs to use Bluetooth to receive data from WaterRower machine
I have a component that scan and list down the BLE devices as follow. It is called, BleDeviceList.js
import React, {
useState,
useEffect,
} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
NativeModules,
NativeEventEmitter,
Button,
Platform,
PermissionsAndroid,
FlatList,
TouchableHighlight,
} from 'react-native';
import {
Colors,
} from 'react-native/Libraries/NewAppScreen';
import BleManager from 'react-native-ble-manager';
const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);
const BleDeviceList = (props) => {
const [isScanning, setIsScanning] = useState(false);
const peripherals = new Map();
const [list, setList] = useState([]);
const [ connectedDevices, setConnectedDevices ] = useState([ ]);
const [ permissionsAllowed, setPermissionsAllowed ] = useState(false)
const startScan = () => {
if (!isScanning) {
BleManager.scan([], 3, true).then((results) => {
console.log('Scanning...');
setIsScanning(true);
}).catch(err => {
console.error(err);
});
}
}
const handleStopScan = () => {
console.log('Scan is stopped');
setIsScanning(false);
}
const handleDisconnectedPeripheral = (data) => {
let peripheral = peripherals.get(data.peripheral);
if (peripheral) {
peripheral.connected = false;
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
console.log('Disconnected from ' + data.peripheral);
}
const handleUpdateValueForCharacteristic = (data) => {
console.log('Received data from ' + data.peripheral + ' characteristic ' + data.characteristic, data.value);
}
const retrieveConnected = () => {
BleManager.getConnectedPeripherals([]).then((results) => {
if (results.length == 0) {
console.log('No connected peripherals')
}
console.log(results);
for (var i = 0; i < results.length; i++) {
var peripheral = results[i];
peripheral.connected = true;
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
});
}
const handleDiscoverPeripheral = (peripheral) => {
console.log('Got ble peripheral', peripheral);
if (!peripheral.name) {
peripheral.name = 'NO NAME';
}
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
const isConnected = (peripheral) => {
return connectedDevices.filter(cd => cd.id == peripheral.id).length > 0;
}
const toggleConnectPeripheral = (peripheral) => {
if (peripheral){
if (isConnected(peripheral)){
BleManager.disconnect(peripheral.id);
setConnectedDevices(connectedDevices.filter(cd => cd.id != peripheral.id))
}else{
BleManager.connect(peripheral.id).then(() => {
let tempConnnectedDevices = [ ...connectedDevices ]
tempConnnectedDevices.push(peripheral);
setConnectedDevices(tempConnnectedDevices);
props.navigation.push('BleRowingSession', { peripheral: peripheral });
let p = peripherals.get(peripheral.id);
if (p) {
p.connected = true;
peripherals.set(peripheral.id, p);
setList(Array.from(peripherals.values()));
props.navigation.push('BleDeviceServiceList', { peripheral: peripheral });
}
console.log('Connected to ' + peripheral.id);
}).catch((error) => {
console.log('Connection error', error);
});
}
}
}
useEffect(() => {
BleManager.start({showAlert: false});
bleManagerEmitter.addListener('BleManagerDiscoverPeripheral', handleDiscoverPeripheral);
bleManagerEmitter.addListener('BleManagerStopScan', handleStopScan );
bleManagerEmitter.addListener('BleManagerDisconnectPeripheral', handleDisconnectedPeripheral );
bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic );
if (Platform.OS === 'android' && Platform.Version >= 23) {
PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION).then((result) => {
if (result) {
console.log("Permission is OK");
setPermissionsAllowed(true);
} else {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION).then((result) => {
if (result) {
console.log("User accept");
setPermissionsAllowed(true);
} else {
console.log("User refuse");
setPermissionsAllowed(false);
}
});
}
});
} else {
setPermissionsAllowed(true)
}
return (() => {
console.log('unmount');
bleManagerEmitter.removeListener('BleManagerDiscoverPeripheral', handleDiscoverPeripheral);
bleManagerEmitter.removeListener('BleManagerStopScan', handleStopScan );
bleManagerEmitter.removeListener('BleManagerDisconnectPeripheral', handleDisconnectedPeripheral );
bleManagerEmitter.removeListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic );
})
}, []);
const renderConnectButton = (item) => {
if (isConnected(item)) {
return null
}
return (
<Button
title="Connect"
onPress={() => {
toggleConnectPeripheral(item)
}}
/>
)
}
const renderDisconnectButton = (item) => {
if (! isConnected(item)) {
return null
}
return (
<Button
title="Disconnect"
onPress={() => {
toggleConnectPeripheral(item)
}}
/>
)
}
const renderItem = (item) => {
const color = item.connected ? 'green' : '#fff';
return (
<TouchableHighlight>
<View style={[styles.row, {backgroundColor: color}]}>
<Text style={{fontSize: 12, textAlign: 'center', color: '#333333', padding: 10}}>{item.name}</Text>
<Text style={{fontSize: 10, textAlign: 'center', color: '#333333', padding: 2}}>RSSI: {item.rssi}</Text>
<Text style={{fontSize: 8, textAlign: 'center', color: '#333333', padding: 2, paddingBottom: 20}}>{item.id}</Text>
{renderConnectButton(item)}
{renderDisconnectButton(item)}
</View>
</TouchableHighlight>
);
}
const renderContent = () => {
if (! permissionsAllowed) {
return <Text>Bluetooth and locations permissions are required.</Text>
}
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={{margin: 10}}>
<Button
title={'Scan Bluetooth (' + (isScanning ? 'on' : 'off') + ')'}
onPress={() => startScan() }
/>
</View>
<View style={{margin: 10}}>
<Button title="Retrieve connected peripherals" onPress={() => retrieveConnected() } />
</View>
{(list.length == 0) &&
<View style={{flex:1, margin: 20}}>
<Text style={{textAlign: 'center'}}>No peripherals</Text>
</View>
}
</View>
</ScrollView>
<FlatList
data={list}
renderItem={({ item }) => renderItem(item) }
keyExtractor={item => item.id}
/>
</SafeAreaView>
</>
)
}
return (
renderContent()
);
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
engine: {
position: 'absolute',
right: 0,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
highlight: {
fontWeight: '700',
},
footer: {
color: Colors.dark,
fontSize: 12,
fontWeight: '600',
padding: 4,
paddingRight: 12,
textAlign: 'right',
},
});
export default BleDeviceList;
As you can see in the code, when the Connect button is clicked, it will redirect the user to another component that reads data from another device. The following is the BleRowingSession.js that reads the data from another device.
import React, { useEffect, useState } from 'react';
import { Text, View, StyleSheet, NativeModules, NativeEventEmitter, ScrollView } from 'react-native';
import BleManager from 'react-native-ble-manager';
const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);
const serviceId = "00001826-0000-1000-8000-00805F9B34FB";
const characteristicId = "00002AD1-0000-1000-8000-00805F9B34FB";
let readDataCache = "";
const BleRowingSession = (props) => {
let peripheral = props.route.params.peripheral;
const [ readData, setReadData ] = useState("");
const setUpBleNotification = () => {
BleManager.retrieveServices(peripheral.id).then((peripheralData) => {
console.log('Retrieved peripheral services', peripheralData);
setTimeout(() => {
BleManager.startNotification(peripheral.id, serviceId, characteristicId).then(() => {
console.log('Started notification on ' + peripheral.id);
bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', (data) => {
readDataCache = readDataCache + "\n" + data.value.toString()
setReadData(readDataCache);
});
setTimeout(() => {
}, 500);
}).catch((error) => {
console.log('Notification error', error);
});
}, 500)
});
}
useEffect(() => {
setUpBleNotification()
}, [ ])
return (
<View style={styles.container}>
<ScrollView>
<Text>Ble Rowing Session</Text>
<Text>{readData}</Text>
</ScrollView>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
})
export default BleRowingSession;
For now, the service id and characteristic id are hardcoded. Of course, I had to choose the right device on the first page that displays the list of BLE devices.
When I run the code, it is working as expected on the Android device, it is receiving the characteristic value. When I run the code on the actual IOS device, it is not receiving any data. But it can scan the devices and connect to them. What is wrong with my code and how can I fix it?

Is it possible to broadcast with RSockets?

My question:
I have implemented RSockets on my back-end (java spring boot) and on my front-end (React.js).
My front-end and back-end are able to communicate. However, I would like to know if it is possible to broadcast messages to multiple connected clients from my back-end.
Is this possible?
Yes it is possible.
Here is an example of a rsocket endpoint using spring webflux & kotlin coroutines which multicasts events to all it's observers.
This uses MutableSharedFlow, but this can be achieved with a PublishSubject or a Flowable in rxjava3.
In this example a rsocket server broadcasts a timestamp every 30 seconds to all the connected clients.
Controller:
#Controller
class ApiResource(
private val chatManager: ChatManager
) {
#MessageMapping("broadcast")
suspend fun broadcast(): Flow<Message> =
chatManager.broadcastStream()
}
Stream manager as well as a demo publisher:
#Service
class ChatManager {
val broadcast = MutableSharedFlow<Message>()
fun broadcastStream() = broadcast
#Scheduled(fixedRate = 30 * 1000)
fun sendBroadcast() = runBlocking{
broadcast.emit(
Message(
"SpringServer",
System.currentTimeMillis().toString(),
"broadcast"
)
)
}
}
React component BroadcastApp.tsx:
import React, {useEffect, useState} from 'react';
import {
BufferEncoders, encodeAndAddCustomMetadata, encodeAndAddWellKnownMetadata,
MESSAGE_RSOCKET_COMPOSITE_METADATA, MESSAGE_RSOCKET_ROUTING,
RSocketClient,
toBuffer, createBuffer
} from 'rsocket-core';
import RSocketWebSocketClient from 'rsocket-websocket-client';
const metadataMimeType = MESSAGE_RSOCKET_COMPOSITE_METADATA.string // message/x.rsocket.composite-metadata.v0
const dataMimeType = 'application/json';
const websocketEndpoint = 'ws://localhost:5051/rsocket';
const transport = new RSocketWebSocketClient({
url: websocketEndpoint,
}, BufferEncoders);
const endpoint = 'broadcast';
interface Message {
senderId: string;
text: string;
channelId: string;
}
type CONNECT_STATUS = 'disconnected' | 'connecting' | 'connected';
function BroadcastApp() {
const defaultMetadata = encodeAndAddWellKnownMetadata(
createBuffer(0),
MESSAGE_RSOCKET_ROUTING,
toBuffer(String.fromCharCode(endpoint.length) + endpoint)
)
const constructMetadataWithChannelId = (cid: string) => encodeAndAddCustomMetadata(
defaultMetadata,
'messaging/x.chat.client-id',
toBuffer(String.fromCharCode(cid.length) + cid)
)
const client = new RSocketClient({
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType,
metadataMimeType,
},
transport,
});
const [connectStatus, setConnectStatus] = useState<CONNECT_STATUS>('disconnected');
const [msgs, setMsgs] = useState<string[]>([]);
useEffect(() => {
if (connectStatus === 'connecting') {
console.log(`rsocket client connecting...`);
client
.connect()
.subscribe({
onError: error => {
console.log(`error: client connect: ${error}`)
setConnectStatus('disconnected');
},
onSubscribe: cancel => {},
onComplete: (sock) => {
sock.requestStream({
metadata: constructMetadataWithChannelId('broadcast')
}).subscribe({
onSubscribe: (subscription) => {
console.log(`rsocket client connected ✅`);
setConnectStatus('connected');
subscription.request(1000)
},
onNext: (event:any) => {
console.log(`received event from channel: ${JSON.stringify(event)}`);
const value = JSON.parse(event.data) as Message;
setMsgs(prev => [value.text, ...prev]);
},
onError: (error) => {
console.log(`err with rsocket subscribe: ${error}`)
}
});
}
});
}
}, [connectStatus])
const handleConnect = () => {
setConnectStatus('connecting');
};
const handleDisconnect = () => {
alert('todo: implement disconnect');
}
return (
<div style={{ padding: 20}}>
<div>
{(connectStatus === 'connected') ? (
<div style={{margin: 10, fontSize: 18}}>
<button
onClick={handleDisconnect}
style={{
padding: 10,
borderRadius: 10,
fontSize: 28,
margin: 10,
}}
>
Disconnect
</button>
</div>
) : (<div>
{connectStatus === 'disconnected' ? (<button
onClick={handleConnect}
style={{
padding: 10,
borderRadius: 10,
fontSize: 28,
}}
>
Connect
</button>) : (
<></>
)}
</div>)}
</div>
<div>
{msgs.map(item => (
<div
style={{
backgroundColor: 'lightgreen',
fontSize: 18,
width: 300,
padding: 10,
borderRadius: 10,
margin: 10,
}}>
{item}
</div>
))}
</div>
</div>
);
}
export default BroadcastApp;

iOS react-native CameraRoll loads too slowly.

With react-native, I implemented IOS CameraRoll that fetches 300 images from 'Camera Roll' Album on first and keep fetching 300 images whenever scroll reaches the end. Below is My code. SalmonCameraRoll.js
import React from 'react'
import {
View,
Text,
TouchableHighlight,
Modal,
StyleSheet,
Button,
CameraRoll,
Image,
Dimensions,
ScrollView,
FlatList,
} from 'react-native'
import Share from 'react-native-share';
import RNFetchBlob from 'react-native-fetch-blob';
let styles
const { width, height } = Dimensions.get('window')
const fetchAmount = 300;
class SalmonCameraRoll extends React.Component {
static navigationOptions = {
title: 'Salmon Camera Roll',
}
constructor(props) {
super(props);
this.state = {
photos: [],
// index: null,
lastCursor: null,
noMorePhotos: false,
loadingMore: false,
refreshing: false,
};
this.tryGetPhotos = this.tryGetPhotos.bind(this);
this.getPhotos = this.getPhotos.bind(this);
this.appendPhotos = this.appendPhotos.bind(this);
this.renderImage = this.renderImage.bind(this);
this.onEndReached = this.onEndReached.bind(this);
this.getPhotos({first: fetchAmount, assetType: 'Photos'});
}
componentDidMount() {
this.subs = [
this.props.navigation.addListener('didFocus', () => {
this.getPhotos({first: fetchAmount, assetType: 'Photos'});
}),
];
}
componentWillUnmount() {
this.subs.forEach(sub => sub.remove());
}
tryGetPhotos = (fetchParams) => {
if (!this.state.loadingMore) {
this.setState({ loadingMore: true }, () => { this.getPhotos(fetchParams)})
}
}
getPhotos = (fetchParams) => {
if (this.state.lastCursor) {
fetchParams.after = this.state.lastCursor;
}
CameraRoll.getPhotos(fetchParams).then(
r => this.appendPhotos(r)
)
}
appendPhotos = (data) => {
const photos = data.edges;
const nextState = {
loadingMore: false,
};
if (!data.page_info.has_next_page) {
nextState.noMorePhotos = true;
}
if (photos.length > 0) {
nextState.lastCursor = data.page_info.end_cursor;
nextState.photos = this.state.photos.concat(photos);
this.setState(nextState);
}
}
onEndReached = () => {
if (!this.state.noMorePhotos) {
this.tryGetPhotos({first: fetchAmount, assetType: 'Photos'});
}
}
renderImage = (photo, index) => {
return (
<TouchableHighlight
style={{borderTopWidth: 1, borderRightWidth: 1, borderColor: 'white'}}
key={index}
underlayColor='transparent'
onPress={() => {
this.props.navigation.navigate('Camera', { backgroundImageUri: photo.node.image.uri })
}
}
>
<Image
style={{
width: width/3,
height: width/3
}}
representation={'thumbnail'}
source={{uri: photo.node.image.uri}}
/>
</TouchableHighlight>
)
}
render() {
return (
<View style={styles.container}>
<View style={styles.modalContainer}>
<FlatList
numColumns={3}
data={this.state.photos}
initialNumToRender={fetchAmount}
onEndReachedThreshold={500}
onEndReached={this.onEndReached}
refreshing={this.state.refreshing}
onRefresh={() => this.tryGetPhotos({first: fetchAmount, assetType: 'Photos'})}
keyExtractor={(item, index) => index}
renderItem={({ item, index }) => (
this.renderImage(item, index)
)}
/>
</View>
</View>
)
}
}
styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
modalContainer: {
// paddingTop: 20,
flex: 1,
},
scrollView: {
flexWrap: 'wrap',
flexDirection: 'row'
},
shareButton: {
position: 'absolute',
width,
padding: 10,
bottom: 0,
left: 0
}
})
export default SalmonCameraRoll
Problem is that in circumstance of a lot of images(about 10000 images) in 'Camera Roll' album, each image component was loaded so slowly that it was also loaded too slowly when scrolling accordingly.
In other famous apps like Facebook or Instagram, it loads all images quickly at once without fetching whenever scroll reaches end.
How can i make my Image component load fast? Or best of all(if possible), how can i make my CameraRoll load all images quickly at once without fetching whenever scroll reaches end?
Thank you.

React Native - Screen blinks when updating props in this.intervalHandle = setInterval

My problem is I want to update my data and display on screen in real time, but every time I try to update this.props.stoveUsage, my screen turns blank when I make the this.getUsage() call in this.intervalHandle = setInverval
This is where my problem occurs:
componentDidMount () {
console.log('Enter StatsScreen.componentDidMount')
this.drawer.drawer.setNativeProps({ style: { width: 0 } })
this.intervalHandle = setInterval(() => {
this.getUsage()
}, 30000)
}
this.getUsage is supposed to update the props in getStoveUsage: (deviceId, viewType, start) =>
dispatch(SmarturnsActions.getUsage(deviceId, viewType, start))
const mapDispatchToProps = (dispatch) => {
return {
selectDevice: (selectedDeviceId) =>
dispatch(SmarturnsActions.deviceSelected(selectedDeviceId)),
getStoveUsage: (deviceId, viewType, start) =>
dispatch(SmarturnsActions.getUsage(deviceId, viewType, start))
}
}
Full code here:
class Stats extends React.Component {
constructor (props) {
super(props)
this.state = {
viewType: 'day',
dt: moment(),
device: {},
updateFlipFlop: true
}
this.intervalHandle = null
}
static navigationOptions = {
tabBarLabel: I18n.t('$tab_usage'),
tabBarIcon: ({ tintColor, focused }) => {
return (
<Image
source={focused ? Images.tabStatsActive : Images.tabStats}
style={[styles.tabIcon, { tintColor: tintColor }]}
/>
)
}
}
getDevice = (selectedDeviceId) => {
const devices = this.props.devices || []
let dev = {}
devices.forEach((d) => {
if (d._id === selectedDeviceId) {
dev = d
}
})
if (!dev._id && devices.length) {
dev = devices[0]// dev is the device we are working with
}
this.setState({ device: dev }, () => {
this.getUsage()
})
}
componentDidMount () {
console.log('Enter StatsScreen.componentDidMount')
this.drawer.drawer.setNativeProps({ style: { width: 0 } })
this.intervalHandle = setInterval(() => {
this.getUsage()
}, 30000)
}
componentWillMount () {
console.log('Enter StatsScreen.componentWillMount')
this.getDevice(this.props.selectedDeviceId)
}
componentWillUnmount = () => {
console.log('Enter MainScreen.componentWillUnmount()')
if (this.intervalHandle) clearInterval(this.intervalHandle)
}
componentWillReceiveProps (nextProps) {
if (nextProps.selectedDeviceId !== this.props.selectedDeviceId) {
this.getDevice(nextProps.selectedDeviceId)
} else if (nextProps.stoveUsage !== this.props.stoveUsage) {
this.setState({ updateFlipflop: !this.state.updateFlipflop })
}
}
onPressPrev = () => {
console.log('onPressPrev')
console.log('state=', this.state)
const dt = moment(this.state.dt).add(-1, this.state.viewType + 's') // converts to 'days', 'weeks', 'months'
this.setState({ dt }, () => {
this.getUsage()
})
}
onPressNext = () => {
console.log('onPressNext')
const dt = moment(this.state.dt).add(1, this.state.viewType + 's') // converts to 'days', 'weeks', 'months'
this.setState({ dt }, () => {
this.getUsage()
})
}
onPressCalendar = () => {
console.log('onPressCalendar')
}
getUsage = () => {
let dt = this.state.dt
let vt = this.state.viewType
let dev = this.state.device
let date
if (vt === 'day') {
date = moment(dt).format('YYYY-MM-D')
} else if (vt === 'week') {
date = moment(dt).startOf('week').format('YYYY-MM-D')
} else {
date = moment(dt).format('YYYY-MM')
}
this.props.getStoveUsage(dev._id, vt, date)
}
onPressSegment = (viewType) => {
console.log('click on segment ', viewType)
this.setState({ viewType: viewType, dt: moment() }, () => {
this.getUsage()
})
}
_onStovePress = (stove) => {
console.log('Enter StatsScreen._onStovePress. stove=', stove._id)
this.props.selectDevice(stove._id)
this.closeDrawer()
}
formatDate = (dt) => {
if (this.state.viewType === 'day') {
return moment(dt).calendar(null, {
sameDay: '[Today]',
nextDay: '[Tomorrow]',
lastDay: '[Yesterday]',
lastWeek: 'ddd, MMM D',
sameElse: 'ddd, MMM D'
})
} else if (this.state.viewType === 'week') {
return moment(dt).startOf('week').format('MMM D') + ' - ' + moment(dt).startOf('week').add(6, 'days').format('MMM D')
} else {
return moment(dt).format('MMM, YYYY')
}
}
displayUsage = () => {
if (!this.props.fetching && this.props.stoveUsage) {
const total = moment.duration(this.props.stoveUsage.total, 'seconds').format('H:mm', {trim: false})
const average = this.props.stoveUsage.average
const usage = this.props.stoveUsage.usage
if (this.state.viewType === 'day') {
return <UsageDay totalUsage={total} average={average} usage={usage} date={this.state.dt} />
} else if (this.state.viewType === 'week') {
return <UsageWeek totalUsage={total} usage={usage} />
} else {
return <UsageMonth totalUsage={total} usage={usage} />
}
}
}
closeDrawer = () => {
this.drawer.close()
this.drawer.drawer.setNativeProps({ style: { width: 0 } })
}
openDrawer = () => {
this.drawer.drawer.setNativeProps({ style: { width: this.drawer.getDrawerWidth() } })
this.drawer.open()
}
_renderDrawer = (dev) => {
const allStoves = this.props.devices || []
const devices = allStoves.filter((d) => { return d.follower.enabled })
const selfStoves = devices.filter(d => !d.is_shared)
const sharedStove = devices.filter(d => !!d.is_shared)
return (
<ScrollView style={[styles.drawerContainer]}>
<View style={styles.drawerHeader} >
<Text style={{ color: '#fff' }}>{dev.owner_username}</Text>
</View>
<List>
<ListItem itemDivider>
<Text>{I18n.t('$setting_my_smarturns')}</Text>
</ListItem>
{
selfStoves.map(stove => (
<ListItem key={stove._id}
onPress={() => { this._onStovePress(stove) }} >
<Body>
<Text>{stove.name}</Text>
<Text note>{stove.owner_username}</Text>
</Body>
</ListItem>
))
}
<ListItem itemDivider>
<Text>{I18n.t('$setting_others_smarturns')}</Text>
</ListItem>
{
sharedStove.map(stove => (
<ListItem key={stove._id}
onPress={() => { this._onStovePress(stove) }} >
<Body>
<Text>{stove.name}</Text>
<Text note>{stove.owner_username}</Text>
</Body>
</ListItem>
))
}
</List>
</ScrollView>)
}
render () {
console.log('Enter StatsScreen.render()')
let dt = this.state.dt
if (this.state.viewType === 'week') dt = moment(dt).startOf('week')
const dateStr = this.formatDate(dt)
return (
<StyleProvider style={getTheme(commonColor)}>
<Container>
<Drawer ref={(ref) => { this.drawer = ref }}
type='overlay'
tapToClose
openDrawerOffset={0.3}
content={this._renderDrawer(this.state.device)}
onClose={() => this.closeDrawer()} >
<Header hasSubtitle>
<Left>
<Button transparent onPress={this.openDrawer}>
<Icon name='navicon' />
</Button>
</Left>
<Body >
<Title>{I18n.t('$usage_title')}</Title>
<Subtitle style={{ color: 'white' }} ellipsizeMode='tail' numberOfLines={1}>{this.state.device.name}</Subtitle>
</Body>
<Right />
</Header>
<Segment>
<Button first active={this.state.viewType === 'day'} onPress={() => this.onPressSegment('day')}><Text>Daily</Text></Button>
<Button active={this.state.viewType === 'week'} onPress={() => this.onPressSegment('week')}><Text>Weekly</Text></Button>
<Button last active={this.state.viewType === 'month'} onPress={() => this.onPressSegment('month')}><Text>Monthly</Text></Button>
</Segment>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', height: 40 }}>
<View>
<Button transparent onPress={this.onPressPrev}>
<Icon name='angle-left' style={{ color: 'rgba(46, 88, 137, 1)' }} />
</Button>
</View>
<View>
<Button transparent onPress={this.onPressCalendar}>
<Text note>{dateStr}</Text>
{/* <Icon name='calendar' /> */}
</Button>
</View>
<View>
<Right>
<Button transparent onPress={this.onPressNext}>
<Icon name='angle-right' style={{ color: 'rgba(46, 88, 137, 1)' }} />
</Button>
</Right>
</View>
</View>
<Content contentContainerStyle={{ flex: 1 }}>
{this.displayUsage()}
</Content>
</Drawer>
</Container>
</StyleProvider>
)
}
}
const mapStateToProps = (state) => {
return {
fetching: state.smarturns.fetching,
devices: state.smarturns.devices,
selectedDeviceId: state.smarturns.selectedDeviceId,
stoveUsage: state.smarturns.usage
}
}
const mapDispatchToProps = (dispatch) => {
return {
selectDevice: (selectedDeviceId) => dispatch(SmarturnsActions.deviceSelected(selectedDeviceId)),
getStoveUsage: (deviceId, viewType, start) => dispatch(SmarturnsActions.getUsage(deviceId, viewType, start))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Stats)

Trying to Access ReactNative.Component Error

I am currently following a basic tutorial on React Native authentication. When I launch the project, it gives the following error:
I understand the error, but cannot see what I am doing wrongly. In particular, I commented out my views until the error disappeared and the project compiled. Here is the perpetrator. Why would it make such error appear?
import React, { Component } from 'react';
import {
ScrollView,
StyleSheet,
TouchableHighlight,
Text
} from 'react-native';
const t = require('tcomb-form-native');
const Form = t.form.Form
const newUser = t.struct({
email: t.String,
password: t.String
})
const options = {
fields: {
email: {
autoCapitalize: 'none',
autoCorrect: false
},
password: {
autoCapitalize: 'none',
password: true,
autoCorrect: false
}
}
}
class RegisterView extends Component {
constructor(props) {
super(props)
this.state = {
value: {
email: '',
password: ''
}
}
}
componentWillUnmount() {
this.setState = {
value: {
email: '',
password: null
}
}
}
_onChange = (value) => {
this.setState({
value
})
}
_handleAdd = () => {
const value = this.refs.form.getValue();
// If the form is valid...
if (value) {
const data = {
email: value.email,
password: value.password,
}
// Serialize and post the data
const json = JSON.stringify(data);
fetch('http://localhost:3000/users/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: json
})
.then((response) => response.json())
.then(() => {
alert('Success! You may now log in.');
// Redirect to home screen
this.props.navigator.pop();
})
.catch((error) => {
alert('There was an error creating your account.');
})
.done()
} else {
// Form validation error
alert('Please fix the errors listed and try again.')
}
}
render() {
return (
<ScrollView style={styles.container}>
<Form
ref='form'
type={newUser}
options={options}
value={this.state.value}
onChange={this._onChange}
/>
<TouchableHighlight onPress={this._handleAdd}>
<Text style={[styles.button, styles.greenButton]}>Create account</Text>
</TouchableHighlight>
</ScrollView>
)
}
}
const styles = StyleSheet.create({
container: {
padding: 20,
flex: 1,
flexDirection: 'column'
},
button: {
borderRadius: 4,
padding: 20,
textAlign: 'center',
marginBottom: 20,
color: '#fff'
},
greenButton: {
backgroundColor: '#4CD964'
},
centering: {
alignItems: 'center',
justifyContent: 'center'
}
})
module.exports = RegisterView
According to documentation from tcomb-form-native,
tcomb-form-native ^0.5: react-native >= 0.25.0
tcomb-form-native ^0.4: react-native >= 0.20.0
tcomb-form-native ^0.3: react-native < 0.13.0
My react-native --version was
react-native-cli: 1.0.0
react-native: 0.36.0
Hence, running npm install tcomb-form-native#0.5.3 solved the issue for me.

Resources