Cant receive data from postMessage in react native webview - react-native

here is my injected js:
window.ReactNativeWebView.postMessage(JSON.stringify({
external_url_open: null,
height: Math.max(document.body.offsetHeight, document.body.scrollHeight, document.documentElement.clientHeight)
}));
(function(){
var attachEvent = function(elem, event, callback)
{
...
}
var all_links = document.querySelectorAll('a[href]');
if ( all_links ) {
for ( var i in all_links ) {
if ( all_links.hasOwnProperty(i) ) {
attachEvent(all_links[i], 'onclick', function(e){
if ( new RegExp( '^https?:\/\/www.dropbox\.com.*', 'gi' ).test( this.href ) ) {
// handle external URL
e.preventDefault();
window.ReactNativeWebView.postMessage(JSON.stringify({
external_url_open: this.href,
}));
}
else { window.location.href = this.href; }
});
}
}
}
})();
I want to receive the pages height on react native side with this code:
<WebView
...
javascriptEnabled={true}
onMessage={(event) => {
// retrieve event data
var data = event.nativeEvent.data; // maybe parse stringified JSON
try {
data = JSON.parse(data)
} catch ( e ) { }
// check if this message concerns us
if ( 'object' == typeof data && data.external_url_open ) {
if (data.external_url_open != null){
// proceed with URL open request
Linking.openURL(data.external_url_open);
}
else {
height = data.height;
console.log(data.page_height);
}
}
}}
injectedJavaScript={jsCode}
...
/>
the link handling part works but the height receive part doesn't and I dont see anything in console when refreshing the page. Can anyone help?

I just needed to relocate this else to the outer if:
else {
height = data.height;
console.log(data.page_height);
}

Related

ngx-dropzone to load images from list

I am using ngx-dropzone with angular 8. I have used ngx-dropzone for uploading and it works but this time I want to load images from a specific list to dropzone. Here is my code.
<ngx-dropzone (change)="onSelect($event)">
<ngx-dropzone-label>Select/Drop images here!</ngx-dropzone-label>
<ngx-dropzone-image-preview ngProjectAs="ngx-dropzone-preview" [removable]="true" (removed)="onRemove(f)" *ngFor="let f of files" [file]="f">
<ngx-dropzone-label></ngx-dropzone-label>
</ngx-dropzone-image-preview>
</ngx-dropzone>
Here is my onSelect event which bindimages and push them into files array list.
onSelect(event) {
this.alertService.clear();
console.log(event.addedFiles);
this.files.push(...event.addedFiles);
if (this.files.length > 4) {
this.alertService.error('Please select only four images for each service.');
this.files = [];
} else {
this.bindImages();
}
}
bindImages() {
this.alertService.clear();
this.imageList = [];
this.files.forEach((x) => {
const file = x;
if (file.type.split('/')[0] !== 'image') {
this.alertService.error('Please select image to proceed further.');
return false;
}
if (file.size > 5242880) {
this.alertService.error('Image size must be equal to or less then 5 MB.');
return false;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
const val = reader.result as string;
const items = [{Serviceimage: val, ServiceId: this.serviceId, SaloonId: this.saloonId}];
this.imageList.push(...items);
};
reader.onerror = (error) => {
console.log('Error: ', error);
return;
};
});
}
Here is the my api codes which returns me the list of images with base64 string
getImages() {
this.alertService.clear();
this.imageModel.SaloonId = this.saloonId;
this.imageModel.ServiceId = this.serviceId;
this.apiService.Create('Saloon/getServiceImages', this.imageModel).subscribe(
resp => {
if (resp.length > 0) {
// Here to load Images to dropzone.
}
console.log(this.files);
},
error => {
this.alertService.error('Error getting images. Please contact admin.');
},
() => { console.log('complete'); }
);
}
This image returns me the following list.

Issue with react-native FlatList rendering data when updating array of objects maintained in redux

