I am rewriting some code to es6 syntax and i am having trouble. This is my first react native project so i am unfamiliar with some things. I am starting with this demo rn-background-geolocation-demo
I added Navigation with a Login screen. When I login and switch views I am getting this error at
onLayout() {
var me = this,
gmap = this.refs.gmap;
this.refs.workspace.measure(function(ox, oy, width, height, px, py) {
me.setState({
mapHeight: height,
mapWidth: width
});
});
}
undefined is not an object (evaluating 'this.refs')
here is the entire file. if u need more information let me know! thanks
Map.js
class Map extends Component {
constructor(props) {
super(props)
this.state = {
locationIcon: require("image!green_circle"),
currentLocation: undefined,
locationManager: undefined,
enabled: false,
isMoving: false,
odometer: 0,
paceButtonStyle: commonStyles.disabledButton,
paceButtonIcon: 'play',
navigateButtonIcon: 'navigate',
mapHeight: 300,
mapWidth: 300,
// mapbox
center: {
lat: 40.7223,
lng: -73.9878
},
zoom: 10,
markers: []
}
}
componentDidMount() {
var me = this,
gmap = this.refs.gmap;
this.locationManager = this.props.locationManager;
// location event
this.locationManager.on("location", function(location) {
console.log('- location: ', JSON.stringify(location));
me.setCenter(location);
gmap.addMarker(me._createMarker(location));
me.setState({
odometer: (location.odometer / 1000).toFixed(1)
});
// Add a point to our tracking polyline
if (me.polyline) {
me.polyline.addPoint(location.coords.latitude, location.coords.longitude);
}
});
// http event
this.locationManager.on("http", function(response) {
console.log('- http ' + response.status);
console.log(response.responseText);
});
// geofence event
this.locationManager.on("geofence", function(geofence) {
console.log('- onGeofence: ', JSON.stringify(geofence));
});
// error event
this.locationManager.on("error", function(error) {
console.log('- ERROR: ', JSON.stringify(error));
});
// motionchange event
this.locationManager.on("motionchange", function(event) {
console.log("- motionchange", JSON.stringify(event));
me.updatePaceButtonStyle();
});
// getGeofences
this.locationManager.getGeofences(function(rs) {
console.log('- getGeofences: ', JSON.stringify(rs));
}, function(error) {
console.log("- getGeofences ERROR", error);
});
SettingsService.getValues(function(values) {
values.license = "eddbe81bbd86fa030ea466198e778ac78229454c31100295dae4bfc5c4d0f7e2";
values.orderId = 1;
values.stopTimeout = 0;
//values.url = 'http://192.168.11.120:8080/locations';
me.locationManager.configure(values, function(state) {
console.log('- configure state: ', state);
me.setState({
enabled: state.enabled
});
if (state.enabled) {
me.initializePolyline();
me.updatePaceButtonStyle()
}
});
});
this.setState({
enabled: false,
isMoving: false
});
}
_createMarker(location) {
return {
title: location.timestamp,
id: location.uuid,
icon: this.locationIcon,
anchor: [0.5, 0.5],
coordinates: {
lat: location.coords.latitude,
lng: location.coords.longitude
}
};
}
initializePolyline() {
// Create our tracking Polyline
var me = this;
Polyline.create({
points: [],
geodesic: true,
color: '#2677FF',
width: 12
}, function(polyline) {
me.polyline = polyline;
});
}
onClickMenu() {
this.props.drawer.open();
}
onClickEnable() {
var me = this;
if (!this.state.enabled) {
this.locationManager.start(function() {
me.initializePolyline();
});
} else {
this.locationManager.resetOdometer();
this.locationManager.stop();
this.setState({
markers: [{}],
odometer: 0
});
this.setState({
markers: []
});
if (this.polyline) {
this.polyline.remove(function(result) {
me.polyline = undefined;
});
}
}
this.setState({
enabled: !this.state.enabled
});
this.updatePaceButtonStyle();
}
onClickPace() {
if (!this.state.enabled) {
return;
}
var isMoving = !this.state.isMoving;
this.locationManager.changePace(isMoving);
this.setState({
isMoving: isMoving
});
this.updatePaceButtonStyle();
}
onClickLocate() {
var me = this;
this.locationManager.getCurrentPosition({
timeout: 30
}, function(location) {
me.setCenter(location);
console.log('- current position: ', JSON.stringify(location));
}, function(error) {
console.error('ERROR: getCurrentPosition', error);
me.setState({
navigateButtonIcon: 'navigate'
});
});
}
onRegionChange() {
console.log('onRegionChange');
}
setCenter(location) {
this.setState({
navigateButtonIcon: 'navigate',
center: {
lat: location.coords.latitude,
lng: location.coords.longitude
},
zoom: 16
});
}
onLayout() {
var me = this,
gmap = this.refs.gmap;
this.refs.workspace.measure(function(ox, oy, width, height, px, py) {
me.setState({
mapHeight: height,
mapWidth: width
});
});
}
updatePaceButtonStyle() {
var style = commonStyles.disabledButton;
if (this.state.enabled) {
style = (this.state.isMoving) ? commonStyles.redButton : commonStyles.greenButton;
}
this.setState({
paceButtonStyle: style,
paceButtonIcon: (this.state.enabled && this.state.isMoving) ? 'pause' : 'play'
});
}
render() {
return (
<View style={commonStyles.container}>
<View style={commonStyles.topToolbar}>
<Icon.Button name="android-options" onPress={this.onClickMenu} backgroundColor="transparent" size={30} color="#000" style={styles.btnMenu} underlayColor={"transparent"} />
<Text style={commonStyles.toolbarTitle}>Background Geolocation</Text>
<SwitchAndroid onValueChange={this.onClickEnable} value={this.state.enabled} />
</View>
<View ref="workspace" style={styles.workspace} onLayout={this.onLayout}>
<RNGMap
ref={'gmap'}
style={{width: this.state.mapWidth, height: this.state.mapHeight}}
markers={this.state.markers}
zoomLevel={this.state.zoom}
onMapChange={(e) => console.log(e)}
onMapError={(e) => console.log('Map error --> ', e)}
center={this.state.center} />
</View>
<View style={commonStyles.bottomToolbar}>
<Icon.Button name={this.state.navigateButtonIcon} onPress={this.onClickLocate} size={25} color="#000" underlayColor="#ccc" backgroundColor="transparent" style={styles.btnNavigate} />
<Text style={{fontWeight: 'bold', fontSize: 18, flex: 1, textAlign: 'center'}}>{this.state.odometer} km</Text>
<Icon.Button name={this.state.paceButtonIcon} onPress={this.onClickPace} iconStyle={commonStyles.iconButton} style={this.state.paceButtonStyle}><Text>State</Text></Icon.Button>
<Text> </Text>
</View>
</View>
);
}
};
module.exports = Map;
The problem comes from the line <View ref="workspace" style={styles.workspace} onLayout={this.onLayout}>; you're passing a reference to your onLayout function, but when it's called, it's called without a context. What you want to do is bind that function before passing the reference. There are many ways to do this, but the simplest is:
<View ref="workspace" style={styles.workspace} onLayout={this.onLayout.bind(this)}>
Arrow Functions lexically bind "this", so if you write your onLayout() function like so:
onLayout = () => {
...
};
you will no longer need to bind it anywhere.
Related
I am attempting to use React to build a website with a Google map that redraws the Polylines after I change the state. I am using setState() to set the state correctly, and I can see the values change when I print the object from this.state in the console but my Polylines on map do not re-render, and change color.
This is the main component that I am using for the project it includes a map and a table, renderMap() is what actually generates the map and update_congestion_lines is what changes the state and I am hoping to trigger a re-render.
import React, { Component } from 'react';
import {Map, Marker, GoogleApiWrapper, InfoWindow, Polyline} from 'google-maps-react';
class HomepageMap extends Component{
static defaultProps = {
center: {
lat: 33.980530,
lng: -117.377020
},
zoom: 11,
style: {
width: '100%',
height: '100%'
}
};
constructor(props){
super(props);
this.state = {
error: null,
cctvs: [],
cctv_objects: [],
center: {
lat: 33.980530,
lng: -117.377020
},
style: {
width: '100%',
height: '100%'
},
zoom: 11,
showingInfoWindow : false,
selectedPlace: {},
activeMarker: {},
image_path : [],
};
this.update_congestion_lines = this.update_congestion_lines.bind(this);
this.grabColor = this.grabColor.bind(this);
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
var url = "http://highwayanalytics.us/api/cctv?format=json&county=Riverside,San+Bernardino";
fetch(url)
.then(res => res.json())
.then(
(result) => {
var list = [];
for(var i = 0; i < result.length; i++){
var cctv = result[i];
if(cctv.image_url !== "Not Reported"){
list.push(cctv);
}
}
this.setState({
cctvs: list,
error: false
});
},
(error) => {
//console.log(error);
this.setState({
cctvs: [],
error: true
})
});
url = "http://highwayanalytics.us/api/graph?format=json&county=Riverside,San+Bernardino";
fetch(url)
.then(res => res.json())
.then(
(result) => {
var list = [];
for(var key in result){
if(result.hasOwnProperty(key)){
var val = result[key];
var i = 0;
if(key === "SR-60" || key === "I-10" || key === "SR-91" || key === "I-210"){
for(i=0;i < val.length;i++){
var prev_cctv = null;
if(i!== 0){
prev_cctv=val[i-1];
}
var next_cctv = null;
if(i !== (val.length-1)){
next_cctv = val[i+1];
//Calc distance
var prev_lat_midpoint = null;
var prev_long_midpoint = null;
var next_lat_midpoint = null;
var next_long_midpoint = null;
var temp = null;
if(prev_cctv !== null){
if(prev_cctv.latitude > val[i].latitude){
temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
prev_lat_midpoint = val[i].latitude + temp;
}
else{
temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
prev_lat_midpoint = val[i].latitude - temp;
}
if(prev_cctv.longitude > val[i].longitude){
temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
prev_long_midpoint = val[i].longitude + temp;
}
else{
temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
prev_long_midpoint = val[i].longitude - temp;
}
}
if(next_cctv !== null){
if(next_cctv.latitude > val[i].latitude){
temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
next_lat_midpoint = val[i].latitude + temp;
}
else{
temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
next_lat_midpoint = val[i].latitude - temp;
}
if(next_cctv.longitude > val[i].longitude){
temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
next_long_midpoint = val[i].longitude + temp;
}
else{
temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
next_long_midpoint = val[i].longitude - temp;
}
}
var object = {
"cctv": val[i],
"cctv_id":val[i].cctv_id,
"prev_cctv": prev_cctv,
"next_cctv": next_cctv,
"prev_lat_midpoint": prev_lat_midpoint,
"prev_long_midpoint": prev_long_midpoint,
"next_lat_midpoint": next_lat_midpoint,
"next_long_midpoint": next_long_midpoint,
"car_count": null
}
list.push(object);
}
}
}
else if( key === "I-15" || key === "I-215"){
for(i=0;i < val.length;i++){
var prev_cctv = null;https://reactjs.org/docs/state-and-lifecycle.html
if(i!== 0){
prev_cctv=val[i-1];
}
var next_cctv = null;
if(i !== (val.length-1)){
next_cctv = val[i+1];
//Calc distance
var prev_lat_midpoint = null;
var prev_long_midpoint = null;
var next_lat_midpoint = null;
var next_long_midpoint = null;
var temp = null;
if(prev_cctv !== null){
if(prev_cctv.latitude > val[i].latitude){
temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
prev_lat_midpoint = val[i].latitude + temp;
}
else{
temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
prev_lat_midpoint = val[i].latitude - temp;
}
if(prev_cctv.longitude > val[i].longitude){
temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
prev_long_midpoint = val[i].longitude + temp;
}
else{
temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
prev_long_midpoint = val[i].longitude - temp;
}
}
if(next_cctv !== null){
if(next_cctv.latitude > val[i].latitude){
temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
next_lat_midpoint = val[i].latitude + temp;
}
else{
temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
next_lat_midpoint = val[i].latitude - temp;
}
if(next_cctv.longitude > val[i].longitude){
temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
next_long_midpoint = val[i].longitude + temp;
}
else{
temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
next_long_midpoint = val[i].longitude - temp;
}
}
object = {
"cctv": val[i],
"cctv_id": val[i].cctv_id,
"prev_cctv": prev_cctv,
"next_cctv": next_cctv,
"prev_lat_midpoint": prev_lat_midpoint,
"prev_long_midpoint": prev_long_midpoint,
"next_lat_midpoint": next_lat_midpoint,
"next_long_midpoint": next_long_midpoint,
"car_count": null
}
list.push(object);
}
}
}
else{
continue;
}
}
}
this.setState({
cctv_objects: list,
error: false
});
},
(error) => {
//console.log(error);
this.setState({
cctvs: [],
error: true
})
});
//Used for updating congestion lines, every 10 seconds
// this.intervalID = setInterval(
// ()=> this.update_congestion_lines(),
// 10000
// );
}
componentWillUnmount(){
clearInterval(this.intervalID);
}
update_congestion_lines(){
//Update Congestions Lines
console.log("Updating Congestion lines");
var cctv_objects_dup = Array.from(this.state.cctv_objects);
//Go through all cameras
for(var i = 0; i < cctv_objects_dup.length;i++){
var target_url = "http://highwayanalytics.us/api/vehicle/?cctv="+cctv_objects_dup[i].cctv_id+"&format=json";
var target_photo_id = null;
if(cctv_objects_dup[i].cctv_id !== undefined){
fetch(target_url)
.then(res => res.json())
.then(
(result) => {
if(result !== undefined){
if(result.results !== undefined){
if(result.results[0] !== undefined){
//Find most recent photo id on specific camera
target_photo_id = result.results[0].photo;
target_url = "http://highwayanalytics.us/api/vehicle/?photo="+target_photo_id+"&format=json";
fetch(target_url)
.then( res => res.json())
.then(
(result) => {
//assign that camera the result of photo count
for(let index = 0; index < cctv_objects_dup.length; index++){
if(cctv_objects_dup[index] === undefined){
console.log("undefined");
continue;
}
if(cctv_objects_dup[index].cctv_id === result.results[0].cctv){
cctv_objects_dup[index].car_count = result.count;
break;
}
}
},
(error) =>{
console.log("Error with using target_photo_id");
}
);
}
}
}
},
(error) => {
console.log("Error updating Congestion");
}
);
}
}
//update cctv objects with new car counts
this.setState({
cctv_objects: cctv_objects_dup
});
//console.log("Lines Done Updating",this.state.cctv_objects[0].car_count);
//console.log("here is cctv_objects",this.state.cctv_objects);
}
onMarkerClick = (props, marker) => {
var latest_image = "http://highwayanalytics.us/api/search/?search=" + props.name;
var path = [];
fetch(latest_image)
.then(res => res.json())
.then(
(result) => {
//console.log(result.results[0].file_name)
path.push(result.results[0].file_name)
this.setState({
activeMarker: marker,
selectedPlace: props,
showingInfoWindow: true,
image_path : path,
});
}
)
};
onMouseoverMarker= (props, marker, e) => {
// this.setState({
// activeMarker: marker,
// selectedPlace: props,
// showingInfoWindow: true
// })
// console.log(this.state.showingInfoWindow)
};
onMouseoutMarker= (props, marker, e) => {
// this.setState({
// activeMarker: null,
// showingInfoWindow: false
// })
// console.log(this.state.showingInfoWindow)
};
onInfoWindowClose = () =>
this.setState({
activeMarker: null,
showingInfoWindow: false
});
onMapClicked = () => {
if (this.state.showingInfoWindow){
this.setState({
activeMarker: null,
showingInfoWindow: false,
image_path: null,
})
console.log(this.state.showingInfoWindow)
}
};
grabColor = (car_count) =>{
console.log("Car Count", car_count);
if(car_count === null){
return 'green';
}
else{
return 'red';
}
}
renderMap(){
var icon_image = process.env.PUBLIC_URL + '/camera_icon2.png';
var cctvs = this.state.cctvs.map(
(d) =>
<Marker
icon={icon_image}
name = {d.id}
onClick = {this.onMarkerClick}
onMouseover={this.onMouseoverMarker}
onMouseout={this.onMouseoutMarker}
position = { {lat: d.latitude, lng: d.longitude} }
lat = {d.latitude}
long = {d.longitude}
image_url = {d.image_url}
route = {d.route}
/>
);
var prev_congestion_lines = this.state.cctv_objects.map(
(object)=>(
//prev_polyline
<Polyline
key= {object.cctv.latitude.toString() + object.cctv.longitude.toString()}
path={[
{ lat: object.prev_lat_midpoint, lng: object.prev_long_midpoint},
{ lat: object.cctv.latitude, lng: object.cctv.longitude},
]}
options={{
strokeColor: this.grabColor(object.car_count),
strokeOpacity: 0.75,
strokeWeight: 10,
icons: [{
offset: '0',
repeat: '10px'}],
}}
/>
)
);
var next_congestion_lines = this.state.cctv_objects.map(
(object)=>(
//prev_polyline
<Polyline
key = {object.cctv_id.toString() + object.cctv.latitude.toString() + object.cctv.longitude.toString()}
path={[
{ lat: object.cctv.latitude, lng: object.cctv.longitude},
{ lat: object.next_lat_midpoint, lng: object.next_long_midpoint},
]}
options={{
strokeColor: this.grabColor(object.car_count),
strokeOpacity: 0.75,
strokeWeight: 10,
icons: [{
offset: '0',
repeat: '10px'}],
}}
/>
)
);
return (
<div style={{ height: '92vh', width: '100%' }}>
<Map
google={this.props.google}
zoom={this.props.zoom}
style={this.props.style}
initialCenter={this.props.center}
onClick={this.onMapClicked}
>
{cctvs}
{prev_congestion_lines}
{next_congestion_lines}
</Map>
</div>
);
}
renderTable(){
var content = []
var path = "http://highwayanalytics.us/image/" + this.state.image_path;
if (this.state.showingInfoWindow !== false) {
content.push(
<div className="row" style={{'padding': '35px'}}>
<h2> Marker Information</h2>
<img
src = {path}
style={{
width: '380px'
}}
/>
<div>
<span> Lat : {this.state.selectedPlace.lat} Long: {this.state.selectedPlace.long}</span>
<p> Route : {this.state.selectedPlace.route} Marker_Id : {this.state.selectedPlace.name}</p>
</div>
</div>
);
}else{
content.push(
<div className="row" style={{'padding': '35px'}}>
<h2> Marker Information</h2>
</div>
);
}
return content;
}
handleClick(){
this.update_congestion_lines();
console.log(this.state.cctv_objects);
this.forceUpdate();
}
render(){
var map = this.renderMap();
var table = this.renderTable();
return(
<div className="row">
<div className="col-9">
{map}
</div>
<div className="col-3">
{table}
<button onClick={this.handleClick}> Update Congestion Lines</button>
</div>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GOOGLE_MAP_KEY
})(HomepageMap);
renderMap() takes care of generating the map, markers, and the polylines. The color of the polyline should change based on the car_count. As you can see am logging the car count,the car count should be null initially but is not null after the state is set, even though console.log tells me the car_count is not null, the color of the line does not change.
grabColor = (car_count) =>{
console.log("Car Count", car_count);
if(car_count === null){
return 'green';
}
else{
return 'red';
}
}
renderMap(){
var icon_image = process.env.PUBLIC_URL + '/camera_icon2.png';
var cctvs = this.state.cctvs.map(
(d) =>
<Marker
icon={icon_image}
name = {d.id}
onClick = {this.onMarkerClick}
onMouseover={this.onMouseoverMarker}
onMouseout={this.onMouseoutMarker}
position = { {lat: d.latitude, lng: d.longitude} }
lat = {d.latitude}
long = {d.longitude}
image_url = {d.image_url}
route = {d.route}
/>
);
var prev_congestion_lines = this.state.cctv_objects.map(
(object)=>(
//prev_polyline
<Polyline
key= {object.cctv.latitude.toString() + object.cctv.longitude.toString()}
path={[
{ lat: object.prev_lat_midpoint, lng: object.prev_long_midpoint},
{ lat: object.cctv.latitude, lng: object.cctv.longitude},
]}
options={{
strokeColor: this.grabColor(object.car_count),
strokeOpacity: 0.75,
strokeWeight: 10,
icons: [{
offset: '0',
repeat: '10px'}],
}}
/>
)
);
var next_congestion_lines = this.state.cctv_objects.map(
(object)=>(
<Polyline
key = {object.cctv_id.toString() + object.cctv.latitude.toString() + object.cctv.longitude.toString()}
path={[
{ lat: object.cctv.latitude, lng: object.cctv.longitude},
{ lat: object.next_lat_midpoint, lng: object.next_long_midpoint},
]}
options={{
strokeColor: this.grabColor(object.car_count),
strokeOpacity: 0.75,
strokeWeight: 10,
icons: [{
offset: '0',
repeat: '10px'}],
}}
/>
)
);
Here is what I get on the console and what the map looks like.
If anyone has an explanation of why the polylines are not being re-rendered after the car_count changes, I would be extremely grateful.
Edit: I have added to rest of the function of the first code block, and added a key to the next_congestion line as noted in the comments.
I am making a vue project and I want to use leaflet inside of my components. I have the map showing but I run into an error when I try to add a marker to the map. I get
Uncaught TypeError: events.forEach is not a function
at VueComponent.addEvents (VM2537 Map.vue:35)
at e.boundFn (VM2533 vue.esm.js:191)
at HTMLAnchorElement. (leaflet.contextmenu.js:328)
at HTMLAnchorElement.r (leaflet.js:5)
<template>
<div>
<div id="map" class="map" style="height: 781px;"></div>
</div>
</template>
<script>
export default {
data() {
return {
map: [],
markers: null
};
},
computed: {
events() {
return this.$store.state.events;
}
},
watch: {
events(val) {
this.removeEvents();
this.addEvents(val);
}
},
methods: {
addEvents(events) {
const map = this.map;
const markers = L.markerClusterGroup();
const store = this.$store;
events.forEach(event => {
let marker = L.marker(e.latlng, { draggable: true })
.on("click", el => {
store.commit("locationsMap_center", e.latlng);
})
//.bindPopup(`<b> ${event.id} </b> ${event.name}`)
.addTo(this.map);
markers.addLayer(marker);
});
map.addLayer(markers);
this.markers = markers;
},
removeEvent() {
this.map.removeLayer(this.markers);
this.markers = null;
}
},
mounted() {
const map = L.map("map", {
contextmenu: true,
contextmenuWidth: 140,
contextmenuItems: [
{
text: "Add Event Here",
callback: this.addEvents
}
]
}).setView([0, 0], 1);
L.tileLayer("/static/map/{z}/{x}/{y}.png", {
maxZoom: 4,
minZoom: 3,
continuousWorld: false,
noWrap: true,
crs: L.CRS.Simple
}).addTo(map);
this.map = map;
}
};
</script>
New2Dis,
Here is your example running in a jsfiddle.
computed: {
events: function () {
return this.store.events;
}
},
watch: {
events: function (val) {
this.removeEvents();
this.addEvents(val);
}
},
methods: {
addEvents(events) {
console.log("hoi")
const map = this.map;
const markers = L.markerClusterGroup();
const store = this.$store;
events.forEach(event => {
let marker = L.marker(event.latlng, { draggable: true })
.on("click", el => {
//store.commit("locationsMap_center", event.latlng);
})
.bindPopup(`<b> ${event.id} </b> ${event.name}`)
.addTo(this.map);
markers.addLayer(marker);
});
map.addLayer(markers);
this.markers = markers;
},
removeEvents() {
if (this.markers != null) {
this.map.removeLayer(this.markers);
this.markers = null;
}
}
},
I did replace some things to make it works, like the $store as I don't have it, and removeEvent was not written correctly, so I'm not sure what I actually fixed...
I have also created a plugin to make it easy to use Leaflet with Vue.
You can find it here
You will also find a plugin for Cluster group here
Give it a try and let me know what you think.
i am having an issue trying to reenable a scrollmagic controller if it has been disabled before.
i want to have the logo color change only triggered if its a narrow viewport (if the logo is in the colored area) and disabled if its wide..that works so far
but if i resize the window to narrow again it won't reenable the controller..i tried to destroy and reset the controller as well but somehow it won't reenable the controller...
codepen (gsap and scrollmagic used):
https://codepen.io/HendrikEng/pen/owyBYz?editors=0011
js:
const mobile = {
controller: new ScrollMagic.Controller(),
changeLogo: {
init: () => {
console.log("init tweens an scrollmagic");
const tweens = {
enterOuter: () => {
TweenMax.fromTo(
".c-logo__outer",
1,
{ fill: "#4dabfc" },
{ fill: "#fff" }
);
},
enterInner: () => {
TweenMax.fromTo(
".c-logo__inner",
1,
{ fill: "#fff" },
{ fill: "#4dabfc" }
);
},
leaveOuter: () => {
TweenMax.fromTo(
".c-logo__outer",
1,
{ fill: "#fff" },
{ fill: "#4dabfc" }
);
},
leaveInner: () => {
TweenMax.fromTo(
".c-logo__inner",
1,
{ fill: "#4dabfc" },
{ fill: "#fff" }
);
}
};
const trigger = document.querySelectorAll(".js-change-logo");
trigger.forEach(id => {
const scene = new ScrollMagic.Scene({
triggerElement: id,
reverse: true,
triggerHook: 0.065,
duration: id.clientHeight
})
.on("enter", () => {
tweens.enterOuter();
tweens.enterInner();
})
.on("leave", () => {
tweens.leaveOuter();
tweens.leaveInner();
})
.addIndicators()
.addTo(mobile.controller);
});
},
destroyTweens: () => {
console.log("kill tweens");
TweenMax.killTweensOf(".c-logo__outer");
TweenMax.killTweensOf(".c-logo__inner");
TweenMax.set(".c-logo__outer", { clearProps: "all" });
TweenMax.set(".c-logo__inner", { clearProps: "all" });
}
}
};
$(window).on("resize", function() {
var win = $(this); //this = window
if (win.width() <= 450) {
// reanble controller if disabledbed before - doesnt work
mobile.controller.enabled(true);
mobile.changeLogo.init();
} else {
// disable scrollmagic controller
mobile.controller.enabled(false);
// destroy tweens
mobile.changeLogo.destroyTweens();
}
}).resize();
#hendrikeng I hope you don't mind, but I changed your code quite a lot. I've found myself needing to do this exact thing numerous times recently, so I based a lot of it on my own work.
I think the largest issue was that you were running a lot of functions on every resize which is not very performant and also makes it difficult to keep track of what's initialised and what's not. Mine relies on an init_flag so that it is only setup once.
There are then methods to update (duration on resize if needed) and destroy.
https://codepen.io/motionimaging/pen/848366af015cdf3a90de5fb395193502/?editors=0100
const mobile = {
init_flag: false,
init: () => {
$(window).on('resize', function(){
const width = $(window).width();
if( width <= 450 ){
if(! mobile.init_flag ){
mobile.setup();
} else {
mobile.update();
}
} else {
if( mobile.init_flag ){
mobile.destroy();
}
}
});
},
setup: () => {
mobile.init_flag = true;
mobile.triggers = document.querySelectorAll('.js-change-logo');
const tweens = {
enterOuter: () => {
TweenMax.fromTo(
'.c-logo__outer',
1,
{ fill: '#4dabfc' },
{ fill: '#fff' }
);
},
enterInner: () => {
TweenMax.fromTo(
'.c-logo__inner',
1,
{ fill: '#fff' },
{ fill: '#4dabfc' }
);
},
leaveOuter: () => {
TweenMax.fromTo(
'.c-logo__outer',
1,
{ fill: '#fff' },
{ fill: '#4dabfc' }
);
},
leaveInner: () => {
TweenMax.fromTo(
'.c-logo__inner',
1,
{ fill: '#4dabfc' },
{ fill: '#fff' }
);
}
};
mobile.controller = new ScrollMagic.Controller();
mobile.triggers.forEach(el => {
el.scene = new ScrollMagic.Scene({
triggerElement: el,
reverse: true,
triggerHook: 0.065,
duration: el.clientHeight
})
.on('enter', () => {
tweens.enterOuter();
tweens.enterInner();
})
.on('leave', () => {
tweens.leaveOuter();
tweens.leaveInner();
})
.addIndicators()
.addTo(mobile.controller);
});
},
update: () => {
if( mobile.init_flag ){
mobile.triggers.forEach(el => {
el.scene.duration(el.clientHeight);
});
}
},
destroy: function(){
mobile.controller.destroy(true);
mobile.init_flag = false;
$('.c-logo > *').attr('style', '');
},
};
mobile.init();
I have a simple react-native client for a website, in login page it provides two option, enter login code manually or scan it using barcode scanner. i've tested the app in real device and emulator lot's of times it's working fine.
Actually i test only over ipv4, and for login im using fetch, which i think is supporting ipv6 by default.
They say over ipv6 network when app was offline, i cannot understand what does it mean to be OFFLINE and be on IPV6 network?
When app is offline, i'm showing error to user that there is no connectivity. so i don't know how it can crash.
should adding a timeout to fetch request fix the issue?
But the app being rejected 3 times due to same error :
Performance - 2.1
Thank you for your resubmission.
Your app crashes on iPhone running iOS 9.3.3 connected to an IPv6
network when we:
Specifically, tapping the login still leads the app to crash.
This occurred when your app was used:
Offline
On Wi-Fi
We have attached detailed crash logs to help troubleshoot this issue.
Here is the login.js :
'use strict';
import React, { Component } from 'react';
import {
Text,
View,
Image,
TextInput,
TouchableOpacity,
ActivityIndicatorIOS,
StyleSheet,
Dimensions,
AlertIOS,
NetInfo,
} from 'react-native';
import Camera from 'react-native-camera';
var { width, height } = Dimensions.get('window');
class Login extends Component {
constructor(props){
super(props);
this.state = {
showProgress: false,
showCamera: false,
cameraType: Camera.constants.Type.back,
barcode: true,
isConnected: false,
}
}
componentWillMount(){
NetInfo.isConnected.fetch().done((data) => {
this.setState({
isConnected: data
})
});
}
_onBarCodeRead(e) {
this.setState({
showCamera: false,
barcodeData: e.data,
logincode: e.data,
success: true,
});
this.onLoginPressed();
}
render(){
if(this.state.showCamera) {
return (
<Camera
ref="cam"
style={styles.container}
onBarCodeRead={this._onBarCodeRead.bind(this)}
type={this.state.cameraType}>
</Camera>
);
} else {
var errorCtrl = <View />;
if(!this.state.success){
errorCtrl = <Text style={styles.error}>
{this.state.message}
</Text>;
}
///// Check login type
if(this.state.barcode){
return(
<View style={styles.container}>
<Image style={styles.logo} source={require('image!logo')} />
<Text style={styles.heading}>
Please use QR-Scanner to login,{'\n'}
or enter the Login code manually.
</Text>
<TouchableOpacity
onPress={this.onQrPressed.bind(this)}
style={styles.button}>
<Text style={styles.buttonText}>Use QR-Scanner</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.toManuall.bind(this)}
>
<Text style={styles.change}>
Want to enter code manually?
</Text>
</TouchableOpacity>
{errorCtrl}
<ActivityIndicatorIOS
animating={this.state.showProgress}
size="large"
style={styles.loader}
/>
</View>
);
} else {
return(
<View style={styles.container}>
<Image style={styles.logo} source={require('image!logo')} />
<Text style={styles.heading}>
Please use QR-Scanner to login,{'\n'}
or enter the Login code manually.
</Text>
<TextInput onChangeText={(text)=> this.setState({logincode: text})} style={styles.loginInput} placeholder={this.state.logincode}>
</TextInput>
<TouchableOpacity onPress={this.onLoginPressed.bind(this)} style={styles.button} >
<Text style={styles.buttonText}>Log in</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.toBarcode.bind(this)}
>
<Text style={styles.change}>
Want to use Barcode?
</Text>
</TouchableOpacity>
{errorCtrl}
<ActivityIndicatorIOS
animating={this.state.showProgress}
size="large"
style={styles.loader}
/>
</View>
);
}
/////
}
}
onLoginPressed(){
if(this.state.isConnected){
/// do the validation
var valid = false;
if(this.state.logincode != undefined && this.state.logincode.includes('opencampus://') && this.state.logincode.includes('access_token=') && this.state.logincode.includes('refresh_token=') && this.state.logincode.includes('id=') && this.state.logincode.includes('name=') && this.state.logincode.includes('scheme=')){
var valid = true;
}
if(valid){
console.log('Login.ios: Attempting to log in with logincode ' + this.state.logincode);
this.setState({showProgress: true});
console.log('Login.ios: calling AuthService class');
var AuthService = require('./AuthService');
AuthService.login({
logincode: this.state.logincode
}, (results)=> {
this.setState(Object.assign({
showProgress: false
}, results));
console.log('Login.ios: AuthService execution finished.', results);
if(results.success && this.props.onLogin){
this.props.onLogin(results);
}
});
} else {
AlertIOS.alert(
'Invalid Input',
'Login code you entered is not valid. Be sure to paste the whole string starting with opencampus://'
);
}
} else {
AlertIOS.alert(
'No Connection',
'Please check your internet connection.'
);
}
}
onQrPressed(){
this.setState({
showCamera: true,
});
}
toManuall(){
this.setState({
barcode: false,
});
}
toBarcode(){
this.setState({
barcode: true,
});
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: '#00a2dd',
paddingTop: 40,
padding: 10,
alignItems: 'center',
flex: 1,
justifyContent: 'center'
},
logo: {
width: 141,
height: 137,
},
heading: {
fontSize: 18,
margin: 10,
marginBottom: 20,
color: '#FFFFFF',
paddingTop: 50,
},
change: {
fontSize: 12,
color: '#FFFFFF',
marginTop:10,
},
loginInput: {
height: 50,
marginTop: 10,
padding: 4,
fontSize: 18,
borderWidth: 1,
borderColor: '#FFFFFF',
borderRadius: 0,
color: '#FFFFFF'
},
button: {
height: 50,
backgroundColor: '#48BBEC',
borderColor: '#48BBEC',
alignSelf: 'stretch',
marginTop: 10,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 5
},
buttonText: {
color: '#fff',
fontSize: 24
},
loader: {
marginTop: 20
},
error: {
color: 'red',
paddingTop: 10
}
});
module.exports = Login;
Here is AuthService.js :
'use strict';
import React, { Component } from 'react';
var SQLite = require('react-native-sqlite-storage');
var DeviceInfo = require('react-native-device-info');
class AuthService extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: false
}
this.errorCB = this.errorCB.bind(this);
this.successCB = this.successCB.bind(this);
}
errorCB(err) {
console.log("Auth Service: error: ", err);
this.state.progress.push("Error: " + (err.message || err));
return false;
}
successCB() {
}
login(creds, cb){
var db = SQLite.openDatabase({name : "oc.db", location: 'default'}, this.successCB.bind(this), this.errorCB.bind(this));
var sql = 'CREATE TABLE IF NOT EXISTS users ('
+ 'access_token text NOT NULL,'
+ 'refresh_token text NOT NULL,'
+ 'userName text NOT NULL,'
+ 'userId text NOT NULL,'
+ 'userMail text NOT NULL,'
+ 'userSignature text NOT NULL,'
+ 'userSignatureFormat text NOT NULL,'
+ 'userCreated text NOT NULL,'
+ 'userAccess text NOT NULL,'
+ 'userLogin text NOT NULL,'
+ 'userStatus text NOT NULL,'
+ 'userTimezone text NOT NULL,'
+ 'userLanguage text NOT NULL,'
+ 'userRoles text NOT NULL,'
+ 'deviceId text NOT NULL,'
+ 'deviceName text NOT NULL,'
+ 'host text NOT NULL,'
+ 'active text NOT NULL'
+ ');';
db.executeSql(sql, [],
this.successCB.bind(this),
this.errorCB.bind(this)
);
var LCode = creds.logincode;
var codeSplited = LCode.split("://");
var codeSplited2 = codeSplited[1].split("?");
var appName = codeSplited[0];
var serverName = codeSplited2[0];
var splitedVars = codeSplited2[1].split("&");
var access_token = splitedVars[0].split("=");
var access_token = access_token[1];
var refresh_token = splitedVars[1].split("=");
var refresh_token = refresh_token[1];
var uid = splitedVars[2].split("=");
var uid = uid[1];
var uname = splitedVars[3].split("=");
var uname = uname[1];
var scheme = splitedVars[4].split("=");
var scheme = scheme[1];
var device_id = DeviceInfo.getUniqueID();
var device_name = DeviceInfo.getDeviceName();
var locale = DeviceInfo.getDeviceLocale();
console.log('AuthService: Try to fetch from : ', serverName);
console.log('request body: ', JSON.stringify({
uid: uid,
refresh_token: refresh_token,
token: access_token,
device: device_id,
device_name: device_name,
}));
fetch(scheme + '://' + serverName, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'language': locale,
'Authorization': 'Bearer ' + access_token,
},
body: JSON.stringify({
uid: uid,
refresh_token: refresh_token,
token: access_token,
device: device_id,
device_name: device_name,
})
})
.then((response)=> {
return response;
})
.then((response)=> {
return response.json();
})
.then((results)=> {
console.log(results);
if(results['result'] == 1){
console.log('Auth Service: Login was successfull');
// User data
var userName = results['session']['user']['name'];
var userId = results['session']['user']['uid'];
var userMail = results['session']['user']['mail'];
var userSignature = results['session']['user']['signature'];
var userSignatureFormat = results['session']['user']['signature_format'];
var userCreated = results['session']['user']['created'];
var userAccess = results['session']['user']['access'];
var userLogin = results['session']['user']['login'];
var userStatus = results['session']['user']['status'];
var userTimezone = results['session']['user']['timezone'];
var userLanguage = results['session']['user']['language'];
var userRoles = results['session']['user']['roles']['2'];
var host = results['session']['user']['host'];
var active = 'yes';
//var userPicture = results['session']['user']['picture'];
console.log('Auth Service: Lets save user data to database');
var query = "INSERT INTO users (access_token, refresh_token, userName, userId, userMail, userSignature, userSignatureFormat, userCreated, userAccess, userLogin, userStatus, userTimezone, userLanguage, userRoles, deviceId, deviceName, host, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
var params = [access_token, refresh_token, userName,userId,userMail,userSignature,userSignatureFormat,userCreated,userAccess,userLogin,userStatus,userTimezone,userLanguage,userRoles,device_id,device_name,host,active];
db.executeSql(query,params,
this.successCB.bind(this),
this.errorCB.bind(this)
);
return cb({
success: true,
userData: results['session']['user']
});
} else if(results['result'] == 0){
console.log('Auth Service: Login failed message is ' + results['message']);
return cb({
success: false,
message: results['message']
});
} else {
console.log('Auth Service: Login failed error is ' + results['error_description']);
return cb({
success: false,
message: results['error_description']
});
}
})
.catch((err)=> {
console.log('AuthService: ' + err);
return cb(err);
})
.done();
}
}
module.exports = new AuthService();
And here is Index.js :
"use strict";
import React, {Component, PropTypes} from 'react';
import {
AppRegistry,
NavigatorIOS,
StyleSheet,
TabBarIOS,
View,
Text,
StatusBar,
} from 'react-native';
var CourseList = require("./app/CourseList");
var Profile = require("./app/Profile");
import Icon from 'react-native-vector-icons/Ionicons';
var SQLite = require('react-native-sqlite-storage');
var Login = require("./app/Login");
var db = SQLite.openDatabase({name : "oc.db", location: 'default'});
StatusBar.setBarStyle('light-content');
class OpenCampus extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: "Courses",
isLoggedIn: false,
userId: null,
};
}
componentWillMount(){
var query = "SELECT * FROM users WHERE active='yes'";
var params = [];
db.transaction((tx) => {
tx.executeSql(query,params, (tx, results) => {
var len = results.rows.length;
if(len > 0){
let row = results.rows.item(0);
this.setState({
isLoggedIn: true,
userId: row.userId
});
}
}, function(){
console.log('index: Something went wrong');
});
});
}
onLogin(results) {
this.setState({
isLoggedIn: true,
});
}
logout() {
console.log("Logout called from index");
var query = "DELETE FROM users WHERE userId=?";
var params = [this.state.userId];
db.transaction((tx) => {
tx.executeSql(query,params, (tx, results) => {
///// check if there is other accounts on database, if yes, make first row active
var query = "SELECT * FROM users WHERE active='yes'";
var params = [];
db.transaction((tx) => {
tx.executeSql(query,params, (tx, results) => {
var len = results.rows.length;
if(len > 0){
let row = results.rows.item(0);
userId = row.userId;
///// Set new user active
var query = "UPDATE users SET active='yes' WHERE userId=?";
var params = [userId];
db.transaction((tx) => {
tx.executeSql(query,params, (tx, results) => {
console.log('index: Active Account Changed');
}, function(){
console.log('index: Something went wrong');
});
});
///////
this.setState({
isLoggedIn: true,
userId: userId,
});
} else {
this.setState({
isLoggedIn: false,
userId: null,
});
}
}, function(){
console.log('index: Something went wrong');
});
});
/////
}, function(){
console.log('index: Something went wrong when logging out');
});
});
}
_renderCourses() {
return (
<NavigatorIOS style={styles.wrapper}
barTintColor='#00a2dd'
titleTextColor='#fff'
tintColor='#ffffff'
ref='RCourses'
initialRoute={{
component: CourseList,
title: 'Courses',
passProps: {filter: 'Courses'},
}}
/>
);
}
_renderRegister() {
return (
<NavigatorIOS style={styles.wrapper}
barTintColor='#00a2dd'
titleTextColor='#fff'
tintColor='#ffffff'
ref='RRegister'
initialRoute={{
component: CourseList,
title: 'Register',
passProps: {filter: 'Register'},
}}
/>
);
}
_renderProfile() {
return (
<NavigatorIOS style={styles.wrapper}
barTintColor='#00a2dd'
titleTextColor='#fff'
tintColor='#ffffff'
ref='RProfile'
initialRoute={{
component: Profile,
title: 'Profile',
passProps: {filter: 'Profile'},
rightButtonTitle: 'Logout',
onRightButtonPress: () => this.logout(),
leftButtonTitle: 'Add Account',
onLeftButtonPress: () => this.addnew(),
}}
/>
);
}
addnew() {
console.log('Send user to login page to add new account');
//// Set old user to inactive
var query = "UPDATE users SET active='no' WHERE active='yes'";
var params = [this.state.userId];
db.transaction((tx) => {
tx.executeSql(query,params, (tx, results) => {
//// Set login status to false so login screen will be shown
console.log(results);
this.setState({
isLoggedIn: false,
userId: null,
});
}, function(){
console.log('index: Something went wrong when adding new account');
});
});
}
popAll(){
if(typeof this.refs.RCourses !== typeof undefined){
this.refs.RCourses.popToTop();
}
if(typeof this.refs.RRegister !== typeof undefined){
this.refs.RRegister.popToTop();
}
if(typeof this.refs.RProfile !== typeof undefined){
this.refs.RProfile.popToTop();
}
}
render() {
if(!this.state.isLoggedIn){
console.log('index: User not logged in. redirecting to Login page.');
return(
<Login onLogin={this.onLogin.bind(this)} />
);
} else {
console.log('index: User is logged in lets show the content');
return (
<TabBarIOS tintColor={"#00a2dd"}>
<Icon.TabBarItem
title="Courses"
iconName="ios-list-outline"
selectedIconName="ios-list-outline"
selected={this.state.selectedTab === "Courses"}
onPress={() => {
this.setState({
selectedTab: "Courses",
});
this.popAll();
}}>
{this._renderCourses()}
</Icon.TabBarItem>
<Icon.TabBarItem
title="Register"
iconName="ios-book"
selectedIconName="ios-book"
selected={this.state.selectedTab === "Register"}
onPress={() => {
this.setState({
selectedTab: "Register",
});
this.popAll();
}}>
{this._renderRegister()}
</Icon.TabBarItem>
<Icon.TabBarItem
title="Profile"
iconName="ios-person"
selectedIconName="ios-person"
selected={this.state.selectedTab === "Profile"}
onPress={() => {
this.setState({
selectedTab: "Profile",
});
this.popAll();
}}>
{this._renderProfile()}
</Icon.TabBarItem>
</TabBarIOS>
);
}
}
}
var styles = StyleSheet.create({
tabContent: {
flex: 1,
alignItems: "center",
},
tabText: {
color: "white",
margin: 50,
},
wrapper: {
flex: 1,
backgroundColor: '#00a2dd',
}
});
AppRegistry.registerComponent('OpenCampus', () => OpenCampus);
UPDATE :
here is the crash log by apple : http://www.ataomega.com/temp..suczkfac.crash
http://www.ataomega.com/temp..hsbgdlod.crash
You should test your app for ipv6 compatibility. Here is a tutorial that explains how to do that.
Boot OS X 10.11
Make sure your Mac is connected to the Internet, but not through Wi-Fi.
Launch System Preferences from your Dock, LaunchPad, or the Apple menu.
Press the Option key and click Sharing. Don’t release the Option key yet.
Select Internet Sharing in the list of sharing services.
Release the Option key.
Select the Create NAT64 Network checkbox.
Choose the network interface that provides your Internet connection, such as Thunderbolt Ethernet.
Select the Wi-Fi checkbox.
Click Wi-Fi Options, and configure the network name and security options for your network.
Setting up local Wi-Fi network options
Select the Internet Sharing checkbox to enable your local network.
When prompted to confirm you want to begin sharing, click Start.
Once sharing is active, you should see a green status light and a label that says Internet Sharing: On. In the Wi-Fi menu, you will also see a small, faint arrow pointing up, indicating that Internet Sharing is enabled. You now have an IPv6 NAT64 network and can connect to it from other devices in order to test your app.
I want to add exported component on TouchableHighlight in react-native.
var MessageBox = require('./GiftedMessengerContainer');
var MyAppName = React.createClass({
_openGiftedMessanger(){
return (<MessageBox style={styles.container}/>);
},
render: function() {
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.imButton}
onPress={this._openGiftedMessanger}>
<Text>Open Chat Room</Text>
</TouchableHighlight>
}
</View>
);
}
AppRegistry.registerComponent('MyAppName', () => AppName);
And my module is,
import React, {
Linking,
Platform,
ActionSheetIOS,
Dimensions,
View,
Text,
//Navigator,
Component,
} from 'react-native';
var GiftedMessenger = require('react-native-gifted-messenger');
var Communications = require('react-native-communications');
// var STATUS_BAR_HEIGHT = Navigator.NavigationBar.Styles.General.StatusBarHeight;
// if (Platform.OS === 'android') {
// var ExtraDimensions = require('react-native-extra-dimensions-android');
// var STATUS_BAR_HEIGHT = ExtraDimensions.get('STATUS_BAR_HEIGHT');
// }
class GiftedMessengerContainer extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this._messages = this.getInitialMessages();
this.state = {
messages: this._messages,
isLoadingEarlierMessages: false,
typingMessage: '',
allLoaded: false,
};
}
componentDidMount() {
this._isMounted = true;
setTimeout(() => {
this.setState({
typingMessage: 'React-Bot is typing a message...',
});
}, 1000); // simulating network
setTimeout(() => {
this.setState({
typingMessage: '',
});
}, 3000); // simulating network
setTimeout(() => {
this.handleReceive({
text: 'Hello Awesome Developer',
name: 'React-Bot',
image: {uri: 'https://facebook.github.io/react/img/logo_og.png'},
position: 'left',
date: new Date(),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
});
}, 3300); // simulating network
}
componentWillUnmount() {
this._isMounted = false;
}
getInitialMessages() {
return [
{
text: 'Are you building a chat app?',
name: 'React-Bot',
image: {uri: 'https://facebook.github.io/react/img/logo_og.png'},
position: 'left',
date: new Date(2016, 3, 14, 13, 0),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
},
{
text: "Yes, and I use Gifted Messenger!",
name: 'Awesome Developer',
image: null,
position: 'right',
date: new Date(2016, 3, 14, 13, 1),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
},
];
}
setMessageStatus(uniqueId, status) {
let messages = [];
let found = false;
for (let i = 0; i < this._messages.length; i++) {
if (this._messages[i].uniqueId === uniqueId) {
let clone = Object.assign({}, this._messages[i]);
clone.status = status;
messages.push(clone);
found = true;
} else {
messages.push(this._messages[i]);
}
}
if (found === true) {
this.setMessages(messages);
}
}
setMessages(messages) {
this._messages = messages;
// append the message
this.setState({
messages: messages,
});
}
handleSend(message = {}) {
// Your logic here
// Send message.text to your server
message.uniqueId = Math.round(Math.random() * 10000); // simulating server-side unique id generation
this.setMessages(this._messages.concat(message));
// mark the sent message as Seen
setTimeout(() => {
this.setMessageStatus(message.uniqueId, 'Seen'); // here you can replace 'Seen' by any string you want
}, 1000);
// if you couldn't send the message to your server :
// this.setMessageStatus(message.uniqueId, 'ErrorButton');
}
onLoadEarlierMessages() {
// display a loader until you retrieve the messages from your server
this.setState({
isLoadingEarlierMessages: true,
});
// Your logic here
// Eg: Retrieve old messages from your server
// IMPORTANT
// Oldest messages have to be at the begining of the array
var earlierMessages = [
{
text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. https://github.com/facebook/react-native',
name: 'React-Bot',
image: {uri: 'https://facebook.github.io/react/img/logo_og.png'},
position: 'left',
date: new Date(2016, 0, 1, 20, 0),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
}, {
text: 'This is a touchable phone number 0606060606 parsed by taskrabbit/react-native-parsed-text',
name: 'Awesome Developer',
image: null,
position: 'right',
date: new Date(2016, 0, 2, 12, 0),
uniqueId: Math.round(Math.random() * 10000), // simulating server-side unique id generation
},
];
setTimeout(() => {
this.setMessages(earlierMessages.concat(this._messages)); // prepend the earlier messages to your list
this.setState({
isLoadingEarlierMessages: false, // hide the loader
allLoaded: true, // hide the `Load earlier messages` button
});
}, 1000); // simulating network
}
handleReceive(message = {}) {
// make sure that your message contains :
// text, name, image, position: 'left', date, uniqueId
this.setMessages(this._messages.concat(message));
}
onErrorButtonPress(message = {}) {
// Your logic here
// re-send the failed message
// remove the status
this.setMessageStatus(message.uniqueId, '');
}
// will be triggered when the Image of a row is touched
onImagePress(message = {}) {
// Your logic here
// Eg: Navigate to the user profile
}
render() {
return (
<GiftedMessenger
ref={(c) => this._GiftedMessenger = c}
styles={{
bubbleRight: {
marginLeft: 70,
backgroundColor: '#007aff',
},
}}
autoFocus={false}
messages={this.state.messages}
handleSend={this.handleSend.bind(this)}
onErrorButtonPress={this.onErrorButtonPress.bind(this)}
maxHeight={Dimensions.get('window').height} //- Navigator.NavigationBar.Styles.General.NavBarHeight - STATUS_BAR_HEIGHT}
loadEarlierMessagesButton={!this.state.allLoaded}
onLoadEarlierMessages={this.onLoadEarlierMessages.bind(this)}
senderName='Awesome Developer'
senderImage={null}
onImagePress={this.onImagePress}
displayNames={true}
parseText={true} // enable handlePhonePress, handleUrlPress and handleEmailPress
handlePhonePress={this.handlePhonePress}
handleUrlPress={this.handleUrlPress}
handleEmailPress={this.handleEmailPress}
isLoadingEarlierMessages={this.state.isLoadingEarlierMessages}
typingMessage={this.state.typingMessage}
/>
);
}
handleUrlPress(url) {
Linking.openURL(url);
}
// TODO
// make this compatible with Android
handlePhonePress(phone) {
if (Platform.OS !== 'android') {
var BUTTONS = [
'Text message',
'Call',
'Cancel',
];
var CANCEL_INDEX = 2;
ActionSheetIOS.showActionSheetWithOptions({
options: BUTTONS,
cancelButtonIndex: CANCEL_INDEX
},
(buttonIndex) => {
switch (buttonIndex) {
case 0:
Communications.phonecall(phone, true);
break;
case 1:
Communications.text(phone);
break;
}
});
}
}
handleEmailPress(email) {
Communications.email(email, null, null, null, null);
}
}
module.exports = GiftedMessengerContainer;
How to add custom views on my screen?
You need to make use of something called as states (in React terms). When onPress function is invoked you set a state variable to open/close which then can be used to show/hide the custom view. For ex:
var MessageBox = require('./GiftedMessengerContainer');
var MyAppName = React.createClass({
getInitialState: function(){
return {
messageBoxShow: 'false'
}
},
_openGiftedMessanger:function(){
this.setState({
messageBoxShow: 'true'
});
},
render: function() {
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.imButton}
onPress={this._openGiftedMessanger}>
<Text>Open Chat Room</Text>
</TouchableHighlight>
{this.state.messageBoxShow === 'true' ? <MessageBox style={styles.container}/> : null };
</View>
);
}
AppRegistry.registerComponent('MyAppName', () => AppName);