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.
Related
I get the following error:
here is my code:
return (
<>
{Object.values(providers).map((provider) => {
if (provider.id === "credentials") {
return null;
}
return (
<div key={provider.name}>
<Button
variant="outlined"
onClick={() => {
setAppLoading(true);
signIn(provider.id);
setAppLoading(false);
}}
className="w-full uppercase !transform-none mt-4"
>
<Image
height={24}
width={24}
src={`/logos/${provider.name}.svg` || "/logos/google.svg"}
alt={provider.name}
/>
<span className="ml-2 !text-[rgba(26, 26, 44, 0.5)]">
Sign in with {provider.name}
</span>
</Button>
</div>
);
})}
</>
);
};
If I silence the error by adding { providers !=== null && ... }, the page loads however,
the auth does not work at all, almost as if it is not getting fired
Here is the Form Button onSubmit, which gets called when I click it however, the signIn("credentials...}) doesn't fire
import { ClientSafeProvider, signIn } from "next-auth/client";
...
onSubmit={async (values, { setErrors }) => {
const { email, password } = values;
setAppLoading(true);
signIn("credentials", {
email,
password,
redirect: false,
}).then((res) => {
setErrors({ error: res?.error });
});
setAppLoading(false);
}}
...
and lastly, here is my [...nextauth].ts
import login from "features/auth/login";
import register from "features/auth/register";
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
export default NextAuth({
providers: [
Providers.Credentials({
name: "Credentials",
credentials: {
name: { label: "Name", type: "text" },
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, _req) {
const { name, email, password } = credentials;
alert("clicked");
console.log("CLICKEEEED!!!!!!!!!!!!!!");
if (typeof name === "undefined") {
return login(email, password);
}
return register(name, email, password);
},
}),
Providers.Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
pages: {
signIn: "/auth/signin",
signOut: "/auth/signout",
error: "/auth/error", // Error code passed in query string as ?error=
verifyRequest: "/auth/verify-request", // (used for check email message)
},
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60, // 30 days
},
});
Are you certain that you have provided valid GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in your .env.local file? This is the most common cause of your error. This would mean that next-auth did not provide you with providers, because of an error in the api.
I just started to learn flutter. I wrote an api with Nodejs and deployed to heroku.
I tested the api by Postman and browser, it works fine with strings and json and pdf file.
How can I make flutter to download pdf file from api
My nodejs api
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/getPdfFile/:Name', function(req, res, next) {
var path = require('path');
console.log(__dirname);
var file = path.join(__dirname, '../pdfs/'+req.params.Name+'.pdf');
res.download(file, function (err) {
if (err) {
console.log("Error");
console.log(err);
} else {
console.log("Success");
}
});
});
router.get('/getName', function(req, res, next) {
res.json({"foo": "bar"});
});
router.get('/getNamee', function(req, res, next) {
res.send('some string');
});
My flutter main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(new MaterialApp(
home: new HomePage(),
));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> {
List data;
//apidenemee4.herokuapp.com/getPdfFile/SpaceX
Future getData() async {
var response = await http.get(
Uri.encodeFull("https://mynodeapi4.herokuapp.com//getPdfFile/SpaceX"),
headers: {"Accept": "application/json"});
print(response.body);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new RaisedButton(
child: new Text("Get data"),
onPressed: getData,
),
),
);
}
}
You can use flutter_downloader - to download and open the file, path_provider- to access device paths and permission_handler - to handle the device storage permissions.
Please add following permissions in AndroidManifest.xml for Android devices
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Add the provider inside application tag
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.snj07.flutter_app_stw.flutter_downloader.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
Create a folder with xml name inside src/main and add the following content in provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
Please customize the following example to download the PDF file and open it in your project. It's taken from flutter_downloader example. I also updated some code for permission handling as per the latest plugin.
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
const debug = true;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(debug: debug);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(
title: 'Downloader',
platform: platform,
),
);
}
}
class MyHomePage extends StatefulWidget with WidgetsBindingObserver {
final TargetPlatform platform;
MyHomePage({Key key, this.title, this.platform}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _documents = [
{
'name': 'PDF',
'link':
'http://darwinlogic.com/uploads/education/iOS_Programming_Guide.pdf'
},
];
List<_TaskInfo> _tasks;
List<_ItemHolder> _items;
bool _isLoading;
bool _permissionReady;
String _localPath;
ReceivePort _port = ReceivePort();
#override
void initState() {
super.initState();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
_isLoading = true;
_permissionReady = false;
_prepare();
}
#override
void dispose() {
_unbindBackgroundIsolate();
super.dispose();
}
void _bindBackgroundIsolate() {
bool isSuccess = IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
_bindBackgroundIsolate();
return;
}
_port.listen((dynamic data) {
if (debug) {
print('UI Isolate Callback: $data');
}
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
final task = _tasks?.firstWhere((task) => task.taskId == id);
if (task != null) {
setState(() {
task.status = status;
task.progress = progress;
});
}
});
}
void _unbindBackgroundIsolate() {
IsolateNameServer.removePortNameMapping('downloader_send_port');
}
static void downloadCallback(
String id, DownloadTaskStatus status, int progress) {
if (debug) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
}
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: Builder(
builder: (context) => _isLoading
? new Center(
child: new CircularProgressIndicator(),
)
: _permissionReady
? new Container(
child: new ListView(
padding: const EdgeInsets.symmetric(vertical: 16.0),
children: _items
.map((item) => item.task == null
? new Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
child: Text(
item.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 18.0),
),
)
: new Container(
padding: const EdgeInsets.only(
left: 16.0, right: 8.0),
child: InkWell(
onTap: item.task.status ==
DownloadTaskStatus.complete
? () {
_openDownloadedFile(item.task)
.then((success) {
if (!success) {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text(
'Cannot open this file')));
}
});
}
: null,
child: new Stack(
children: <Widget>[
new Container(
width: double.infinity,
height: 64.0,
child: new Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new Text(
item.name,
maxLines: 1,
softWrap: true,
overflow:
TextOverflow.ellipsis,
),
),
new Padding(
padding:
const EdgeInsets.only(
left: 8.0),
child: _buildActionForTask(
item.task),
),
],
),
),
item.task.status ==
DownloadTaskStatus
.running ||
item.task.status ==
DownloadTaskStatus.paused
? new Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child:
new LinearProgressIndicator(
value: item.task.progress /
100,
),
)
: new Container()
]
.where((child) => child != null)
.toList(),
),
),
))
.toList(),
),
)
: new Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 24.0),
child: Text(
'Please grant accessing storage permission to continue -_-',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blueGrey, fontSize: 18.0),
),
),
SizedBox(
height: 32.0,
),
FlatButton(
onPressed: () {
_checkPermission().then((hasGranted) {
setState(() {
_permissionReady = hasGranted;
});
});
},
child: Text(
'Retry',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 20.0),
))
],
),
),
)),
);
}
Widget _buildActionForTask(_TaskInfo task) {
if (task.status == DownloadTaskStatus.undefined) {
return new RawMaterialButton(
onPressed: () {
_requestDownload(task);
},
child: new Icon(Icons.file_download),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.running) {
return new RawMaterialButton(
onPressed: () {
_pauseDownload(task);
},
child: new Icon(
Icons.pause,
color: Colors.red,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.paused) {
return new RawMaterialButton(
onPressed: () {
_resumeDownload(task);
},
child: new Icon(
Icons.play_arrow,
color: Colors.green,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.complete) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Text(
'Open',
style: new TextStyle(color: Colors.green),
),
RawMaterialButton(
onPressed: () {
_delete(task);
},
child: Icon(
Icons.delete_forever,
color: Colors.red,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else if (task.status == DownloadTaskStatus.canceled) {
return new Text('Canceled', style: new TextStyle(color: Colors.red));
} else if (task.status == DownloadTaskStatus.failed) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Text('Failed', style: new TextStyle(color: Colors.red)),
RawMaterialButton(
onPressed: () {
_retryDownload(task);
},
child: Icon(
Icons.refresh,
color: Colors.green,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else {
return null;
}
}
void _requestDownload(_TaskInfo task) async {
task.taskId = await FlutterDownloader.enqueue(
url: task.link,
headers: {"auth": "test_for_sql_encoding"},
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true);
}
void _cancelDownload(_TaskInfo task) async {
await FlutterDownloader.cancel(taskId: task.taskId);
}
void _pauseDownload(_TaskInfo task) async {
await FlutterDownloader.pause(taskId: task.taskId);
}
void _resumeDownload(_TaskInfo task) async {
String newTaskId = await FlutterDownloader.resume(taskId: task.taskId);
task.taskId = newTaskId;
}
void _retryDownload(_TaskInfo task) async {
String newTaskId = await FlutterDownloader.retry(taskId: task.taskId);
task.taskId = newTaskId;
}
Future<bool> _openDownloadedFile(_TaskInfo task) {
return FlutterDownloader.open(taskId: task.taskId);
}
void _delete(_TaskInfo task) async {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await _prepare();
setState(() {});
}
Future<bool> _checkPermission() async {
if (widget.platform == TargetPlatform.android) {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
if (statuses[Permission.storage] == PermissionStatus.denied) {
if (await Permission.contacts.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
Future<Null> _prepare() async {
final tasks = await FlutterDownloader.loadTasks();
int count = 0;
_tasks = [];
_items = [];
_tasks.addAll(_documents.map((document) =>
_TaskInfo(name: document['name'], link: document['link'])));
_items.add(_ItemHolder(name: 'Documents'));
for (int i = count; i < _tasks.length; i++) {
_items.add(_ItemHolder(name: _tasks[i].name, task: _tasks[i]));
count++;
}
tasks?.forEach((task) {
for (_TaskInfo info in _tasks) {
if (info.link == task.url) {
info.taskId = task.taskId;
info.status = task.status;
info.progress = task.progress;
}
}
});
_permissionReady = await _checkPermission();
_localPath = (await _findLocalPath()) + Platform.pathSeparator + 'Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
setState(() {
_isLoading = false;
});
}
Future<String> _findLocalPath() async {
final directory = widget.platform == TargetPlatform.android
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
return directory.path;
}
}
class _TaskInfo {
final String name;
final String link;
String taskId;
int progress = 0;
DownloadTaskStatus status = DownloadTaskStatus.undefined;
_TaskInfo({this.name, this.link});
}
class _ItemHolder {
final String name;
final _TaskInfo task;
_ItemHolder({this.name, this.task});
}
I have a webpart with sharepoint framework and reactjs and i use hellojs --> hellojs
I do: npm install hellojs --save and all works fine in edge, chrome and firefox but i need this in Internet Explorer 11.
I try with a sample in js and html and works fine in internet explorer 11 but not in my sharepoint project. I have this:
import * as React from 'react';
import * as hello from 'hellojs';
import { Event } from '../interfaces/Event';
export class Authentication extends React.Component<{}, { sendEvent: boolean }> {
private refreshTokenInterval: number;
constructor(public props, public context) {
super(props, context);
this.state = {
sendEvent: true
};
}
public login(network) {
hello.init({
aad: {
name: 'Azure Active Directory',
oauth: {
version: 2,
auth: 'https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize',
grant: 'https://login.microsoftonline.com/tenant/oauth2/v2.0/token'
},
// Authorization scopes
scope: {
// you can add as many scopes to the mapping as you want here
profile: 'Group.Read.All',
offline_access: ''
},
scope_delim: ' ',
login: (p) => {
if (p.qs.response_type === 'code') {
// Let's set this to an offline access to return a refresh_token
p.qs.access_type = 'offline_access';
}
},
base: 'https://www.graph.microsoft.com/v1.0/',
get: {
me: 'me'
},
xhr: (p) => {
if (p.method === 'post' || p.method === 'put') {
JSON.parse(p);
} else if (p.method === 'patch') {
hello.utils.extend(p.query, p.data);
p.data = null;
}
return true;
},
// Don't even try submitting via form.
// This means no POST operations in <=IE9
form: false
}
});
hello.init(
{
aad: 'clientId'
},
{
redirect_uri: 'my redirect',
//redirect_uri: 'https://localhost:4321/temp/workbench.html',
scope: 'Group.Read.All'
}
);
// By defining response type to code, the OAuth flow that will return a refresh token to be used to refresh the access token
// However this will require the oauth_proxy server
hello(network).login({ display: 'none' }).then(
(authInfo) => {
console.log(authInfo);
localStorage.setItem('logged', authInfo.authResponse.access_token);
localStorage.setItem('timeToRefresh', authInfo.authResponse.expires_in.toString());
this.props.setEvent(Event.GET_ALL_GROUPS);
this.setState({ sendEvent: false });
clearInterval(this.refreshTokenInterval);
this.refreshTokenInterval = window.setInterval(() => {
let timeToRefresh = Number(localStorage['timeToRefresh']) - 1;
localStorage.setItem('timeToRefresh', timeToRefresh.toString());
if (timeToRefresh <= 200) {
localStorage.clear();
sessionStorage.clear();
}
}, 1000);
},
(e) => {
console.error('Signin error: ' + e.error.message);
}
);
}
public componentDidMount() {
let logged = localStorage['logged'];
if (logged === undefined) this.login('aad');
else {
if (this.state.sendEvent) {
this.props.setEvent(null);
this.props.setEvent(Event.GET_ALL_GROUPS);
}
}
}
public render() {
return null;
}
/*private logout(network) {
// Removes all sessions, need to call AAD endpoint to do full logout
hello(network).logout({ force: true }, console.log).then(
function() {
console.log('Out');
},
function(e) {
console.error('Sign out error: ' + e.error.message);
}
);
}*/
}
And i call this class in a main class:
public render(): JSX.Element {
return (
<div className="row">
<div className="col-md-2" style={{ maxWidth: '250px' }}>
<LeftPanel setEvent={this.getEvent} />
</div>
<div className="col-md-10">
<Authentication setEvent={this.getEvent} />
<CenterPanel event={this.state.event} context={this.props.context} />
</div>
</div>
);
}
Console in internet explorer 11:
Solved!
cd "folder with the package.json"
npm install url-polyfill --save
import 'url-polyfill';
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);
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.