I'm loading a dynamic form in a FlatList. I'm supposed to add few rows with UI elements such as TextInput, checkbox, etc. based on a condition. For example, I have a dropdown, when I select a value, I have to add a row with a button and remove that row when I select another value.
My issue is that whenever I select a value in the dropdown to add a row with button, FlatList is adding duplicate rows and they are not visible as objects in my array when I debug them. I'm using Redux to manage my application state and updating my array in reducers.
I have to display objects in my array in FlatList without any duplicates using Redux for state management.
Code:
<FlatList
key={Math.floor(Math.random())}
ref={(ref) => { this.flatListRef = ref; }}
data={this.state.mainArray}
renderItem={this._renderItem}
keyExtractor={({ item, id }) => String(id)}
extraData={this.prop}
keyboardDismissMode="on-drag"
showsVerticalScrollIndicator={false}
scrollEnabled={true}
removeClippedSubviews={false}
scrollsToTop={false}
initialNumToRender={100}
/>
componentWillReceiveProps(nextProps) {
this.setState({mainArray: nextProps.mainArray});
}
Updating array in Reducer:
case VALIDATE_DEPENDENCY: {
// adding or removing dependency questions
let dependingQues = [];
let formArrayTemp = [...state.mainArray];
let exists;
// console.log("formArrayTemp123", formArrayTemp);
if (action.item.isDependentQuestion) {
if (action.item.dependentQuestionsArray.length > 0) {
let dependent_ques = action.item.dependentQuestionsArray[0];
state.depending_questions_array.filter((obj, id) => {
if (JSON.stringify(obj.key_array) === JSON.stringify(dependent_ques.key)) {
dependingQues.push(obj);
}
});
// console.log("dependingQues123", dependingQues);
if (dependent_ques.answer === action.item.answer) {
dependingQues.map((obj, id) => {
exists = formArrayTemp.filter((res, idx) => {
if (JSON.stringify(res.key_array) === JSON.stringify(obj.key_array)) {
return res;
}
});
// console.log("exists123", exists);
if (exists.length === 0) {
formArrayTemp.splice(action.index + id + 1, 0, obj);
// console.log("mjkgthbythyhy", action.index + id + 1);
}
});
}
else {
let objIndex = formArrayTemp.findIndex(obj => JSON.stringify(obj.key_array) === JSON.stringify(dependent_ques.key));
if (objIndex !== -1) {
formArrayTemp[objIndex].answer = "";
formArrayTemp[objIndex].borderColor = "black";
formArrayTemp.splice(objIndex, 1);
// console.log("frtg6hyjukiu", formArrayTemp);
}
}
}
}
// adding or removing dependent sections
let dependentSections = [];
let tempArr = [];
let count = 0;
if (action.item.isDependent) {
if (action.item.dependentArray.length > 0) {
let dependent_section = action.item.dependentArray[0];
state.dependent_sections_array.filter((obj, id) => {
if (obj.section_id === dependent_section.section_id) {
dependentSections.push(obj);
}
});
// console.log("dependentSections123", dependentSections);
let lastIndex;
formArrayTemp.filter((res, id) => {
if (action.item.section_id === res.section_id && res.dependentArray &&
JSON.stringify(action.item.dependentArray) === JSON.stringify(res.dependentArray)) {
tempArr.push(res);
lastIndex = formArrayTemp.FindLastIndex(a => a.section_id === res.section_id);
}
});
// console.log("lastIndex123", lastIndex);
tempArr.map((obj, id) => {
if (obj.answer === obj.dependentArray[0].answer) {
count++;
}
});
if (dependent_section.answer === action.item.answer) {
dependentSections.map((obj, id) => {
if (formArrayTemp.contains(obj) === false) {
formArrayTemp.splice(lastIndex + id + 1, 0, obj);
// console.log("cfrererfre", formArrayTemp);
}
});
}
else {
let countTemp = [];
if (count === 0) {
let countTemp = [];
dependentSections.filter((res, id) => {
if (formArrayTemp.contains(res) === true) {
formArrayTemp.filter((obj, idx) => {
if (obj.section_id === res.section_id) {
obj.answer = "";
obj.borderColor = "black";
countTemp.push(obj);
}
});
}
});
let jobsUnique = Array.from(new Set(countTemp));
formArrayTemp = formArrayTemp.filter(item => !jobsUnique.includes(item));
// console.log("frefrefre123", formArrayTemp);
// return { ...state, mainArray: [...formArrayTemp] }
}
}
}
}
console.log("formArray123", formArrayTemp);
formArrayTemp.sort((a, b) => {
return a.posiiton - b.position;
});
return { ...state, mainArray: formArrayTemp }
};
Update
After researching this issue I came across this issue #24206 in GitHub. There it states:
There's a few things you aren't doing quite correctly here. Each time the render function runs you are creating a new array object for your data, so React has to rerender the entire list each time, you're renderItem function and keyExtractor function are also remade each rerender, and so the FlatList has to be rerun. You're also using the index for your key which means as soon as anything moves it will rerender everything after that point. I would recommend looking at what does/doesn't change when you rerun things (arrays, functions, object), and why the keyExtractor is important for reducing re-renders.
So I tried commenting KeyExtractor prop of FlatList and the issue got resolved. But this is causing issue in another form. How can I resolve that?

