Is it possible to use ExcelJS in React Native? - android

I am trying to create an excel sheet from React Native mobile application. I have tried using xlsx to generate excel. But it is not supporting the styling excel sheet. Is it possible to use ExcelJS in React Native?

Yes, it is possible with the help of Google spreadsheet and tabletop library. After searching a lot I found this way:
Install the tabletop library.
Upload your .xlsx file in google drive.
Now in select File -> Share-> Publish to web.
Once you click a new window open and your file published.
Now close this window and copy the key of your file.
The Key will be found in the URL of the browser.
Forex:
https://docs.google.com/spreadsheets/d/108O2Memo7yJ3-7fzngFSoulVj-J32vwcyLDu5Lslhik/edit#gid=1371815480
In the above URL: this will be the key "108O2Memo7yJ3-7fzngFSoulVj-J32vwcyLDu5Lslhik"
6. Now in your code pass this key to the init method of the tabletop.
7. Get data from the file and show it in the list.
Library install :
npm install tabletop
In component did mount I initialize tabletop.
componentDidMount() {
// Facts
Tabletop.init({
key: 'keyCopyFromUrl',
callback: googleData => {
this.setState({
arrayFacts: googleData
})
},
simpleSheet: true
});
Now for showing this data
<FlatList
data={this.state.arrayFacts}
renderItem={this.renderItemFacts}
keyExtractor={(key, index) => index.toString()}
/>

First of all install the required modules (assuming usage of Expo):
npm install --save exceljs
expo install expo-file-system
expo install expo-sharing
Code example - Imports:
// Required to save to cache
import * as FileSystem from 'expo-file-system';
// ExcelJS
import ExcelJS from 'exceljs';
// From #types/node/buffer
import { Buffer as NodeBuffer } from 'buffer';
// Share excel via share dialog
import * as Sharing from 'expo-sharing';
Code example - Function to generate Excel and return uri
// This returns a local uri that can be shared
const generateShareableExcel = async (): Promise<string> => {
const now = new Date();
const fileName = 'YourFilename.xlsx';
const fileUri = FileSystem.cacheDirectory + fileName;
return new Promise<string>((resolve, reject) => {
const workbook = new ExcelJS.Workbook();
workbook.creator = 'Me';
workbook.created = now;
workbook.modified = now;
// Add a sheet to work on
const worksheet = workbook.addWorksheet('My Sheet', {});
// Just some columns as used on ExcelJS Readme
worksheet.columns = [
{ header: 'Id', key: 'id', width: 10 },
{ header: 'Name', key: 'name', width: 32 },
{ header: 'D.O.B.', key: 'dob', width: 10, }
];
// Add some test data
worksheet.addRow({ id: 1, name: 'John Doe', dob: new Date(1970, 1, 1) });
worksheet.addRow({ id: 2, name: 'Jane Doe', dob: new Date(1969, 2, 3) });
// Test styling - what OP wanted
worksheet.eachRow((row, rowNumber) => {
row.getCell(1).font = {
name: 'Arial Black',
color: { argb: 'FF00FF00' },
family: 2,
size: 14,
bold: true
};
});
// Write to file
workbook.xlsx.writeBuffer().then((buffer: ExcelJS.Buffer) => {
// Do this to use base64 encoding
const nodeBuffer = NodeBuffer.from(buffer);
const bufferStr = nodeBuffer.toString('base64');
FileSystem.writeAsStringAsync(fileUri, bufferStr, {
encoding: FileSystem.EncodingType.Base64
}).then(() => {
resolve(fileUri);
});
});
});
}
Code Example - Function to share generated Excel. Call this, for example, on the press of a button:
const shareExcel = async () => {
const shareableExcelUri: string = await generateShareableExcel();
Sharing.shareAsync(shareableExcelUri, {
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // Android
dialogTitle: 'Your dialog title here', // Android and Web
UTI: 'com.microsoft.excel.xlsx' // iOS
}).catch(error => {
console.error('Error', error);
}).then(() => {
console.log('Return from sharing dialog');
});
}

Related

Angular Cordova Project (Android) doesn't open Razorpay external page (Enter OTP/ Enter Login credential page)

Razor pay works properly when opening in a browser but when i convert the project into an android app, external page which is supposed to be opened to submit OTP doesn't show up. I understand i need an inAppBrowser to open the link but it is not controlled by me. Razorpay will open the link automatically when the user chooses card payment or internet banking. How to solve this? please help!
window-ref.service.ts
import {Injectable} from '#angular/core';
// declare var cordova:any;
export interface ICustomWindow extends Window {
__custom_global_stuff: string;
}
function getWindow(): any {
return window;
}
#Injectable({
providedIn: 'root'
})
export class WindowRefService {
get nativeWindow(): ICustomWindow {
return getWindow();
}
}
app.component.ts
constructor(private winRef: WindowRefService) {
this._window = this.winRef.nativeWindow;
}
private _window: ICustomWindow;
public rzp: any;
public options: any = {
key: 'KEY', // add razorpay key here
name: 'Bunto Couriers Pvt. Ltd.',
description: 'Delivery Fee',
amount: this.price*100, // razorpay takes amount in paisa
prefill: {
name: '',
email: '', // add your email id
},
image: 'https://isell-bunto.000webhostapp.com/assets/img/loader.png',
notes: {},
theme: {
color: '#3880FG'
},
handler: this.paymentHandler.bind(this),
modal: {
ondismiss: (() => {
this.zone.run(() => {
// add current page routing if payment fails
this.toastr.error("Payment Error!");
})
})
}
};
initPay(): void {
this.rzp = new this.winRef.nativeWindow['Razorpay'](this.options);
this.rzp.open();
}
paymentHandler(res: any) {
this.zone.run(() => {
// add API call here
console.log(res);
});
}
this is a pop up window which opens fine in both browser and in android app
this external page doesn't show up in the app
in the app, this is as far as it goes. external url doesn't open after this stage
I'm not sure how the Javascript library will work while we don't have the control over showing it using the InAppBrowser.
For this, I really suggest to use the native Cordova Razorpay plugin for more compatibility and stability: https://github.com/razorpay/razorpay-cordova
You can check their sample apps (Cordova and Ionic)
Add command: cordova plugin add https://github.com/razorpay/razorpay-cordova.git --save
var options = {
description: 'Credits towards consultation',
image: 'https://i.imgur.com/3g7nmJC.png',
currency: 'INR',
key: 'rzp_test_1DP5mmOlF5G5ag',
order_id: 'order_7HtFNLS98dSj8x',
amount: '5000',
name: 'foo',
theme: {
color: '#F37254'
}
}
var successCallback = function(success) {
alert('payment_id: ' + success.razorpay_payment_id)
var orderId = success.razorpay_order_id
var signature = success.razorpay_signature
}
var cancelCallback = function(error) {
alert(error.description + ' (Error '+error.code+')')
}
RazorpayCheckout.on('payment.success', successCallback)
RazorpayCheckout.on('payment.cancel', cancelCallback)
RazorpayCheckout.open(options)

