React Native atob() / btoa() not working without remote JS debugging - react-native

I have a testing app in react native, and all works fine when I have enabled the debug js remotely. It works fine in device (from XCode) and simulator, after run:
react-native run ios
The problem is that if I stop remote js debugging, the login test not works anymore.The login logic is very simple, I'm making a fetch to an api to test a login, the API endpoint is over https.
What I need to change?
Updated: This code works perfetly with JS Debug Remote Enabled, if I disable it, it not works anymore.
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react'
import {
AppRegistry,
StyleSheet,
View,
Button,
Alert
} from 'react-native'
export default class MyClass extends Component {
constructor (props) {
super(props)
this.testFetch = this.testFetch.bind(this)
}
async testFetch () {
const email = 'email#example.com'
const password = '123456'
try {
const response = await fetch('https://www.example.com/api/auth/login', {
/* eslint no-undef: 0 */
method: 'POST',
headers: {
'Accept': 'application/json' /* eslint quote-props: 0 */,
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(email + ':' + password)
}
})
Alert.alert('Error fail!', 'Fail')
console.log(response)
} catch (error) {
Alert.alert('Error response!', 'Ok')
}
}
render () {
return (
<View style={styles.container}>
<Button
onPress={this.testFetch}
title="Test me!"
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5
}
})
AppRegistry.registerComponent('testingReactNative', () => MyClass)
Thanks.