Hide/Show the corresponding data from the chart on legend click ngx-charts

I am working with angular 6 and ngx-chart and I need on clicking the legend item, the corresponding data from the chart should show/hide
The ng-chart library does have this functionality and my client requests it.
Edit01:
I have almost everything working but I have a problem when applying axisFormat. once I remove an item from the legend it reformats the x-axis and doesn't literally put how the data comes without applying the AxisFormat. Any solution?
onSelect (event) {
let temp = JSON.parse(JSON.stringify(this.multi));
this.sourceData = JSON.parse(JSON.stringify(this.multi2));
if (this.isDataShown(event)) {
//Hide it
temp.some(pie => {
const pie2 = pie.name[0] + ',' + pie.name[1];
// console.log('pie', pie[0], event, pie2);
if (pie2 === event) {
pie.series = [];
return true;
}
});
} else {
//Show it back
console.log('Entramos en el ELSE');
const pieToAdd = this.sourceData.filter(pie => {
const pie2 = pie.name[0] + ',' + pie.name[1];
return pie2 === event;
});
temp.some(pie => {
const pie2 = pie.name[0] + ',' + pie.name[1];
if (pie2 === event) {
pie.series = pieToAdd[0].series;
return true;
}
});
}
console.log('log temp: ' + JSON.stringify(temp));
this.multi = temp;
// this.axisFormat(this.multi);
}
isDataShown = (name) => {
const selectedPie = this.multi.filter(pie => {
const pie2 = pie.name[0] + ',' + pie.name[1];
return pie2 === name && pie.series[0] !== undefined;
});
return selectedPie && selectedPie.length > 0;
}
axisFormat(val) {
const options = { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' };
// Esto funciona pero al hacer timeline no pone horas val.toLocaleDateString("es-ES", options);
console.log('val:', val.toLocaleDateString('es-ES', options));
return val.toLocaleDateString('es-ES', options);
}
HTML
<ngx-charts-line-chart [view]="" [scheme]="colorScheme" [results]="multi" [gradient]="gradient" [xAxis]="showXAxis" [yAxis]="showYAxis" [legend]="showLegend" legendPosition="'below'" [showXAxisLabel]="showXAxisLabel" [showYAxisLabel]="showYAxisLabel"
[xAxisLabel]="xAxisLabel" [yAxisLabel]="yAxisLabel" [autoScale]="autoScale" [timeline]="timeline" [roundDomains]="true" [animations]="animations" (select)="onSelect($event)" [xAxisTickFormatting]="axisFormat">
<ng-template #seriesTooltipTemplate let-items="model">
<p>{{items[0].name | date:'medium'}}</p>
<ul>
<li *ngFor="let item of items">
{{item.series}}: {{item.value | number}}
</li>
</ul>
</ng-template>
</ngx-charts-line-chart>
EDIT
Hello,
I have already managed to solve the problem adding an example in case it can help other people.
https://stackblitz.com/edit/click-lengd-ngx-charts
I am kinda new to Stack Overflow, but i think you should specify your answer more and show us what you already tried. Nevertheless I will try to help you.
You should give your chart a (select)="onClickFunction ($event)" in HTML. In your TS you then call the onClickFunction(event). I always start with giving it a console.log(event) to see what i get from clicking on the legend.
After clicking on the legend, you get the label of the element you clicked on. You can then search for this label in your data and remove this data out of the array you use for filling the chart.
If you provide a stackblitz or plunker wtih your code, I will gladly show you how to do it.
This is how we can achieve it for ngx-pie-chart. With the help of select event, capture it, identify the item from the data and make it zero. Next, on the next click, add the value back from the source copy. See it working here ngx-pie-chart label-filter
onSelect (event) {
let temp = JSON.parse(JSON.stringify(this.single));
if (this.isDataShown(event)) {
//Hide it
temp.some(pie => {
if (pie.name === event) {
pie.value = 0;
return true;
}
});
} else {
//Show it back
const pieToAdd = this.sourceData.filter(pie => {
return pie.name === event;
});
temp.some(pie => {
if (pie.name === event) {
pie.value = pieToAdd[0].value;
return true;
}
});
}
this.single = temp;
}
isDataShown = (name) => {
const selectedPie = this.single.filter(pie => {
return pie.name === name && pie.value !== 0;
});
return selectedPie && selectedPie.length > 0;
}

React-Native componentWillRecieveProps rendering issue

In my RN project, I want to achieve this.
// componentWillReceiveProps
componentWillReceiveProps = async (nextProps) => {
let { searchText, peopleTab } = this.props;
let params = {};
if (peopleTab !== nextProps.peopleTab) {
params.peopleTab = nextProps.peopleTab;
}
// here i want to pass nextProps.searchText without a condition with the params like this.
// params.searchText = nextProps.searchText
if (Object.keys(params).length > 0) {
await this.props.fetchUnfollowedPeople(params);
}
}
I want to send nextProps.searchText with params object, if there is a new value. Otherwise I want to send this.props.searchText with the params object.
The above code, if I uncomment
// params.searchText = nextProps.searchText
it gives the infinity loop. How can I achieve this?
Setting the let { searchText, peopleTab } = this.props; in componentWillReceiveProps causes the new value to be pasted
componentWillMount() {
this.searchText = this.props.searchText ;
this.peopleTab = this.props.peopleTab ;
}
componentWillReceiveProps = async (nextProps) => {
const params = [];
if (this.peopleTab !== nextProps.peopleTab) {
params['peopleTab'] = nextProps.peopleTab ;
}
if (Object.keys(params).length > 0) {
await this.props.fetchUnfollowedPeople(params);
}
}

StreamTrack's readyState is getting changed to ended, just before playing the stream (MediaStream - MediaStreamTrack - WebRTC)

The jsfiddle (https://jsfiddle.net/kalyansai99/mm1b74uy/22/) contains code where the user can toggle between front and back camera of the mobile.
In few mobiles its working fine (Moto g5 plus, Moto E3 and so on - Chrome Browser) and in few mobiles (Mi Redimi Note 4 - Chrome Browser) when I am switching to back camera, initially the stream is loading with a track of "readyState" as "live". But when i am about to play the stream in video player, the "readyState" is getting changed to "ended" and black screen is been shown on the video tag.
Not sure whats happening. Any clues?
JSFiddle Code
var player = document.getElementById('player');
var flipBtn = document.getElementById('flipBtn');
var deviceIdMap = {};
var front;
var constraints = {
audio: false,
video: {
frameRate: 1000
}
};
var gotDevices = function (deviceList) {
var length = deviceList.length;
console.log(deviceList);
for (var i = 0; i < length; i++) {
var deviceInfo = deviceList[i];
if (deviceInfo.kind === 'videoinput') {
if (deviceInfo.label.indexOf('front') !== -1) {
deviceIdMap.front = deviceInfo.deviceId;
} else if (deviceInfo.label.indexOf('back') !== -1) {
deviceIdMap.back = deviceInfo.deviceId;
}
}
}
if (deviceIdMap.front) {
constraints.video.deviceId = {exact: deviceIdMap.front};
front = true;
} else if (deviceIdMap.back) {
constraints.video.deviceId = {exact: deviceIdMap.back};
front = false;
}
console.log('deviceIdMap - ', deviceIdMap);
};
var handleError = function (error) {
console.log('navigator.getUserMedia error: ', error);
};
function handleSuccess(stream) {
window.stream = stream;
// this is a video track as there is no audio track
console.log("Track - ", window.stream.getTracks()[0]);
console.log('Ready State - ', window.stream.getTracks()[0].readyState);
if (window.URL) {
player.src = window.URL.createObjectURL(stream);
} else {
player.src = stream;
}
player.onloadedmetadata = function (e) {
console.log('Ready State - 3', window.stream.getTracks()[0].readyState);
player.play();
console.log('Ready State - 4', window.stream.getTracks()[0].readyState);
}
console.log('Ready State - 2', window.stream.getTracks()[0].readyState);
}
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
flipBtn.addEventListener('click', function () {
if (window.stream) {
window.stream.getTracks().forEach(function(track) {
track.stop();
});
}
if (front) {
constraints.video.deviceId = {exact: deviceIdMap.back};
} else {
constraints.video.deviceId = {exact: deviceIdMap.front};
}
front = !front;
navigator.getUserMedia(constraints, handleSuccess, handleError);
}, false);
console.log(constraints);
navigator.getUserMedia(constraints, handleSuccess, handleError);
#player {
width: 320px;
}
#flipBtn {
width: 150px;
height: 50px;
}
<video id="player" autoplay></video>
<div>
<button id="flipBtn">
Flip Camera
</button>
</div>
Replace track.stop() to track.enabled=false and when adding track to the stream, enable it back using track.enabled=true
The MediaStream.readyState property is changed to "ended" when we stop the track and can never be used again. Therefore its not wise to use stop. For more reference:
https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/readyState
https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/stop