How to set headers downloading a file using React Native and react-native-fs?

In order to download an asset an Authorization header needs to be set when using react-native-fs with React Native.
Following the documentation the header is set as such:
const options = {
headers: {
Authorization: `Bearer ${accessToken}`,
},
fromUrl: url,
toFile: path,
};
// const permission = await insurePermissions();
const task = RNFS.downloadFile(options);
It works perfect in iOS however Android, using an emulator running either Android 6, 8, 9, or 10, the header isn't being sent so the server is instead returning a different asset with error because the user isn't being authenticated.
How is the Authentication header set in Android with react-native-fs?
This problem is consistent with other React Native file system libraries. I instead used Axios to download the file and saved with with RNFS.
const fetchFile = async (accessToken, id) => {
const dir = await getDirectory();
await flushFileProofs();
const baseUrl = 'https://example.com';
const url = `${baseUrl}/images/${id}/download`;
const fileName = `${id}-file.png`;
const path = `${dir}/${fileName}`;
const data = await axios.request({
method: 'GET',
url,
headers: {
Accept: '*/*',
Authorization: `Bearer ${accessToken}`,
},
responseType: 'arraybuffer',
})
.then(response => Buffer.from(response.data, 'binary').toString('base64'));
await RNFS.writeFile(path, data, 'base64');
FileViewer.open(path);
};

