Get a .js file dynamically rather than importing them - reactjs

I have a small app that gives support to 30+ languages. I used react-intl to achieve my task. In react-intl I got to import every locale where every local file is around 7-8kbs, whereas I want to reduce these unnecessary imports and want to import only one file
app.js
import {IntlProvider, addLocaleData} from 'react-intl'
import ca from 'react-intl/locale-data/ca'
import cs from 'react-intl/locale-data/cs'
...
import hu from 'react-intl/locale-data/hu'
import id from 'react-intl/locale-data/id'
import enMessages from '../assets/translations/en.json'
Translations.getLocale('fr').then(function(localeData){
addLocaleData(localeData);
console.log("localeData");
console.log(localeData); //Code instead of array of objects
}, function(status) {
alert('Something went wrong.');
});
Now the ca, cs,hu etc. contain array of objects returned from the respective js files.
I tried using XHR but instead of returning the array of objects, I get the code that is written in the .js file. Is there any way I can dynamically import the js file or if I can get the array of objects from the code returned by XMLHttpRequest.
Translations.js
getLocale: function(lang, successHandler, errorHandler){
var url = 'http://localhost/img/' + lang + '.js';
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
//xhr.responseType = 'application/javascript';
xhr.onload = function() {
var status = xhr.status;
if (status == 200) {
resolve(xhr.response);
} else {
reject(status);
}
};
xhr.send();
});
//return message;
}

If I understand you correctly, you retrieve the javascript code, which you want retrieve the output from.
One solution is to use eval, although this is generally not considered very secure. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
You can also make the code an automatically executing function that puts the output on a global variable, and access it from there. Append the content of the js file as a script in the head tag, and make the file contain something like.
myGlobalVar = (function() {
return {
key: val
};
})();
I do not know the format of your translate.js files, but you could also consider putting the translations in a json file, if it's a fixed output for each language. Which I think would be the safest solution.

I managed to load the locale files dynamically like this :
Note that my locale string formatting might not be ideal, and ignore the polyfill if you don't plan on supporting old browsers.
import {addLocaleData} from 'react-intl';
const locale = // get this from browser language
// ensure that the polyfill is loaded before calling this
const isUsingIntlPolyfill = Object.prototype.hasOwnProperty.call(window, 'IntlPolyfill');
// eg: turns 'fr-fr' into 'fr-FR' because intl polyfill locale files are formatted like this
const formatLocale = str => `${str.split('-')[0]}${str.split('-')[1] ? `-${str.split('-')[1].toUpperCase()}` : ''}`;
if (isUsingIntlPolyfill) {
const polyfill = document.createElement('script');
// path of the file might differ for your setup
polyfill.setAttribute('src', `/i18n/polyfill/${formatLocale(locale)}.js`);
document.getElementsByTagName('head')[0].appendChild(polyfill);
}
const script = document.createElement('script');
// path of the file might differ for your setup
script.setAttribute('src', `/i18n/${locale.split('-')[0]}.js`);
script.onload = () => {
addLocaleData([...window.ReactIntlLocaleData[locale.substring(0, 2)]]);
// your locale is loaded, do some more stuff from here ...
};
document.getElementsByTagName('head')[0].appendChild(script);

Related

How to convert HEIC to JPG in React?