That's the ways I fixed it. As #chemitaxis suggests, add base-64 module from NPM:
npm i -S base-64
Based on it, I propose a couple of ways to use it:
Importing it in files you need it
Then, you can import 'encode' and 'decode' methods using aliases, this way:
import {decode as atob, encode as btoa} from 'base-64'
Of course, using aliases is optional.
Polyfill way
You can set atob and btoa as global variables on React Native. Then, you won't need to import them on each file you need it. You have to add this code:
import {decode, encode} from 'base-64'
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
You need to place it at the beginning of your index.js, so that it can be loaded before another file uses atob and btoa.
I suggest you to copy it on a separate file (let's say base64Polyfill.js), and then import it on index.js

Here you go (https://sketch.expo.io/BktW0xdje). Create a separate component (e.g. Base64.js), import it and it's ready to use. For instance Base64.btoa('123');
// #flow
// Inspired by: https://github.com/davidchambers/Base64.js/blob/master/base64.js
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const Base64 = {
btoa: (input:string = '') => {
let str = input;
let output = '';
for (let block = 0, charCode, i = 0, map = chars;
str.charAt(i | 0) || (map = '=', i % 1);
output += map.charAt(63 & block >> 8 - i % 1 * 8)) {
charCode = str.charCodeAt(i += 3/4);
if (charCode > 0xFF) {
throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
},
atob: (input:string = '') => {
let str = input.replace(/=+$/, '');
let output = '';
if (str.length % 4 == 1) {
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (let bc = 0, bs = 0, buffer, i = 0;
buffer = str.charAt(i++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
buffer = chars.indexOf(buffer);
}
return output;
}
};
export default Base64;

The main part of your question has been answered, but I see there's still some uncertainty about why it works with remote debugging enabled.
When debugging remotely, the JavaScript code is actually running in Chrome, and the diffs to the virtual dom are being communicated to the native app over a web socket.
http://facebook.github.io/react-native/docs/javascript-environment
atob and btoa are available in the context of the browser, and that's why it works there.
When you stop debugging, though, the JavaScript is again interpreted in a process on your device or simulator, which doesn't have access to functions that are provided by the browser.

Related

React Native create text highlight styled Text component in Text component by function

I am currently working on small project.
What I am trying to do is to create text hightlight in my speech to text react native app
My project has Flask API server to detect text data whether it is voice phishing or not.
I am using [react-native-voice]https://github.com/react-native-voice/voice for speech to text
my react native app will send text data segment every 5 seconds to Flask API server to find out whether it is voice phishing or not then Flask API server will return score that is 0 to 1 so over 0.5 is voice phishing
anyway what i am trying to say is that when the text data is considered to be voice phishing (score over 0.5) text data on my app will be highlighted
here is my TextBox Component code
is that possible my setHighlight function
input
hi my name is woo young from Well Bing Bank give me your account my number is 112-22314-12314. Thank you.
return
hi my name is woo young from Well Bing Bank
<Text style={styles.highlighted}>
give me your account
<Text>
my number is 112-22314-12314. Thank you.
but it returns
hi my name is woo young from Well Bing Bank
[object Object]
my number is 112-22314-12314. Thank you.
import { Text, View, StyleSheet } from "react-native";
const NO_WIDTH_SPACE = '​';
const highlight = string =>
string.split(' ').map((word, i) => (
<Text key={i}>
<Text style={styles.highlighted}>{word} </Text>
{NO_WIDTH_SPACE}
</Text>
));
export default class TextBox extends Component {
setHighlight = (string, array) => {
for (let i = 0; i < (array.length)/2; i++) {
let seg = string.slice(array[2*i], array[2*i+1])
let firstIdx = string.slice(0, array[2*i-1])
let lastIdx = string.slice(array[2*i+2], array.length)
let segText = highlight(seg)
let result = firstIdx + segText + lastIdx
return result
}
}
render = () => {
return(
<View style={styles.textBox}>
{this.props.partialResults.map((result, index) => {
const hightlightText = this.setHighlight(result,[3,4])
return (
<Text style={styles.resultText} key={`partial-result-${index}`}>
{ hightlightText }
</Text>
)
})}
</View>
)
}
}
const styles = StyleSheet.create({
textBox: {
marginTop: 10,
width: 330,
height: 400,
backgroundColor: "white",
borderWidth: 2,
padding: 10,
borderColor: "dodgerblue"
},
resultText: {
fontSize: 18
},
highlighted: {
padding:2,
backgroundColor: 'red',
},
})
Not sure if your logic is sound but you can return the result as JSON Stringify, that may indicate what that object contains and give you a better idea what to return.
setHighlight = (string, array) => {
for (let i = 0; i < (array.length)/2; i++) {
let seg = string.slice(array[2*i], array[2*i+1])
let firstIdx = string.slice(0, array[2*i-1])
let lastIdx = string.slice(array[2*i+2], array.length)
let segText = highlight(seg)
let result = firstIdx + segText + lastIdx
return JSON.stringify(result)
}
}

Realm - Second insert with throws error: Error: 'xxx' must be of type 'string', got 'null' (null)

Upgrading to Realm 10.8.0 in a React-Native project when I perform an insert/write to a schema the first one is successful, but the second insert throws the following error: Error: Workflows.InternalName must be of type 'string', got 'null' (null)
I cannot determine why the first insert was successful and the second errors? This is a sandbox text where the value are hard-coded and definitely not null.
package.json
"react": "17.0.2",
"react-native": "0.65.1",
"realm": "^10.8.0"
App.js
import React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import Realm from 'realm';
class Workflows {
ExternalName;
InternalName;
static schema = {
name: 'Workflows',
primaryKey: 'InternalName',
properties: {
ExternalName: 'string',
InternalName: 'string',
}
};
}
export default class App extends React.Component {
constructor() {
super();
this.realm = new Realm({
schema: [Workflows],
schemaVersion: 1,
});
console.log("Db: Realm created");
}
record;
ctr = 0;
saveData = () => {
let workflows = new Workflows();
workflows.InternalName = "Internal Name" + this.ctr;
workflows.ExternalName = "External Name" + this.ctr;
this.ctr++
try {
this.realm.write(() => {
this.record = this.realm.create(Workflows.schema.name, workflows);
})
this.record = this.realm.objects(Workflows.schema.name);
console.log(`The lists of workflows are: ${this.record.map((workflow) => workflow.InternalName)}`);
}
catch (error) {
console.log(error)
}
}
render () {
return (
<View style={styles.appContainer} >
<Button title="Save Realm data" onPress={this.saveData} />
</View>
);
}
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}
})
The following test have different results.
Adding logging after the new Workflows() and subsequent assignment shows the second workflows object does not get created properly (but ONLY if the realm.create is in the code)
let workflows = new Workflows();
workflows.InternalName = "Internal Name" + this.ctr;
workflows.ExternalName = "External Name" + this.ctr;
console.log("workflows InternalName: " + workflows.InternalName)
console.log("workflows ExternalName: " + workflows.ExternalName)
console.log("workflows object: ", workflows)
*CONSOLE OUTPUT*
LOG Running "Test" with {"rootTag":171}
LOG Db: Realm created
LOG workflows InternalName: Internal Name0
LOG workflows ExternalName: External Name0
LOG workflows object: {"ExternalName": "External Name0", "InternalName": "Internal Name0"}
LOG The lists of workflows are: Internal Name0
LOG workflows InternalName: null
LOG workflows ExternalName: null
LOG workflows object: {}
LOG [Error: Workflows.InternalName must be of type 'string', got 'null' (null)]
Using the following realm.create works everytime (vs passing an object variable):
this.realm.create(Workflows.schema.name, { InternalName: "Internal Name" + this.ctr, ExternalName: "External Name" + this.ctr });
Commenting out the this.realm.create(Workflows.schema.name, workflows); caused the object to be created and assigned correctly, but obviously now to write/insert is performed.

How do we use Flutterwave payment in react native?

I want to integrate flutterwave in my react native application. I downloaded their npm package called flutterwave-react-native and followed their tutorial but still can't do it. I'm using their sample snippet on Github and I'm getting an error that says:
this.usePaymentLink is not a function
I searched everywhere but couldn't find where this.usePaymentLink was defined. You can check out my snippet and tell me what I missed and how this.usePaymentLink can look like.
import React from 'react';
import {View, TouchableOpacity} from 'react-native';
import {FlutterwaveInit} from 'flutterwave-react-native';
class MyCart extends React.Component {
abortController = null;
componentWillUnmout() {
if (this.abortController) {
this.abortController.abort();
}
}
handlePaymentInitialization = () => {
this.setState({
isPending: true,
}, () => {
// set abort controller
this.abortController = new AbortController;
try {
// initialize payment
const paymentLink = await FlutterwaveInit(
{
tx_ref: generateTransactionRef(),
authorization: '[merchant public key]',
amount: 100,
currency: 'USD',
customer: {
email: 'customer-email#example.com',
},
payment_options: 'card',
},
this.abortController
);
// use payment link
return this.usePaymentLink(paymentLink);
} catch (error) {
// do nothing if our payment initialization was aborted
if (error.code === 'ABORTERROR') {
return;
}
// handle other errors
this.displayErrorMessage(error.message);
}
});
}
render() {
const {isPending} = this.state;
return (
<View>
...
<TouchableOpacity
style={[
styles.paymentbutton,
isPending ? styles.paymentButtonBusy : {}
]}
disabled={isPending}
onPress={this.handlePaymentInitialization}
>
Pay $100
</TouchableOpacity>
</View>
)
}
}
so i have been trying to apply it on expo but finally got a breakthrough.
// so i made some little corrections before i could get it running
// this is the code directly from their npm or github
import {PayWithFlutterwave} from 'flutterwave-react-native';
<PayWithFlutterwave
...
onRedirect={handleOnRedirect}
options={{
tx_ref: transactionReference,
authorization: '[merchant public key]',
customer: {
email: 'customer-email#example.com'
},
amount: 2000,
currency: 'NGN',
payment_options: 'card'
}}
/>
// my correction
first of all handleOnRedirect must be a defined function
secondly i removed the three dots (...) before the handleOnRedirect function
then created a function to generate a randomized refrenced no
then i pasted my public flutterwave account key for "merchant public key"
i also pasted my flutterwave account email in place of this 'customer-email#example.com'
import {PayWithFlutterwave} from 'flutterwave-react-native';
const handleOnRedirect = () => {
console.log('sadi')
}
const generateRef = (length) => {
var a = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".split("");
var b = [];
for (var i=0; i<length; i++) {
var j = (Math.random() * (a.length-1)).toFixed(0);
b[i] = a[j];
}
return b.join("");
}
<PayWithFlutterwave
onRedirect={handleOnRedirect}
options={{
tx_ref: generateRef(11),
authorization: 'MY_PUBLIC_KEY',
customer: {
email: 'user#gmail.com'
},
amount: 2000,
currency: 'NGN',
payment_options: 'card'
}}
/>
``

React-Native HighCharts Boost Module Usage

I have a data set of four series each with 250000 points so approximately one million in total. I am creating an app with React Native. I tried using minimal HighCharts Wrapper but couldn't make the boost module work. Here is my App.js:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import HighchartsReactNative from '#highcharts/highcharts-react-native'
const modules = ['boost']
export default class App extends React.Component {
constructor(props) {
super(props);
let chartSeries = []
for(let i = 0; i < 4 ; i ++){
let series = {data:[]};
for(let i= 0; i < 2500; i++){
let point = [i+1,Math.floor(220-Math.random(0,120)*10)]
series.data.push(point)
}
chartSeries.push(series);
}
this.state = {
chartOptions: {
boost:{
enabled:true,
seriesThreshold:2000
},
chart:{
zoomType:'xy'
},
series: chartSeries
}
};
}
render() {
return (
<View style={styles.container}>
<HighchartsReactNative
styles={styles.container}
options={this.state.chartOptions}
modules={modules}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
justifyContent: 'center',
flex: 1
}
});
It is really simple and it renders eventually but it takes too much time. Boost module doesn't seem to work correctly. Is my usage wrong here?
It does not work correctly, because you set the boost.seriesThreshold equal to 2000 what means that the chart does not switch into the boost module until it has got at least 2000 series.
I think you had intention to set the plotOptions.series.boostThreshold to configure the boost so that it will turn on when certain series has 2000 points, didn't you?
In sum, you just need to set the mentioned seriesThreshold to 1, remove the boost: true (as it is redundant, and true by default), and then set the plotOptions.series.boostThreshold parameter equal to 2000.
API References:
https://api.highcharts.com/highcharts/boost.seriesThreshold
https://api.highcharts.com/highcharts/plotOptions.series.boostThreshold

Trying to load obj & mtl file with Three.js in React Native

Main objective : Load animated models exported from Maya into React Native app
Exported files : obj, mtl & png file
I have setup https://github.com/react-community/react-native-webgl in my React Native project and it is working properly.
Now, when I am trying to load the MTL file using the MTLLoader, I am getting following error:
Can't find variable: document
Apparently, the MTLLoader is calling TextureLoader which internally calls some load function which has 'document' reference. So what could be the solution to this ?
Here are the two files that I am using:
three.js
const THREE = require("three");
global.THREE = THREE;
if (!window.addEventListener)
window.addEventListener = () => { };
// require("three/examples/js/renderers/Projector");
require("three/examples/js/loaders/MTLLoader");
require("three/examples/js/loaders/OBJLoader");
export default THREE;
ThreeView.js
import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import { WebGLView } from "react-native-webgl";
import THREE from "./three";
import { image } from "src/res/image";
export default class ThreeView extends Component {
requestId: *;
componentWillUnmount() {
cancelAnimationFrame(this.requestId);
}
onContextCreate = (gl: WebGLRenderingContext) => {
const rngl = gl.getExtension("RN");
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const renderer = new THREE.WebGLRenderer({
canvas: {
width,
height,
style: {},
addEventListener: () => { },
removeEventListener: () => { },
clientHeight: height
},
context: gl
});
renderer.setSize(width, height);
renderer.setClearColor(0xffffff, 1);
let camera, scene;
let cube;
function init() {
camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
camera.position.y = 150;
camera.position.z = 500;
scene = new THREE.Scene();
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load('female-croupier-2013-03-26.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load('female-croupier-2013-03-26.obj', function (object) {
scene.add(object);
}, onLoading, onErrorLoading);
}, onLoading, onErrorLoading);
}
const onLoading = (xhr) => {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
};
const onErrorLoading = (error) => {
console.log('An error happened', error);
};
const animate = () => {
this.requestId = requestAnimationFrame(animate);
renderer.render(scene, camera);
// cube.rotation.y += 0.05;
gl.flush();
rngl.endFrame();
};
init();
animate();
};
render() {
return (
<View style={styles.container}>
<WebGLView
style={styles.webglView}
onContextCreate={this.onContextCreate}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
},
webglView: {
width: 300,
height: 300
}
});
This error is as others have said caused by threejs trying to use features from a browser which react-native does not have.
I've gotten so far as to be able to load the textures (which is the stage you're getting the error from) by monkey patching the texture loader to use the loader in react-native-webgl. Add this in your init function (right near the top preferably).
//make sure you have defined renderer and rngl
/*
const renderer = new THREE.WebGLRenderer(...)
const rngl = gl.getExtension("RN");
*/
const loadTexture = async function(url, onLoad, onProgress, onError) {
let textureObject = new THREE.Texture();
console.log("loading",url,'with fancy texture loader');
let properties = renderer.properties.get(textureObject);
var texture = await rngl.loadTexture({yflip: false, image: url});
/*
rngl.loadTexture({ image: url })
.then(({ texture }) => {
*/
console.log("Texture [" + url + "] Loaded!")
texture.needsUpdate = true;
properties.__webglTexture = texture;
properties.__webglInit = true;
console.log(texture);
if (onLoad !== undefined) {
//console.warn('loaded tex', texture);
onLoad(textureObject);
}
//});
return textureObject;
}
THREE.TextureLoader.prototype.load = loadTexture;
This solves the problem of loading textures and I can see them load in Charles but they still don't render on a model so I'm stuck past that point. Technically a correct answer but you'll be stuck as soon as you've implemented it. I'm hoping you can comment back and tell me you've gotten further.
I had a similar setup and encountered same issue. My option was to switch to JSONLoader which doesn’t need document to render in react-native. So, I just loaded my model in Blender with a three-js addon, then exported it as json. Just check out this process of adding a three-js adon to Blender
https://www.youtube.com/watch?v=mqjwgTAGQRY
All the best
this might get you closer:
The GLTF format supports embedding texture images (as base64). If your asset pipeline allows it, you could convert to GLTF and then load into three/react-native.
I had to provide some "window" polyfills for "decodeUriComponent" and "atob" because GLTFLoader uses FileLoader to parse the base64:
I've successfully loaded embedded buffers, but you'll need more polyfills to load textures. TextureLoader uses ImageLoader, which uses document.createElementNS
You are using the MTLLoader which uses TextureLoader, and the TextureLoader uses the ImageLoader.
The imageloader uses the document.createElementNS() function.
what i did to solve this was to directly call the THREEjs TextureLoader:
let texture = new THREE.Texture(
url //URL = a base 64 JPEG string in this case
);
(for the use of Texture check the Texture documentation)
Then i used the Image class from React native (instead of the THREEjs Image, which requires the DOM to be constructed) to give that to the Texture as a property:
import { Image } from 'react-native';
var img = new Image(128, 128);
img.src = url;
texture.normal = img;
And then finally map the texture over the target material:
const mat = new THREE.MeshPhongMaterial();
mat.map = texture;
In the react native documentation it will explain how the react native Image element can be used, it supports base64 encoded JPEG.
Maybe there's a way for you to single out the part where it calls for the TextureLoader and replace that part with this answer. Let me know how it works out.
side note, i havent tried to display this yet in my webGLView, but in the logs it looked like normal threejs objects, it's worth the try
Use TextureLoader from expo-three
import { TextureLoader } from "expo-three";
export function loadTexture(resource) {
if (textureCache[resource]) {
return textureCache[resource].clone();
}
const texture = new TextureLoader().load(resource);
texture.magFilter = NearestFilter;
texture.minFilter = NearestFilter;
textureCache[resource] = texture;
return texture;
}
Source: https://github.com/EvanBacon/Expo-Crossy-Road/blob/master/src/Node/Generic.js