Uploading image to firebase in react native: undefined is not a function

As the title says, I'm trying to upload Image to firebase in react native. I'm using react-native-image-picker and firebase modules for that. My code goes as: (Only including the "main" parts for clarity)
import ImagePicker from 'react-native-image-picker';
...
//called on pressing a button
onChooseImagePress = async () => {
let result = await ImagePicker.open({ //error occurs here
takePhoto: true,
useLastPhoto: true,
chooseFromLibrary: true
});
if (!result.cancelled) {
this.uploadImage(result.uri, "test-image")
.then(() => {
Alert.alert("Success");
})
.catch((error) => {
Alert.alert(error);
});
}
}
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase.storage().ref('images').child("userName/" + imageName);
return ref.put(blob);
}
....
Issue:
I am getting this error: undefined is not a function. Here's a screenshot of the same:
I'm not sure what it even means, since ImagePicker has an open function. Please note that I have provided the desired permissions. So it is not an issue due to that. Please help me resolve this. Thanks...
Are you using React-native ImagePicker? There is no open in the API document.
API Reference of react-native-image-picker
This is the default example of getting the value of the selected image you want.
import ImagePicker from 'react-native-image-picker';
// More info on all the options is below in the API Reference... just some common use cases shown here
const options = {
title: 'Select Avatar',
customButtons: [{ name: 'fb', title: 'Choose Photo from Facebook' }],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
/**
* The first arg is the options object for customization (it can also be null or omitted for default options),
* The second arg is the callback which sends object: response (more info in the API Reference)
*/
ImagePicker.launchImageLibrary(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
const source = { uri: response.uri };
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source,
});
}
});

How to fix Expo Android app which can't make more than 2 http request in a row

I'm having an ugly issue that only affect my expo app on Android.
Im trying to upload a base64 image taken with expo ImagePicker to Firebase Storage passing the image value with a http-request made with axios to a Firebase Cloud Function which returns the url of the saved image. This url goes in Firestore, but this is out of reach of my question I think.
My current implementation works flawless in IOS (I can get as many urls as I want, they upload pretty quick actually) but, in Android I only can upload 2 images in a row; when I try for the third time, my app get frozen when reach axios/fetch* statement and gives no clue of whats happened. Console is just as it was before trying the third time and the apps or simulators freeze.
Here you can see this behaviour in a 2 min video:
https://youtu.be/w66iXnKDmdo
When I begun working in this bug I was using fetch instead of axios. At that time the issue was that I was able to upload only one image. It were necessary to close and open the app again to upload one more. Now with axios Im able to upload 2 insted of one, but the problem persist.
This is how I implemented the code:
const imageBase64 = 'QWEpqw0293k01...'
This is how I upload the image to Firebase Cloud Storage:
export const savePhoto = (imageBase64) => {
const db = firebase.firestore();
const docRef = db.collection('Comidas').doc();
return () => {
uploadImageToFirestore(imageBase64)
.then(imageUrl => {
console.log('image-url: ', imageUrl);
docRef.set({ imagen: { uri: imageUrl }, });
})
.catch(err => console.log('error: ', err));
};
};
I made a function helper that allow me to make the http request reusable:
import axios from 'axios';
export const uploadImageToFirestore = (imageBase64) => {
//<--- here is where it get frozen the third time
//<--- console.log() calls three times but not axios
return axios({
method: 'post',
url: 'https://us-central1-menuapp-9feb4.cloudfunctions.net/almacenamientoImagen',
data: {
image: base64
},
})
.then(res => res.data.imageUrl)
.catch(err => console.log('error while uploading base64: ', err));
};
This invoques the following Firebase Cloud Function:
exports = module.exports = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const body = req.body;
console.log('image: ', body.image);
fs.writeFileSync("/tmp/uploaded-image.jpg", body.image, "base64", err => {
console.log(err);
return res.status(500).json({ error: err });
});
const bucket = gcs.bucket("myapp.appspot.com");
const uuid = UUID();
bucket.upload(
"/tmp/uploaded-image.jpg",
{
uploadType: "media",
destination: "/comidas/" + uuid + ".jpg",
metadata: {
metadata: {
contentType: "image/jpeg",
firebaseStorageDownloadTokens: uuid
}
}
},
(err, file) => {
if (!err) {
console.log('url: ', {
imageUrl:
"https://firebasestorage.googleapis.com/v0/b/" +
bucket.name +
"/o/" +
encodeURIComponent(file.name) +
"?alt=media&token=" +
uuid
});
res.status(201).json({
imageUrl:
"https://firebasestorage.googleapis.com/v0/b/" +
bucket.name +
"/o/" +
encodeURIComponent(file.name) +
"?alt=media&token=" +
uuid
});
} else {
console.log(err);
res.status(500).json({ error: err });
}
}
);
});
});
I know that axios it’s not being called because there is no log neither register of the Firebase Cloud Function execution.
I expect this code to upload as many images as user consider he/she needs, not just 2 per app session as it does at this moment
How can I solve this?