HEIC is Apple's own format to store high resolution images made with iOS cameras. But I would store on backend JPG, because HEIC is not displayed in most browsers, not even in Safari.
SOLUTION 1
I tried this for the conversion:
const buffer = Buffer.from(await file.arrayBuffer())
const d = heicConvert({ buffer, format: 'JPEG' })
const imgBase64 = btoa(
d.reduce((data, byte) => `${data}${String.fromCharCode(byte)}`, '')
)
but because I use Next.js it is not compatible with it.
Failed to compile.
./node_modules/libheif-js/libheif/libheif.js
Module not found: Can't resolve 'fs' in '/Users/janoskukoda/Workspace/tikex/portal/team/node_modules/libheif-js/libheif'
SOLUTION 2
I tried this also:
export default uploadImage
const buffer = await file.arrayBuffer()
const image = sharp(buffer)
const metadata = await image.metadata()
if (metadata.format === 'heic') {
// Convert the image to JPG
const jpgBuffer = await image.jpeg().toBuffer()
// Encode the JPG image as a base64 string
const imgBase64 = btoa(
jpgBuffer.reduce((data, byte) => `${data}${String.fromCharCode(byte)}`, '')
)
}
But I can not compile, seems sharp is not recommended to use in client side.
Do you have any other way to do it?
Anyway idea comes here: https://itnext.io/tackling-iphone-or-ipad-images-support-in-browser-8e3e64e9aaa1
If you show me a solution uses serverless api, it is also ok. It is important file comes from html input element.
SOLUTION 3
import loadImage from 'blueimp-load-image'
convertedImage = await new Promise((resolve, reject) => {
loadImage(
propsAndFile.file,
resolve,
{ orientation: true, canvas: true },
reject
)
})
Before getting to the answer: You can never trust data uploaded by a client — you must always validate/convert using a process that is not accessible by a user (a backend process) to ensure data validity: even if there are mechanisms in place to validate received network requests as coming from an authenticated user on your site, any user can still use developer tools to execute arbitrary JavaScript and send whatever kinds of network requests with whatever payloads that they want to.
Regarding iOS devices and getting JPEG instead of HEIF from a file input: You don't need to do this yourself — iOS can do it for you.
<input type="file"> elements support an accept attribute that can be used to restrict the kinds of file media types that can be uploaded: read more at the Limiting accepted file types section of the MDN documentation article for <input type="file">.
Below is an example which shows how to use that attribute. When an iOS user selects the input, they can choose to take a photo using their camera or select one from the photo library. iOS will perform the necessary file conversion to JPEG automatically in both of these cases (for example, even when a selected image from the photo library is in HEIF format). The example demonstrates this when you try it with an HEIF-encoded image on an iOS device:
const input = document.getElementById('image-upload');
const output = document.getElementById('output');
const updateDisplayedFileInfo = () => {
const file = input.files?.[0];
if (!file) {
output.textContent = 'No file selected';
return;
}
const dateModified = new Date(file.lastModified).toISOString();
const {name, size, type} = file;
const data = {
dateModified,
name,
size,
type,
};
const json = JSON.stringify(data, null, 2);
output.textContent = json;
};
input.addEventListener('change', updateDisplayedFileInfo);
updateDisplayedFileInfo();
pre { background-color: hsla(0, 0%, 50%, 0.1); padding: 0.5rem; } code { font-family: monospace; }
<input id="image-upload" type="file" accept="image/jpeg" />
<pre><code id="output"></code></pre>
Install the heic2jpeg library by running the following command in your terminal:
npm install heic2jpeg
Import the heic2jpeg library in your React component:
import heic2jpeg from 'heic2jpeg';
Convert the HEIC file to JPG by calling the convert method of the heic2jpeg library and passing in the HEIC file as an argument:
const jpegData = await heic2jpeg.convert(heicFile);
You can then use the jpegData to create a new JPG file or display it in an img element:
const jpegFile = new File([jpegData], 'image.jpg', { type: 'image/jpeg' });
// or
const imageElement = document.createElement('img');
imageElement.src = URL.createObjectURL(jpegFile);
document.body.appendChild(imageElement);
Note that the heic2jpeg library requires the canvas and process modules to be available in the global context, so you may need to include these modules in your application as well.

how to write to a json file in react