Sending Images from android device to api

I'm trying to send an image from an android device to a laravel api using react native, but it doesn't read the url of the image, i.e Unable to init from given url (file:///storage/emulated/0/DCIM/Camera/IMG_20181013_133327.jpg, it always brings that error, it doesn't read the url of the image, file:///storage/emulated/0/DCIM/Camera/IMG_20181013_133327.jpg please how can I send the image to my laravel api successfully or I can I download based on the location of the image on the android device
REACT NATIVE AXIOS
//THE IMAGES ARE FIRST SELECTED AND THE IMAGES ARRAY IS SET WITH THE URI OF THE IMAGES
imageUpload(){
ImagePicker.openPicker({
multiple: true,
cropping: true,
mediaType: 'photo'
}) .then(images => {
console.log(images);
const imagesArray = [];
if(images){
images.map(i => {
imagesArray.push({uri: i.path, type: i.mime, name: i.path});
} );
}
this.setState({
images_array: imagesArray
});
console.log(imagesArray);
console.log(this.state.images_array);
}).catch(e => console.log(e));
}
//THE IMAGES ALONG WITH OTHER DETAILS ARE SENT TO THE LARAVEL API
seller(){
this.setState({loader: true});
var data = {
name: this.state.name,
// user_id: this.state.user_id,
user_id: 18,
description: this.state.description,
amount: this.state.amountT,
qty: this.state.qty,
cat_id: this.state.cat_id,
photos: this.state.images_array
};
/* var config = {
headers: {'Authorization': "Bearer " + this.state.token}
};*/
axios.post(
'http://10.0.2.2:8000/api/sell',
data,
// config
).then((response) => {
this.setState({loader: false});
console.log(response);
Alert.alert(
'Success',
'Product posted Successfully',
[
{text: 'OK', onPress: this.props.navigation.navigate('Land', {})},
], );
}).catch((error) => {
this.setState({loader: false});
Alert.alert(
'Error',
'Internal Server Error, please try again later',
[
{text: 'OK'},
], );
console.log(error);
});
};
LARAVEL BACKEND, i.e Intervention Image api is used
public function imagesUpload($goodsId, $photos){
$images = $photos;
// $count = $images->count;
foreach($images as $image){
$uri = $image['uri'];
$filename = basename($uri);
Image::make($uri)->save(public_path('img/' . $filename));
$saver = new Images;
$saver->product_id = $goodsId;
$saver->location_url = 'img/'.$filename;
$saver->save();
}
return true;
}
using double // does not work on real devices , it may work on emulator but not on real devices .
try using this
uri:'file:///storage/emulated/0/DCIM/IMG_20161201_125218.jpg'
make sure to use /// three forward slashes.

Categories

Resources