I have a json file that I will like to add data into it.
The json file contain:
[{"name":"Flossi","image":"https://robohash.org/istevelitut.png?size=50x50&set=set1","price":49,"info":"Cras dui."},
{"name":"Rab","image":"https://robohash.org/voluptatumsuntiste.png?size=50x50&set=set1","price":64,"info":"Phasellus insi."}]
I tried to use fs but I understand it cannot be done with react.
Tried to install this package to npm:
https://www.npmjs.com/package/write-json-file
But get an error on the compiling..
Help?
NodeJS using the built in module fs to read and write and React-Native doesnt have one. I would recommend trying to use react-native-fs, although there are some Android limitations. After following the installation setup, you have to ask yourself where you want to save the JSON. If its in the app internal storage, there's no problem, but if you want to save to external storage there's additional setup that may ultimately just not work (I've experienced this).
import RNFS from 'react-native-fs';
import { Alert, Platform, PermissionsAndroid,} from 'react-native';
const saveFile= (saveLocation,data,encoding='utf8')=> {
let dataString = JSON.stringify(data,null,2)
RNFS.writeFile(saveLocation,dataString,encoding)
.then(()=>Alert.alert('Saved the data!')
.catch((err)=>Alert.alert("Failed to save data!",err.message)
}
// you may or may not need this
const getFilePermissionsAndroid = async ()=>{
const permissions = [
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
]
// map permissions to whether or not they are granted
let checkPermissions = await Promise.all(permissions.map(permission=>
PermissionsAndroid.check(permission)
))
// filter for ungranted permissions
let permissionsToRequest = permissions.filter((permission,i)=>!checkPermissions[i])
// if all are granted theres nothing to do
if (permissionsToRequest.length == 0)
return true
// request the ungranted permissions
let requestResult = await PermissionsAndroid.requestMultiple(permissionsToRequest);
// I think requestResult is an array of Booleans but im not sure
return Array.isArray(requestResult) ?
// if array return true only if all permissions are granted
requestResult.every(permission=>Boolean(permission)) :
requestResult
}
const data = [
{
name: "Flossi",
image: "https://robohash.org/istevelitut.png?size=50x50&set=set1",
price: 49,
info: "Cras dui."
},
{
name: "Rab",
image: "https://robohash.org/voluptatumsuntiste.png?size=50x50&set=set1",
price: 64,
info: "Phasellus insi."
}
];
// add some stuff
data.push({
name:"blah blah",
image:"https://i.kym-cdn.com/entries/icons/mobile/000/003/406/trap-card-2.jpg",
price:Math.floor(Math.random()*69),
info:"Blah blah info"
});
// save to file
if(Platform.OS == 'android'){
if(!await getFilePermissionsAndroid())
Alert.alert('Writing to external storage may fail without these permissions','Writes to internal storage should be fine tho');
}
// check out documentation for more paths
let path = RNFS.DocumentDirectoryPath + 'data.json';
saveFile(path,data);

How do I create a JS file in my react app?

I have a silly library that can only load the required meta data from another JS file. That file looks like this
// customStuff.js
__customStuff = [
{.. stuff 1},
{.. stuff 2},
{.. etc}
]
That silly library uses a parameter called custom_stuff_filename which is a string that describes the custom stuff file
{
custom_stuff_file_name: "customStuff.js"
}
customStuff.js sits in a public folder that looks like this myapp/public/silly_library/static/customStuff.js whereas the rest of my source code sits in myapp/src/
As you can see, customStuff.js is static. Stuff inside that file sits there only if I put it.
I don't want it this way. Instead, I want to make an API call in some file in my src so that a __customStuff is a result of that API call.
How do I do that? Is there any way in javascript where I can save data into another javascript file?
Try in this way :-
componentDidMount{
fetch("URL")
.then(response => {
response.blob().then(blob => {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = '__customStuff.json';
a.click();
});
}

Is there a way to dump a thousand images somewhere and extract them using REST Api?

Here is the thing:-
I have over a thousand images saved locally in my mac. I have a landing page that mocks an ecommerce deal site. It would be tedious to have to manually type in the src url in the img tag for a thousand pictures. Thus, i thought i could somehow have this images dumped in a cloud storage or something and use REST api get method to extract these images in a response.data. Then assign it to a $scope variable and use ng-repeat to bind the images in my landing page view. Is this possible? If not, what are the alternatives? SQL database?
Appreciate your help. P.S. I am totally a beginner at web development.
Install node.js. It's Javascript for a server which should make it pretty easy since you already know Javascript.
On a Mac, you can install node like this:
brew install node
Use this node.js code (credit to codepedia.com, tweaked a little by me):
//include http, fs and url module
var http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url');
imageDir = './images/';
//create http server listening on port 3333
http.createServer(function (req, res) {
//use the url to parse the requested url and get the image name
var query = url.parse(req.url,true).query;
pic = query.image;
if (typeof pic === 'undefined') {
getImages(imageDir, function (err, files) {
var imageList = JSON.stringify(files);
res.writeHead(200, {'Content-type':'application/json'});
res.end(imageList);
});
} else {
//read the image using fs and send the image content back in the response
fs.readFile(imageDir + pic, function (err, content) {
if (err) {
res.writeHead(400, {'Content-type':'text/html'})
console.log(err);
res.end("No such image");
} else {
//specify the content type in the response will be an image
res.writeHead(200,{'Content-type':'image/jpg'});
res.end(content, "binary");
}
});
}
}).listen(3333);
console.log("Server running at http://localhost:3333/");
//get the list of jpg files in the image dir
function getImages(imageDir, callback) {
var fileType = '.jpg',
files = [], i;
fs.readdir(imageDir, function (err, list) {
for(i=0; i<list.length; i++) {
if(path.extname(list[i]) === fileType) {
files.push(list[i]); //store the file name into the array files
}
}
callback(err, files);
});
}
Run this from the command line to start you new image server (assuming you named the file "server.js"):
node server.js
You should see this text appear on the command line:
Server running at http://localhost:3333/
You can quickly test it by going to this address in your browser and you should see a JSON object showing you an array of all the filenames in the "./images" directory. By the way, this program assumes you're putting the images folder in the same directory as "server.js". You can put the images directory anywhere and just change the path of the variable "imageDir".
Now you can load the list of files from Angular using this code in your controller:
$http.get("http://localhost:3333", function(data) {
$scope.images = data;
});
In your view, you can now use an ng-repeat like this to display all the images:
<div ng-repeat="image in images" style="padding: 8px">
<img src="http://localhost:3333/image={{ image }}">
</div>
Note: this will work if you run it locally on your Mac or if you upload all the images to a server on which you can use Node.js.

Is there an alternative for File() constructor for Safari and IE?

I am using the File() constructor for creating file object for uploading a blob file to the server. The following code works fine for Chrome, but fails for Safari and Internet Explorer.
image_url = new File([blob],file_name,{type: mimeString});
The code is breaking at this line and getting this error in console "FileConstructor is not a constructor" (evaluating 'new File([blob],file_name,{type: mimeString})')
Using the FileReader API is an alternative to this but I am not able to fix this issue.
I Suggest to use the blob api, I've found the same problem and I solved like that:
var html = <svg>whatever on svg </svg>
var fileName = "myfile.svg";
var blob = new Blob([html], {type: 'image/svg'});
blob.lastModifiedDate = new Date();
// var blobAttrs = {type: "image/svg"};
// var file = new File([html], fileName, blobAttrs);
var formData = new FormData();
formData.append("file",blob,fileName);
It is not a "file", but you can use it like it was.
According to web "Can I use" Safari does not support the new File() constructor. See this link http://caniuse.com/#feat=fileapi
So I think you have to either use FileReader or maybe use some of the polyfills listed here https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills
Especially this one could be useful for you https://github.com/mailru/FileAPI (I did not use it myself)
Also have a look at this SO answer What to use instead of FileReader for Safari?
If you can use ES6 classes:
class CustomFile extends Blob
{
constructor(blobParts, filename, options) {
super(blobParts, options);
this.name = filename || "";
if(options) {
this.lastModified = options.lastModified;
}
}
lastModified = 0;
name = "";
}
const blob = new Blob();
const fileObject = new CustomFile([blob],"myfile");
console.log(fileObject);
There is a File ponyfill on npm which works with modern module imports. This makes usage simple but you do need to import it in every module where you use new File().
import File from '#tanker/file-ponyfill';
const myFile = new File(['somefiledata'], 'example.txt', { type: 'text/plain'});

Resources