I'm trying to dynamically edit the cell value but some of the components are not working properly like MoodCellRenderer and NumericCellEditor. Other editor components are working fine.
Can you please help me to make it work?
import React, {Component} from "react";
import {render} from "react-dom";
import {AgGridReact} from "ag-grid-react";
import "ag-grid-enterprise";
import 'bootstrap/dist/js/bootstrap.js';
function cloneObject(obj) {
return JSON.parse(JSON.stringify(obj));
}
class Example8 extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [{
headerName: "Value",
field: "value",
width: 100,
editable: true,
cellEditorSelector: function(params) {
if (params.data.type === 'age') return {
component: 'numericCellEditor'
};
if (params.data.type === 'gender') return {
component: 'agRichSelectCellEditor',
params: {
values: ['Male', 'Female']
}
};
if (params.data.type === 'mood') return {
component: 'moodEditor'
};
return null;
}
},
{
headerName: "Type",
field: "type",
width: 100
}
],
rowData: [{
value: 14,
type: 'age'
},
{
value: 'female',
type: 'gender'
},
{
value: "Happy",
type: 'mood'
},
{
value: 21,
type: 'age'
},
{
value: 'male',
type: 'gender'
},
{
value: "Sad",
type: 'mood'
}
],
gridOptions: {
onRowEditingStarted: function(event) {
console.log('never called - not doing row editing');
},
onRowEditingStopped: function(event) {
console.log('never called - not doing row editing');
},
onCellEditingStarted: function(event) {
console.log('cellEditingStarted');
},
onCellEditingStopped: function(event) {
console.log('cellEditingStopped');
},
components: {
genderCellRenderer: GenderCellRenderer,
numericCellEditor: NumericCellEditor,
moodCellRenderer: MoodCellRenderer,
moodEditor: MoodEditor,
countryCellRenderer: CountryCellRenderer
}
}
};
}
onGridReady = params => {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
params.api.sizeColumnsToFit();
};
render() {
return (
<div style={{ width: "100%", height: "100%" }}>
<div
id="myGrid"
style={{
height: "100%",
width: "100%"
}}
className="ag-theme-balham"
>
<AgGridReact
columnDefs={this.state.columnDefs}
components={this.state.components}
rowData={this.state.rowData}
onGridReady={this.onGridReady}
/>
</div>
</div>
);
}
}
function getCharCodeFromEvent(event) {
event = event || window.event;
return (typeof event.which == "undefined") ? event.keyCode : event.which;
}
function isCharNumeric(charStr) {
return !!/\d/.test(charStr);
}
function isKeyPressedNumeric(event) {
var charCode = getCharCodeFromEvent(event);
var charStr = String.fromCharCode(charCode);
return isCharNumeric(charStr);
}
// simple function cellRenderer, just returns back the name of the country
function CountryCellRenderer(params) {
return params.value.name;
}
// function to act as a class
function NumericCellEditor() {}
// gets called once before the renderer is used
NumericCellEditor.prototype.init = function(params) {
// create the cell
this.eInput = document.createElement('input');
if (isCharNumeric(params.charPress)) {
this.eInput.value = params.charPress;
} else {
if (params.value !== undefined && params.value !== null) {
this.eInput.value = params.value;
}
}
var that = this;
this.eInput.addEventListener('keypress', function(event) {
if (!isKeyPressedNumeric(event)) {
that.eInput.focus();
if (event.preventDefault) event.preventDefault();
} else if (that.isKeyPressedNavigation(event)) {
event.stopPropagation();
}
});
// only start edit if key pressed is a number, not a letter
var charPressIsNotANumber = params.charPress && ('1234567890'.indexOf(params.charPress) < 0);
this.cancelBeforeStart = charPressIsNotANumber;
};
NumericCellEditor.prototype.isKeyPressedNavigation = function(event) {
return event.keyCode === 39 ||
event.keyCode === 37;
};
// gets called once when grid ready to insert the element
NumericCellEditor.prototype.getGui = function() {
return this.eInput;
};
// focus and select can be done after the gui is attached
NumericCellEditor.prototype.afterGuiAttached = function() {
this.eInput.focus();
};
// returns the new value after editing
NumericCellEditor.prototype.isCancelBeforeStart = function() {
return this.cancelBeforeStart;
};
// example - will reject the number if it contains the value 007
// - not very practical, but demonstrates the method.
NumericCellEditor.prototype.isCancelAfterEnd = function() {
var value = this.getValue();
return value.indexOf('007') >= 0;
};
// returns the new value after editing
NumericCellEditor.prototype.getValue = function() {
return this.eInput.value;
};
// any cleanup we need to be done here
NumericCellEditor.prototype.destroy = function() {
// but this example is simple, no cleanup, we could even leave this method out as it's optional
};
// if true, then this editor will appear in a popup
NumericCellEditor.prototype.isPopup = function() {
// and we could leave this method out also, false is the default
return false;
};
function GenderCellRenderer() {}
GenderCellRenderer.prototype.init = function(params) {
this.eGui = document.createElement('span');
if (params.value !== "" || params.value !== undefined || params.value !== null) {
var gender = '<img border="0" width="15" height="10" src="https://raw.githubusercontent.com/ag-grid/ag-grid/master/packages/ag-grid-docs/src/images/' + params.value.toLowerCase() + '.png">';
this.eGui.innerHTML = gender + ' ' + params.value;
}
};
GenderCellRenderer.prototype.getGui = function() {
return this.eGui;
};
function MoodCellRenderer() {}
MoodCellRenderer.prototype.init = function(params) {
this.eGui = document.createElement('span');
if (params.value !== "" || params.value !== undefined || params.value !== null) {
var imgForMood = params.value === 'Happy' ? 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/packages/ag-grid-docs/src/images/smiley.png' : 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/packages/ag-grid-docs/src/images/smiley-sad.png';
this.eGui.innerHTML = '<img width="20px" src="' + imgForMood + '" />';
}
};
MoodCellRenderer.prototype.getGui = function() {
return this.eGui;
};
function MoodEditor() {
this.defaultImgStyle = 'padding-left:10px; padding-right:10px; border: 1px solid transparent; padding: 4px;';
this.selectedImgStyle = 'padding-left:10px; padding-right:10px; border: 1px solid lightgreen; padding: 4px;';
}
MoodEditor.prototype.onKeyDown = function(event) {
var key = event.which || event.keyCode;
if (key == 37 || // left
key == 39) { // right
this.toggleMood();
event.stopPropagation();
}
};
MoodEditor.prototype.toggleMood = function() {
this.selectMood(this.mood === 'Happy' ? 'Sad' : 'Happy');
};
MoodEditor.prototype.init = function(params) {
this.container = document.createElement('div');
this.container.style = "border-radius: 15px; border: 1px solid grey;background: #e6e6e6;padding: 15px; text-align:center;display:inline-block;outline:none";
this.container.tabIndex = "0"; // to allow the div to capture keypresses
this.happyImg = document.createElement('img');
this.happyImg.src = 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/packages/ag-grid-docs/src/images/smiley.png';
this.happyImg.style = this.defaultImgStyle;
this.sadImg = document.createElement('img');
this.sadImg.src = 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/packages/ag-grid-docs/src/images/smiley-sad.png';
this.sadImg.style = this.defaultImgStyle;
this.container.appendChild(this.happyImg);
this.container.appendChild(this.sadImg);
var that = this;
this.happyImg.addEventListener('click', function(event) {
that.selectMood('Happy');
params.stopEditing();
});
this.sadImg.addEventListener('click', function(event) {
that.selectMood('Sad');
params.stopEditing();
});
this.container.addEventListener('keydown', function(event) {
that.onKeyDown(event)
});
this.selectMood(params.value);
};
MoodEditor.prototype.selectMood = function(mood) {
this.mood = mood;
this.happyImg.style = (mood === 'Happy') ? this.selectedImgStyle : this.defaultImgStyle;
this.sadImg.style = (mood === 'Sad') ? this.selectedImgStyle : this.defaultImgStyle;
};
// gets called once when grid ready to insert the element
MoodEditor.prototype.getGui = function() {
return this.container;
};
MoodEditor.prototype.afterGuiAttached = function() {
this.container.focus();
};
MoodEditor.prototype.getValue = function() {
return this.mood;
};
// any cleanup we need to be done here
MoodEditor.prototype.destroy = function() {};
MoodEditor.prototype.isPopup = function() {
return true;
};
// setup the grid after the page has finished loading
export default Example8;
import React, { Component } from 'react'
import { render } from 'react-dom'
// import { AgGridReact } from 'ag-grid-react'
import 'ag-grid-enterprise'
import 'bootstrap/dist/js/bootstrap.js'
import 'ag-grid/dist/styles/ag-grid.css'
import 'ag-grid/dist/styles/ag-theme-balham.css'
import {DataGrid} from '#cib-jpm-ui-toolkit/data-grid'
import $ from 'jquery'
function cloneObject (obj) {
return JSON.parse(JSON.stringify(obj))
}
class InvoiceTextFields extends Component {
constructor (props) {
super(props)
this.state = {
columnDefs: [
{headerName: 'Field Name', field: 'fieldName', width: 70},
{headerName: 'Extracted Taxonomy', field: 'extractedTaxonomy', width: 70},
{headerName: 'Taxonomy Data Field', field: 'taxonomyDataField', width: 70},
{
headerName: 'Corrected Texonomy',
field: 'value',
width: 70,
editable: true,
cellEditorSelector:function (params) {
if (params.data.type === 'age') {
return {
component: 'numericCellEditor'
}
}
if (params.data.type === 'examination') {
return {
component: 'agSelectCellEditor',
params: { values: extractValues(carMappings) },
valueFormatter: function (params) {
return lookupValue(carMappings, params.value)
},
valueParser: function (params) {
return lookupKey(carMappings, params.newValue)
}
}
}
if (params.data.type === 'tenor') {
return {
component: 'agSelectCellEditor',
params: { values: extractValues(tenorMappings) },
valueFormatter: function (params) {
return lookupValue(carMappings, params.value)
},
valueParser: function (params) {
return lookupKey(carMappings, params.newValue)
}
}
}
if (params.data.type === 'dateVal') {
return {
cellEditor: 'datePicker'
}
}
return null
}
}
],
// components: { datePicker: getDatePicker() },
rowData: [
{value: '28/02/2019', type: 'dateVal', fieldName:'Expiry Date'},
{value: 'YES', type: 'tenor', fieldName:'Tenor Type'},
{value: 'Vessel 1', type: 'text', fieldName:'Vessel Name'},
{value: 'Examine Document', type: 'examination', fieldName:'Examination Type'},
],
gridOptions:{
onRowEditingStarted: function (event) {
console.log('never called - not doing row editing')
},
onRowEditingStopped: function (event) {
console.log('never called - not doing row editing')
},
onCellEditingStarted: function (event) {
console.log('cellEditingStarted')
},
onCellEditingStopped: function (event) {
console.log('cellEditingStopped')
}
}
}
}
onGridReady = params => {
this.gridApi = params.api
this.gridColumnApi = params.columnApi
params.api.sizeColumnsToFit()
};
onCellClicked = (event) => {
console.log('hi: ' + event.colDef.field)
console.log('hi type: ' + event.colDef.type)
if (this.props.correctedTaxonomy === null) {
console.log('null')
}
if (this.props.correctedTaxonomy === '') {
console.log('empty')
}
if (event.colDef.field === 'value' && this.props.correctedTaxonomy !== null) {
var rowModel = this.gridApi.getModel()
var rowNode = rowModel.rowsToDisplay[event.rowIndex]
event.data.correctedText = this.props.correctedTaxonomy
rowNode.setDataValue('value', event.data.correctedText)
}
};
render () {
return (
<div style={{ width: '100%', height: '100%' }}>
<div
id='myGrid'
style={{
height: '100%',
width: '103%'
}}
className='ag-theme-balham'
>
<DataGrid
columnDefs={this.state.columnDefs}
// components={this.state.components}
rowData={this.state.rowData}
gridOptions={this.state.gridOptions}
onGridReady={this.onGridReady}
onCellClicked={this.onCellClicked.bind(this)}
/>
</div>
</div>
)
}
}
var carMappings = {
'Examine Document': 'Examine Document',
'Do not Examine': 'Do not Examine',
'Send on approval with Doc Exam': 'Send on approval with Doc Exam',
'Send on Approval Without Doc' : 'Send on Approval Without Doc ',
'Exam' : 'Exam'
}
var tenorMappings = {
'YES': 'YES',
'NO': 'NO'
}
function extractValues (mappings) {
return Object.keys(mappings)
}
function lookupValue (mappings, key) {
return mappings[key]
}
function lookupKey (mappings, name) {
for (var key in mappings) {
if (mappings.hasOwnProperty(key)) {
if (name === mappings[key]) {
return key
}
}
}
}
function getCharCodeFromEvent (event) {
event = event || window.event
return (typeof event.which === 'undefined') ? event.keyCode : event.which
}
function isCharNumeric (charStr) {
return !!/\d/.test(charStr)
}
function isKeyPressedNumeric (event) {
var charCode = getCharCodeFromEvent(event)
var charStr = String.fromCharCode(charCode)
return isCharNumeric(charStr)
}
function getDatePicker () {
console.log('in gerDatePicker...')
function Datepicker () {}
Datepicker.prototype.init = function (params) {
this.eInput = document.createElement('input')
this.eInput.value = params.value
$(this.eInput).datepicker({ dateFormat: 'dd/mm/yy' })
}
Datepicker.prototype.getGui = function () {
return this.eInput
}
Datepicker.prototype.afterGuiAttached = function () {
this.eInput.focus()
this.eInput.select()
}
Datepicker.prototype.getValue = function () {
return this.eInput.value
}
Datepicker.prototype.destroy = function () {}
Datepicker.prototype.isPopup = function () {
return false
}
return Datepicker
}
export default InvoiceTextFields
import React, { Component } from "react";
import { render } from "react-dom";
import { AgGridReact } from "ag-grid-react";
import "ag-grid-enterprise";
import 'bootstrap/dist/js/bootstrap.js';
import $ from 'jquery'
function cloneObject (obj) {
return JSON.parse(JSON.stringify(obj))
}
class InvoiceTextFields extends Component {
constructor (props) {
super(props)
this.state = {
columnDefs: [
{headerName: 'Field Name', field: 'fieldName', width: 70},
{headerName: 'Extracted Taxonomy', field: 'extractedTaxonomy', width: 70},
{headerName: 'Taxonomy Data Field', field: 'taxonomyDataField', width: 70},
{
headerName: 'Corrected Texonomy',
field: 'value',
width: 70,
editable: true,
cellEditorSelector:function (params) {
if (params.data.type === 'age') {
return {
component: 'numericCellEditor'
}
}
if (params.data.type === 'examination') {
return {
component: 'agRichSelectCellEditor',
params: { values: extractValues(carMappings) },
valueFormatter: function (params) {
return lookupValue(carMappings, params.value)
},
valueParser: function (params) {
return lookupKey(carMappings, params.newValue)
}
}
}
if (params.data.type === 'tenor') {
return {
component: 'agSelectCellEditor',
params: { values: extractValues(tenorMappings) },
valueFormatter: function (params) {
return lookupValue(carMappings, params.value)
},
valueParser: function (params) {
return lookupKey(carMappings, params.newValue)
}
}
}
if (params.data.type === 'dateVal') {
return {
cellEditor: 'datePicker'
}
}
return null
}
}
],
// components: { datePicker: getDatePicker() },
rowData: [
{value: '28/02/2019', type: 'dateVal', fieldName:'Expiry Date'},
{value: 'YES', type: 'tenor', fieldName:'Tenor Type'},
{value: 'Vessel 1', type: 'text', fieldName:'Vessel Name'},
{value: 'Examine Document', type: 'examination', fieldName:'Examination Type'},
],
gridOptions:{
onRowEditingStarted: function (event) {
console.log('never called - not doing row editing')
},
onRowEditingStopped: function (event) {
console.log('never called - not doing row editing')
},
onCellEditingStarted: function (event) {
console.log('cellEditingStarted')
},
onCellEditingStopped: function (event) {
console.log('cellEditingStopped')
}
}
}
}
onGridReady = params => {
this.gridApi = params.api
this.gridColumnApi = params.columnApi
params.api.sizeColumnsToFit()
};
onCellClicked = (event) => {
console.log('hi: ' + event.colDef.field)
console.log('hi type: ' + event.colDef.type)
if (this.props.correctedTaxonomy === null) {
console.log('null')
}
if (this.props.correctedTaxonomy === '') {
console.log('empty')
}
if (event.colDef.field === 'value' && this.props.correctedTaxonomy !== null) {
var rowModel = this.gridApi.getModel()
var rowNode = rowModel.rowsToDisplay[event.rowIndex]
event.data.correctedText = this.props.correctedTaxonomy
rowNode.setDataValue('value', event.data.correctedText)
}
};
render () {
return (
<div style={{ width: '100%', height: '100%' }}>
<div
id='myGrid'
style={{
height: '100%',
width: '103%'
}}
className='ag-theme-balham'
>
<AgGridReact
columnDefs={this.state.columnDefs}
// components={this.state.components}
rowData={this.state.rowData}
gridOptions={this.state.gridOptions}
onGridReady={this.onGridReady}
// onCellClicked={this.onCellClicked.bind(this)}
/>
</div>
</div>
)
}
}
var carMappings = {
'Examine Document': 'Examine Document',
'Do not Examine': 'Do not Examine',
'Send on approval with Doc Exam': 'Send on approval with Doc Exam',
'Send on Approval Without Doc' : 'Send on Approval Without Doc ',
'Exam' : 'Exam'
}
var tenorMappings = {
'YES': 'YES',
'NO': 'NO'
}
function extractValues (mappings) {
return Object.keys(mappings)
}
function lookupValue (mappings, key) {
return mappings[key]
}
function lookupKey (mappings, name) {
for (var key in mappings) {
if (mappings.hasOwnProperty(key)) {
if (name === mappings[key]) {
return key
}
}
}
}
function getCharCodeFromEvent (event) {
event = event || window.event
return (typeof event.which === 'undefined') ? event.keyCode : event.which
}
function isCharNumeric (charStr) {
return !!/\d/.test(charStr)
}
function isKeyPressedNumeric (event) {
var charCode = getCharCodeFromEvent(event)
var charStr = String.fromCharCode(charCode)
return isCharNumeric(charStr)
}
function getDatePicker () {
console.log('in gerDatePicker...')
function Datepicker () {}
Datepicker.prototype.init = function (params) {
this.eInput = document.createElement('input')
this.eInput.value = params.value
$(this.eInput).datepicker({ dateFormat: 'dd/mm/yy' })
}
Datepicker.prototype.getGui = function () {
return this.eInput
}
Datepicker.prototype.afterGuiAttached = function () {
this.eInput.focus()
this.eInput.select()
}
Datepicker.prototype.getValue = function () {
return this.eInput.value
}
Datepicker.prototype.destroy = function () {}
Datepicker.prototype.isPopup = function () {
return false
}
return Datepicker
}
export default InvoiceTextFields
Related
I followed this recommendation from the Leaflet Routing Machine regarding interactions i.e. onClicks.
With my implementation, I'm saving the waypoints in local-storage—saving the latitude and longitude obj I get from the map click, to an array called markers
The event handler has a conditional which separates the click into two outcomes—an adding (to the markers array) or updating it.
Like I said in the title, initial interaction is fine, it's just when I remove any marker and try to add it again is the problem. Also I noticed the markers array is completely empty, and next event fired is an update when clearly it should be an addition:
Here is the relevant code in the Routing Machine:
class Routing extends MapLayer {
static contextType = UserContextDispatch;
constructor(props) {
super(props);
this.state = {
showSpinner: false,
localDispatch: null,
};
this.handleLoader = this.handleLoader.bind(this);
this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
this.handleSetMarker = this.handleSetMarker.bind(this);
}
handleRemoveWayPoint() {
var waypoints = this.control.getWaypoints();
for (let i = waypoints.length - 1; i >= 0; i--) {
console.log('waypoints[i].latLng !== null ', waypoints[i].latLng !== null);
if (waypoints[i].latLng !== null) {
waypoints[i].latLng = null;
break;
}
}
this.control.setWaypoints(waypoints);
}
createLeafletElement(props) {
const { map } = this.props.leaflet;
if (map && !this.control) {
this.control = L.Routing.control({
collapsible: true,
show: false,
position: 'bottomleft',
lineOptions: {
styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
},
waypoints: [null],
createMarker: function(i, wp, nWps) {
if (i === 0) {
return L.marker(wp.latLng, {
icon: startIcon,
draggable: true,
keyboard: true,
alt: 'current location'
}).on('drag', function(e) {
e.latlng.alt = 'current location';
console.log('there be dragons start!!', e);
RoutingMachineRef.handleSetMarker({
...e.oldLatLng,
...e.latlng
});
});
}
if (i === nWps - 1) {
return L.marker(wp.latLng, {
icon: endIcon,
draggable: true,
alt: 'current destination'
}).on('drag', function(e) {
e.latlng.alt = 'current destination';
console.log('there be dragons dest!!', e);
RoutingMachineRef.handleSetMarker({
...e.oldLatLng,
...e.latlng
});
});
}
}
});
L.Routing.errorControl(this.control).addTo(map);
}
return this.control.getPlan();
}
componentDidMount() {
const { map } = this.props.leaflet;
console.log('markers ', markers);
this.setState(prevState => {
localDispatch: prevState.localDispatch = this.context.dispatch;
});
map.addControl(this.control);
}
updateLeafletElement(fromProps, toProps) {
const { map } = this.props.leaflet;
var self = this;
self;
var { markers } = this.props;
function createButton(label, container) {
var btn = L.DomUtil.create('button', '', container);
btn.setAttribute('type', 'button');
btn.innerHTML = label;
return btn;
}
var { localDispatch } = this.state;
var container = L.DomUtil.create('div'),
startBtn = createButton('Start from this location', container),
destBtn = createButton('Go to this location', container);
map.on(
'click',
function(e) {
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn, 'click', function() {
if (e.latlng) {
e.latlng.alt = 'current location';
console.log('adding);
localDispatch({
type: 'addMarker',
payload: {
marker: e.latlng
}
});
}
if (markers.length === 0) {
console.log('updating ');
e.latlng.alt = 'current location';
localDispatch({
type: 'updateMarkers',
payload: {
marker: e.latlng
}
});
}
self.control.spliceWaypoints(0, 1, e.latlng);
map.closePopup();
});
L.DomEvent.on(
destBtn,
'click',
function() {
console.log('e', e);
if (markers[1] === undefined) {
e.latlng.alt = 'current destination';
console.log('e.latlng ', e.latlng);
localDispatch({
type: 'addMarker',
payload: {
marker: e.latlng
}
});
}
if (toProps.markers[1] !== undefined) {
console.log('updating ');
e.latlng.alt = 'current destination';
localDispatch({
type: 'updateMarkers',
payload: {
marker: e.latlng
}
});
}
this.control.spliceWaypoints(1, 1, e.latlng);
map.closePopup();
}.bind(this)
);
}.bind(this)
);
if (toProps.removeRoutingMachine !== false) {
this.control.setWaypoints([]);
}
}
componentWillUnmount() {
this.destroyRouting();
}
destroyRouting() {
const { map } = this.props.leaflet;
if (map) {
map.removeControl(this.control);
}
}
}
export default withLeaflet(Routing);
Thanks in advance!
As you can see I have some code related to the Map (the onClick for the waypoints) in the RoutingMachine itself; after thinking about it I moved it to the Map component as a handlerFunction. And now it works!
import React, { useState, useEffect, useRef } from 'react';
import { Button, Dimmer, Loader } from 'semantic-ui-react';
import L from 'leaflet';
import * as ELG from 'esri-leaflet-geocoder';
import Control from 'react-leaflet-control';
// import MapboxLayer from '../MapboxLayer/MapboxLayer.jsx';
import Routing from '../RoutingMachine/RoutingMachine.jsx';
import { parse, stringify } from 'flatted';
import { userState, userDispatch } from '../Context/UserContext.jsx';
import UIContext from '../Context/UIContext.jsx';
function currentMapViewPropsAreEqual(prevProps, nextProps) {
console.log('prevProps, nextProps ', prevProps, nextProps);
console.log(
'prevProps.currentMapView === nextProps.currentMapView && prevProps.Map === nextProps.Map && prevProps.TileLayer === nextProps.TileLayer ',
prevProps.currentMapView === nextProps.currentMapView &&
prevProps.Map === nextProps.Map &&
prevProps.TileLayer === nextProps.TileLayer
);
return (
prevProps.currentMapView === nextProps.currentMapView &&
prevProps.Map === nextProps.Map &&
prevProps.TileLayer === nextProps.TileLayer
);
}
function MyMap({ currentMapView, Map, TileLayer }) {
console.log('currentMapView; ', currentMapView);
var [animate, setAnimate] = useState(false);
var [userLocation, setUserLocation] = useState(null);
const [myState, setMyState] = useState(null);
var handleWaypointsOnMapRef = useRef(handleWaypointsOnMap);
var mapRef = useRef();
var mapRefForRoutingMachine = useRef();
var { state } = userState();
var { dispatch } = userDispatch();
var {
currentMap,
isRoutingVisible,
removeRoutingMachine,
isLengthOfMarkersLessThanTwo,
markers
} = state;
useEffect(() => {
handleWaypointsOnMapRef.current = handleWaypointsOnMap;
}); // update after each render
useEffect(() => {
var { current = {} } = mapRef;
var { leafletElement: map } = current;
console.log('foo');
console.log('currentMap ', currentMapView);
map.locate({ setView: true });
map.on('locationfound', handleOnLocationFound);
}, []);
useEffect(() => {
var searchControl = new ELG.Geosearch({
useMapBounds: false
});
var { current = {} } = mapRef;
var { leafletElement: map } = current;
console.log('mapRef ', mapRef);
searchControl.addTo(map);
var cb = e => handleWaypointsOnMapRef.current(e); // then use most recent cb value
searchControl.on('results', cb);
if (Object.keys(currentMap).length === 0) {
dispatch({
type: 'setMap',
payload: {
currentMap: stringify(map)
}
});
}
return () => {
searchControl.off('results', cb);
};
}, []);
function handleOnClickClearOneMarkerAtTime() {
dispatch({
type: 'setIsRoutingVisible',
payload: {
isRoutingVisible: false
}
});
mapRefForRoutingMachine.current.handleRemoveWayPoint();
dispatch({
type: 'deleteUserMarkers'
});
}
function handleOnClickClearAllMarkers() {
mapRefForRoutingMachine.current.handleClearWayPoints();
dispatch({
type: 'resetUserMarkers'
});
}
function handleOnClickMarkerClick(e) {
e.originalEvent.view.L.DomEvent.stopPropagation(e);
}
function handleWaypointsOnMap(e) {
var { current = {} } = mapRef;
var { leafletElement: map } = current;
dispatch({
type: 'setIsRoutingVisible',
payload: {
isRoutingVisible: true
}
});
dispatch({
type: 'setRemoveRoutingMachine',
payload: {
removeRoutingMachine: false
}
});
function createButton(label, container) {
var btn = L.DomUtil.create('button', '', container);
btn.setAttribute('type', 'button');
btn.innerHTML = label;
return btn;
}
var container = L.DomUtil.create('div'),
startBtn = createButton('Start from this location', container),
destBtn = createButton('Go to this location', container);
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn, 'click', function() {
if (markers.length === 0) {
e.latlng.alt = 'current location';
console.log('adding current location', e.latlng);
dispatch({
type: 'addMarker',
payload: {
marker: e.latlng
}
});
}
if (markers[0] != undefined) {
e.latlng.alt = 'current location';
console.log('updating current location', e.latlng);
dispatch({
type: 'updateMarkers',
payload: {
marker: e.latlng
}
});
}
mapRefForRoutingMachine.current.handleSpliceWaypoints(0, 1, e.latlng);
map.closePopup();
});
L.DomEvent.on(
destBtn,
'click',
function() {
console.log('e', e);
if (markers.length === 1) {
e.latlng.alt = 'current destination';
console.log('adding destination ', e.latlng);
dispatch({
type: 'addMarker',
payload: {
marker: e.latlng
}
});
}
if (markers.length === 2 && markers[1] !== undefined) {
e.latlng.alt = 'current destination';
console.log('updating destination', e.latlng);
dispatch({
type: 'updateMarkers',
payload: {
marker: e.latlng
}
});
}
mapRefForRoutingMachine.current.handleSpliceWaypoints(1, 1, e.latlng);
map.closePopup();
}.bind(this)
);
}
function handleOnViewportChanged(e) {
console.log('viewport change', e);
console.log('currentMapView ', currentMapView);
var { current = {} } = mapRef;
var { leafletElement: map } = current;
map.on('zoomend', function() {
var zoom = map.getZoom();
console.log('zoom ', zoom);
console.log("'dispatch setMapZoom'; ");
dispatch({
type: 'setMapZoom',
payload: {
currentMapView: zoom
}
});
});
}
function handleOnLocationFound(e) {
console.log('e ', e);
var { current = {} } = mapRef;
var { leafletElement: map } = current;
map.setZoom(currentMapView);
var latlng = e.latlng;
var radius = e.accuracy;
var circle = L.circle(latlng, radius);
circle.addTo(map);
}
return (
<Map
preferCanvas={true}
id="myMap"
animate={animate}
zoom={currentMapView}
ref={mapRef}
onViewportChanged={handleOnViewportChanged}
onClick={e => handleWaypointsOnMap(e)}
>
<TileLayer
url={`https://api.mapbox.com/styles/v1/${process.env.MAPBOX_USERNAME}/${
process.env.MAPBOX_STYLE_ID
}/tiles/256/{z}/{x}/{y}#2x?access_token=${process.env.MAPBOX_ACCESS_TOKEN}`}
attribution='Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox'
/>
<Control position="bottomleft">
<Button onClick={handleOnClickClearOneMarkerAtTime} color="orange" size="small">
delete one marker!
</Button>
</Control>
<Control position="bottomright">
<Button onClick={handleOnClickClearAllMarkers} color="red" size="small">
clear all!
</Button>
</Control>
{mapRef && (
<Routing
isRoutingVisible={isRoutingVisible}
ref={mapRefForRoutingMachine}
markers={markers}
stringify={stringify}
isLengthOfMarkersLessThanTwo={isLengthOfMarkersLessThanTwo}
removeRoutingMachine={removeRoutingMachine}
userLocation={userLocation}
/>
)}
</Map>
);
}
var MemoizedMyMap = React.memo(MyMap, currentMapViewPropsAreEqual);
export default MemoizedMyMap;
And this is the Routing Machine:
import { MapLayer } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet-routing-machine';
import { withLeaflet } from 'react-leaflet';
import UserContextDispatch from '../Context/UserContext.jsx';
import { Dimmer, Loader } from 'semantic-ui-react';
import { isEqual } from 'lodash';
import AwesomeDebouncePromise from 'awesome-debounce-promise';
class Routing extends MapLayer {
static contextType = UserContextDispatch;
constructor(props) {
super(props);
this.state = {
showSpinner: false,
localDispatch: null,
markerIsBeingDragged: false,
currentMarker: {}
};
this.handleLoader = this.handleLoader.bind(this);
this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
this.handleClearWayPoints = this.handleClearWayPoints.bind(this);
this.handleSpliceWaypoints = this.handleSpliceWaypoints.bind(this);
this.handleSetMarker = this.handleSetMarker.bind(this);
}
handleSetMarker(marker) {
var { markers } = this.props;
if (markers[0] !== undefined && markers[0].alt === 'current location') {
this.setState(prevState => ({
currentMarker: { ...prevState.currentMarker, ...marker }
}));
}
if (markers[1] !== undefined && markers[1].alt === 'current destination') {
this.setState(prevState => ({
currentMarker: { ...prevState.currentMarker, ...marker }
}));
}
console.log('this.state ', this.state);
}
handleSpliceWaypoints(start, end, obj) {
this.control.spliceWaypoints(start, end, obj);
}
handleLoader() {
var { showSpinner } = this.state;
if (this.state.showSpinner === false) {
this.setState(function(prevState) {
return { showSpinner: !prevState.showSpinner };
});
return (
<Dimmer active inverted>
<Loader />
</Dimmer>
);
}
this.setState(function(prevState) {
return { showSpinner: (prevState.showSpinner = true) };
});
}
handleRemoveWayPoint() {
var waypoints = this.control.getWaypoints();
for (let i = waypoints.length - 1; i >= 0; i--) {
console.log('waypoints[i].latLng !== null ', waypoints[i].latLng !== null);
if (waypoints[i].latLng !== null) {
waypoints[i].latLng = null;
break;
}
}
console.log('waypoints ', waypoints);
this.control.setWaypoints(waypoints);
}
handleClearWayPoints() {
this.control.setWaypoints([L.latLng(null, null), L.latLng(null, null)]);
}
componentDidMount() {
const { map } = this.props.leaflet;
var { markers } = this.props;
this.control.setWaypoints([L.latLng(markers[0]), L.latLng(markers[1])]);
this.setState(prevState => {
localDispatch: prevState.localDispatch = this.context.dispatch;
});
map.addControl(this.control);
}
createLeafletElement(props) {
const { map } = this.props.leaflet;
var startIcon = new L.Icon({
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
shadowUrl:
'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
var endIcon = new L.Icon({
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl:
'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
if (map && !this.control) {
var RoutingMachineRef = this;
this.control = L.Routing.control({
collapsible: true,
show: false,
position: 'bottomleft',
lineOptions: {
styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
},
waypoints: [null],
createMarker: function(i, wp, nWps) {
if (i === 0) {
return L.marker(wp.latLng, {
icon: startIcon,
draggable: true,
keyboard: true,
alt: 'current location'
}).on('move', function(e) {
e.target._latlng.alt = 'current location';
console.log('e.target._latlng', e.target._latlng);
console.log('there be dragons start!!', e);
RoutingMachineRef.setState(prevState => ({
markerIsBeingDragged: !prevState.markerIsBeingDragged
}));
RoutingMachineRef.handleSetMarker(e.target._latlng);
});
}
if (i === nWps - 1) {
return L.marker(wp.latLng, {
icon: endIcon,
draggable: true,
alt: 'current destination'
}).on('move', function(e) {
e.target._latlng.alt = 'current destination';
console.log(' e.target._latlng', e.target._latlng);
RoutingMachineRef.setState(prevState => ({
markerIsBeingDragged: !prevState.markerIsBeingDragged
}));
console.log('there be dragons dest!!', e);
RoutingMachineRef.handleSetMarker(e.target._latlng);
});
}
}
});
L.Routing.errorControl(this.control).addTo(map);
}
return this.control.getPlan();
}
updateLeafletElement(fromProps, toProps) {
var { currentMarker, localDispatch, markerIsBeingDragged } = this.state;
// console.log('fromProps, toProps ', fromProps, toProps);
if (markerIsBeingDragged && currentMarker.hasOwnProperty('alt')) {
if (isEqual(toProps.markers[0], currentMarker) === false) {
localDispatch({
type: 'updateMarkers',
payload: {
marker: currentMarker
}
});
}
if (isEqual(toProps.markers[1], currentMarker) === false) {
localDispatch({
type: 'updateMarkers',
payload: {
marker: currentMarker
}
});
}
this.setState(prevState => ({
markerIsBeingDragged: !prevState.markerIsBeingDragged
}));
}
if (toProps.removeRoutingMachine !== false) {
this.control.setWaypoints([]);
}
}
componentWillUnmount() {
console.log("'unmount' ", 'unmount');
this.destroyRouting();
}
destroyRouting() {
const { map } = this.props.leaflet;
if (map) {
map.removeControl(this.control);
}
}
}
export default withLeaflet(Routing);
I am very new to write test cases, i tried to write test cases for my dashboard page and i am getting Cannot read property 'clientHeight' of undefined error, please see my all dashboard.js page below & my test cases file. please help me on this
getting Error in this line:
var t = window.innerHeight - document.getElementsByClassName('nep-header')[0].clientHeight - 20 + "p
My Dashboard.js page:
import { CompanyDashboard, DeleteCompanyDashboard } from "../../APICall/CompanyDashboard";
import React from "react";import { GetimageUrl } from "../../Common/UtilityFunctions";
import { Table, Modal, Button, Message } from "#maknowledgeservices/neptune";
import './Dashboard.css';
import { dateFormatConversionForSorting } from '../../Common/UtilityFunctions';
import { setTimeout } from "timers";
import Loader from '../../Components/Loader/Loader';
var successMessage = false;
var errorMessage = false;
var showing = true;
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.showing = true;
setTimeout(() => {
var divsToHide = document.getElementsByClassName("nep-table-empty"); //divsToHide is an array
for (var i = 0; i < divsToHide.length; i++) {
divsToHide[i].style.visibility = "hidden"; // or
divsToHide[i].style.display = "none"; // depending on what you're doing
}
}, 10);
this.state = {
visible: false,
DeleteCompStr: "",
DeleteCompID: 0,
columnDefs: [
{
title: "Company", render: "Companyname", fixed: "left", width: 320, sorter: order => (a, b) => {
if (order === 'asc') return a.Companyname.localeCompare(b.Companyname)
return b.Companyname.localeCompare(a.Companyname)
}
},
{
title: 'Year End', width: 95, render: d => this.renderMethod(d.financeYearEndMonth + " " + d.financeYearEnd), sorter: order => (a, b) => {
if (order === 'asc') return dateFormatConversionForSorting(a.financeYearEndMonth + " " + a.financeYearEnd).localeCompare(dateFormatConversionForSorting(b.financeYearEndMonth + " " + b.financeYearEnd))
return dateFormatConversionForSorting(b.financeYearEndMonth + " " + b.financeYearEnd).localeCompare(dateFormatConversionForSorting(a.financeYearEndMonth + " " + a.financeYearEnd))
}
},
{
title: 'LTM Financials', width: 95, render: d => this.renderMethod(d.ltmMonth + " " + d.ltmYear), sorter: order => (a, b) => {
if (order === 'asc') return dateFormatConversionForSorting(a.ltmMonth + " " + a.ltmYear).localeCompare(dateFormatConversionForSorting(b.ltmMonth + " " + b.ltmYear))
return dateFormatConversionForSorting(b.ltmMonth + " " + b.ltmYear).localeCompare(dateFormatConversionForSorting(a.ltmMonth + " " + a.ltmYear))
}
},
{
title: "AccuRate", width: 95, render: s => this.StatusIndicator(s.accurate),
},
{
title: "Financial Trends", width: 95, render: s => this.StatusIndicator(s.finTrend),
},
{
title: "Newsflow Trends", width: 115,
render: (s) => (
<div style={{ cursor: "default" }}>
{this.StatusIndicator(s.newsflow)}
<a className="tooltip" style={{ float: "right" }}
onClick={this.show.bind(this, s)}
><i style={{ cursor: "pointer" }} className="icon-Delete" name="delete"
onMouseOver={({ target }) => target.style.color = '#021155'}
onMouseOut={({ target }) => target.style.color = '#75787B'} /></a>
</div>
),
}
],
companyCount: '',
rowData: [],
res: ""
}
this.show = this.show.bind(this)
}
show(selRowVal) {
this.setState({
visible: true,
})
this.DeleteCompID = selRowVal.id;
this.DeleteCompStr = selRowVal.Companyname;
}
handleOk = () => {
DeleteCompanyDashboard("DeleteCompany/" + this.DeleteCompID).then(responce => {
if (responce.data === true) {
if (successMessage === false) {
successMessage = true;
Message.success(this.DeleteCompStr + ' Company has been deleted successfully', 7, {
onClose: () => {
successMessage = false;
}
});
}
}
else {
if (errorMessage === false) {
errorMessage = true;
Message.error('Server error', 7, {
onClose: () => {
errorMessage = false;
}
});
}
}
this.componentDidMount();
this.setState({
visible: false,
});
});
}
handleCancel = () => {
this.setState({
visible: false,
})
}
renderMethod(params) {
return params.substring(3);
}
handleRemove(selectedValue) {
this.show();
}
StatusIndicator(params) {
switch (params) {
case 'Positive':
return <span style={{ color: '#388E3C' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
case 'Negative':
return <span style={{ color: '#C62828' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
case 'Stable':
return <span style={{ color: '#C68700' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
case 'Neutral':
return <span style={{ color: '#C68700' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
default:
return <span style={{ color: '#55565A' }}>{params}</span>
break;
}
}
componentDidMount() {
CompanyDashboard("GetCompany").then(responce => {
this.showing = false;
this.setState({
rowData: responce.data,
companyCount: responce.data.length === 0 || undefined ? 0 : responce.data.length
});
});
this.tableHeight();
}
componentDidUpdate(prevProps, prevState) {
if (prevState.companyCount !== this.state.companyCount) {
CompanyDashboard("GetCompany").then(responce => {
this.setState({ rowData: responce.data, companyCount: responce.data.length === 0 || undefined ? 0 : responce.data.length });
});
}
this.tableHeight();
}
tableHeight() {
var t = window.innerHeight - document.getElementsByClassName('nep-header')[0].clientHeight - 20 + "px"
var ele = document.getElementById("pr");
ele.style.height = t.toString();
}
rowClicked(e) {
setTimeout(() => {
let selectedCompany = this.state.rowData.filter(x => x.cik == e.cik);
selectedCompany = selectedCompany.map(function (obj) {
let val = obj.Companyname;
delete obj['Companyname']
obj.companyname = val;
return obj
}
);
sessionStorage.setItem("selectedCompany", JSON.stringify(selectedCompany));
const { from } = {
from: { pathname: "/company" }
};
this.props.history.push(from);
}, 300)
}
handleClick = (value = this.state.value) => {
value += Math.random() * 12
if (value >= 100) {
value = 100
this.setState({ value })
} else {
this.setState({ value }, () => {
setTimeout(this.handleClick, 320)
})
}
}
render() {
return (
<div id="pr" className="tableSpace">
<label>Companies <span className="tableSpan">({this.state.companyCount})</span></label>
<div style={{ display: (this.showing ? 'block' : 'none') }} >
<Loader />
</div>
<Table
keygen="cik"
striped
bordered
fixed="both"
className="tableClass"
bordered fixed="both"
width={1024}
columns={this.state.columnDefs}
data={this.state.rowData}
onRowClick={this.rowClicked.bind(this)}
/>
<Modal
visible={this.state.visible}
width={500}
title=" Delete Company"
onClose={this.handleCancel}
maskCloseAble={false}
footer={[
<Button key="cancel" className="nep-button nep-button-secondary" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="ok" type="primary" onClick={this.handleOk}>
Delete
</Button>,
]} >
The company <b>{this.DeleteCompStr}</b> will be permanently deleted and this action cannot be undone. <br /> Are you sure you want to proceed ?
</Modal>
</div>
);}}export default Dashboard;`
My dashbord test cases file code:
import React from 'react'
import { render, cleanup } from '#testing-library/react';
import Dashboard from "../Components/Dashboard/Dashboard"
import axios from "axios";
import { shallow } from 'enzyme'
import { Table } from "#maknowledgeservices/neptune";
afterEach(cleanup);
jest.mock('axios');
it("should render initial layout", () => {
const component = shallow(<Dashboard />);
expect(component.getElements()).toMatchSnapshot();
});
it("test to render Dashboard component", async () => {
axios.post.mockResolvedValue({ data: [{ Companyname: "xyz", accurate: "Positive", }], status: 200, statusText: "OK" });
await render(<Dashboard />, Table);
var result = render(<Dashboard />);
expect(result).toBeTruthy();
});
I have a bit of a weird error with react and my google charts, when i first login to my page that shows my chart everything shows fine, but theres a place where i import data for new values to show on my chart and the chart disappears when i import new values and i get the following errors:
Uncaught TypeError: processedData[(index + 1)] is undefined
Uncaught Error: A cross-origin error was thrown. React doesn't have access to the actual error object in development
I have added null checks and everything but the error still persists.
Here is my code where the error is happening:
import React from 'react';
import { Chart } from "react-google-charts";
export const DoubleColumnChart = (props) => {
const processData = (data) => {
if (data == null) return [[]];
if (data.labels == null) return [[]];
var processedData = [[]];
if(processData == null) return [[]];
processedData[0].push('Category');
data.labels.forEach(function (label) {
var finalLabel = null;
finalLabel = label[0];
if (label.length > 1) {
for (var i = 1; i < label.length; i++) {
if (finalLabel.length > parseInt(160 / data.labels.length, 10)) {
finalLabel = finalLabel + '...';
break;
}
finalLabel = finalLabel + '\n' + label[i];
}
}
processedData.push([finalLabel]);
});
data.datasets.forEach(function (dataset) {
processedData[0].push(dataset.label);
dataset.data.forEach(function (data, index) {
if(processData == null) return [[]];
processedData[index + 1].push(data);
});
});
return processedData;
}
const processColors = (data) => {
if (data == null) return [];
if (data.datasets == null) return [[]];
var processedColors = [];
data.datasets.forEach(function (dataset) {
processedColors.push(dataset.backgroundColor);
});
return processedColors
}
if (props.isVisible == false) {
return <div></div>;
}
return (
<Chart
width={'99%'}
height={'375px'}
chartType="ColumnChart"
loader={<div>Loading Chart</div>}
data={processData(props.data)}
options={{
animation: {
duration: 1500,
easing: 'out',
startup: true,
},
legend: { position: 'bottom', textStyle: { color: 'gray' } },
vAxis: { textStyle: { color: 'gray' } },
hAxis: { textStyle: { fontSize: 10, color: 'gray' } },
tooltip: { trigger: 'hover', showColorCode: true },
chartArea: {
top: '2%',
left: '5%',
height: "77%",
width: "100%",
},
colors: processColors(props.data),
dataOpacity: '0.9',
}}
chartEvents={[
{
eventName: 'select',
callback: ({ chartWrapper }) => {
const chart = chartWrapper.getChart()
const selection = chart.getSelection()
if (selection.length === 1) {
const [selectedItem] = selection;
const { row } = selectedItem;
var labelData = props.data.labels[row];
var finalLabel = '';
for (var i = 0; i < labelData.length; i++) {
finalLabel = finalLabel + labelData[i];
}
finalLabel = finalLabel.replace(/ /g, '');
if (props.onSegmentClick) props.onSegmentClick(finalLabel);
}
},
},
]}
rootProps={{ 'data-testid': '1' }}
/>
);
}
But if i hard refresh, the new data does show and the console error goes away
i am trying to implement video streaming using react-native
i am using
webrtc package(https://github.com/oney/react-native-webrtc)
socket-io-client
and
oney/RCTWebRTCDemo(https://github.com/oney/RCTWebRTCDemo)
when debug js remotely is enabled then live streaming works absolutely as expected but when remote debugging is disabled it stucks at connecting i feel like some point it stops working.
i m trying to include this demo into my existing app.
my code -live.js
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TouchableHighlight,
View,
TextInput,
ListView,
Platform,
} from 'react-native';
import io from 'socket.io-client';
const socket = io.connect('https://react-native-webrtc.herokuapp.com', {transports: ['websocket']});
import {
RTCPeerConnection,
RTCMediaStream,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStreamTrack,
getUserMedia,
} from 'react-native-webrtc';
React.createClass=require('create-react-class');
const configuration = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]};
const pcPeers = {};
let localStream;
function getLocalStream(isFront, callback) {
let videoSourceId;
// on android, you don't have to specify sourceId manually, just use facingMode
// uncomment it if you want to specify
if (Platform.OS === 'ios') {
MediaStreamTrack.getSources(sourceInfos => {
console.log("sourceInfos: ", sourceInfos);
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if(sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
});
}
getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 640, // Provide your own width, height and frame rate here
minHeight: 360,
minFrameRate: 30,
},
facingMode: (isFront ? "user" : "environment"),
optional: (videoSourceId ? [{sourceId: videoSourceId}] : []),
}
}, function (stream) {
console.log('getUserMedia success', stream);
callback(stream);
}, logError);
}
function join(roomID) {
socket.emit('join', roomID, function(socketIds){
console.log('join', socketIds);
for (const i in socketIds) {
const socketId = socketIds[i];
createPC(socketId, true);
}
});
}
function createPC(socketId, isOffer) {
const pc = new RTCPeerConnection(configuration);
pcPeers[socketId] = pc;
pc.onicecandidate = function (event) {
console.log('onicecandidate', event.candidate);
if (event.candidate) {
socket.emit('exchange', {'to': socketId, 'candidate': event.candidate });
}
};
function createOffer() {
pc.createOffer(function(desc) {
console.log('createOffer', desc);
pc.setLocalDescription(desc, function () {
console.log('setLocalDescription', pc.localDescription);
socket.emit('exchange', {'to': socketId, 'sdp': pc.localDescription });
}, logError);
}, logError);
}
pc.onnegotiationneeded = function () {
console.log('onnegotiationneeded');
if (isOffer) {
createOffer();
}
}
pc.oniceconnectionstatechange = function(event) {
console.log('oniceconnectionstatechange', event.target.iceConnectionState);
if (event.target.iceConnectionState === 'completed') {
setTimeout(() => {
getStats();
}, 1000);
}
if (event.target.iceConnectionState === 'connected') {
createDataChannel();
}
};
pc.onsignalingstatechange = function(event) {
console.log('onsignalingstatechange', event.target.signalingState);
};
pc.onaddstream = function (event) {
console.log('onaddstream', event.stream);
container.setState({info: 'One peer join!'});
const remoteList = container.state.remoteList;
remoteList[socketId] = event.stream.toURL();
container.setState({ remoteList: remoteList });
};
pc.onremovestream = function (event) {
console.log('onremovestream', event.stream);
};
pc.addStream(localStream);
function createDataChannel() {
if (pc.textDataChannel) {
return;
}
const dataChannel = pc.createDataChannel("text");
dataChannel.onerror = function (error) {
console.log("dataChannel.onerror", error);
};
dataChannel.onmessage = function (event) {
console.log("dataChannel.onmessage:", event.data);
container.receiveTextData({user: socketId, message: event.data});
};
dataChannel.onopen = function () {
console.log('dataChannel.onopen');
container.setState({textRoomConnected: true});
};
dataChannel.onclose = function () {
console.log("dataChannel.onclose");
};
pc.textDataChannel = dataChannel;
}
return pc;
}
function exchange(data) {
const fromId = data.from;
let pc;
if (fromId in pcPeers) {
pc = pcPeers[fromId];
} else {
pc = createPC(fromId, false);
}
if (data.sdp) {
console.log('exchange sdp', data);
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
if (pc.remoteDescription.type == "offer")
pc.createAnswer(function(desc) {
console.log('createAnswer', desc);
pc.setLocalDescription(desc, function () {
console.log('setLocalDescription', pc.localDescription);
socket.emit('exchange', {'to': fromId, 'sdp': pc.localDescription });
}, logError);
}, logError);
}, logError);
} else {
console.log('exchange candidate', data);
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
}
}
function leave(socketId) {
console.log('leave', socketId);
const pc = pcPeers[socketId];
const viewIndex = pc.viewIndex;
pc.close();
delete pcPeers[socketId];
const remoteList = container.state.remoteList;
delete remoteList[socketId]
container.setState({ remoteList: remoteList });
container.setState({info: 'One peer leave!'});
}
socket.on('exchange', function(data){
exchange(data);
});
socket.on('leave', function(socketId){
leave(socketId);
});
socket.on('connect', function(data) {
console.log('connect');
getLocalStream(true, function(stream) {
localStream = stream;
container.setState({selfViewSrc: stream.toURL()});
container.setState({status: 'ready', info: 'Please enter or create room ID'});
});
});
function logError(error) {
console.log("logError", error);
}
function mapHash(hash, func) {
const array = [];
for (const key in hash) {
const obj = hash[key];
array.push(func(obj, key));
}
return array;
}
function getStats() {
const pc = pcPeers[Object.keys(pcPeers)[0]];
if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) {
const track = pc.getRemoteStreams()[0].getAudioTracks()[0];
console.log('track', track);
pc.getStats(track, function(report) {
console.log('getStats report', report);
}, logError);
}
}
let container;
const RCTWebRTCDemo = React.createClass({
getInitialState: function() {
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => true});
return {
info: 'Initializing',
status: 'init',
roomID: '',
isFront: true,
selfViewSrc: null,
remoteList: {},
textRoomConnected: false,
textRoomData: [],
textRoomValue: '',
};
},
componentDidMount: function() {
container = this;
},
_press(event) {
this.refs.roomID.blur();
this.setState({status: 'connect', info: 'Connecting'});
join(this.state.roomID);
},
_switchVideoType() {
const isFront = !this.state.isFront;
this.setState({isFront});
getLocalStream(isFront, function(stream) {
if (localStream) {
for (const id in pcPeers) {
const pc = pcPeers[id];
pc && pc.removeStream(localStream);
}
localStream.release();
}
localStream = stream;
container.setState({selfViewSrc: stream.toURL()});
for (const id in pcPeers) {
const pc = pcPeers[id];
pc && pc.addStream(localStream);
}
});
},
receiveTextData(data) {
const textRoomData = this.state.textRoomData.slice();
textRoomData.push(data);
this.setState({textRoomData, textRoomValue: ''});
},
_textRoomPress() {
if (!this.state.textRoomValue) {
return
}
const textRoomData = this.state.textRoomData.slice();
textRoomData.push({user: 'Me', message: this.state.textRoomValue});
for (const key in pcPeers) {
const pc = pcPeers[key];
pc.textDataChannel.send(this.state.textRoomValue);
}
this.setState({textRoomData, textRoomValue: ''});
},
_renderTextRoom() {
return (
<View style={styles.listViewContainer}>
<ListView
dataSource={this.ds.cloneWithRows(this.state.textRoomData)}
renderRow={rowData => <Text>{`${rowData.user}: ${rowData.message}`}</Text>}
/>
<TextInput
style={{width: 200, height: 30, borderColor: 'gray', borderWidth: 1}}
onChangeText={value => this.setState({textRoomValue: value})}
value={this.state.textRoomValue}
/>
<TouchableHighlight
onPress={this._textRoomPress}>
<Text>Send</Text>
</TouchableHighlight>
</View>
);
},
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
{this.state.info}
</Text>
{this.state.textRoomConnected && this._renderTextRoom()}
<View style={{flexDirection: 'row'}}>
<Text>
{this.state.isFront ? "Use front camera" : "Use back camera"}
</Text>
<TouchableHighlight
style={{borderWidth: 1, borderColor: 'black'}}
onPress={this._switchVideoType}>
<Text>Switch camera</Text>
</TouchableHighlight>
</View>
{ this.state.status == 'ready' ?
(<View>
<TextInput
ref='roomID'
autoCorrect={false}
style={{width: 200, height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({roomID: text})}
value={this.state.roomID}
/>
<TouchableHighlight
onPress={this._press}>
<Text>Enter room</Text>
</TouchableHighlight>
</View>) : null
}
<RTCView streamURL={this.state.selfViewSrc} style={styles.selfView}/>
{
mapHash(this.state.remoteList, function(remote, index) {
return <RTCView key={index} streamURL={remote} style={styles.remoteView}/>
})
}
</View>
);
}
});
const styles = StyleSheet.create({
selfView: {
width: 200,
height: 150,
},
remoteView: {
width: 200,
height: 150,
},
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
listViewContainer: {
height: 150,
},
});
export default RCTWebRTCDemo;
The repo has code that is deprecated from WebRTC. You can check the webrtc api from developer.mozilla.org and review the react-native-webrtc library to see the new api.
Here is the working code:
! Mind that in const url = 'http...'; you must enter the server side link
import React, { Component } from "react";
import { Text, TouchableHighlight, View, YellowBox } from "react-native";
import { getUserMedia, RTCIceCandidate, RTCPeerConnection, RTCSessionDescription, RTCView } from "react-native-webrtc";
import io from "socket.io-client";
import s from './style';
YellowBox.ignoreWarnings(['Setting a timer', 'Unrecognized WebSocket connection', 'ListView is deprecated and will be removed']);
const url = 'https://ac07cd91.ngrok.io';
const socket = io.connect(url, { transports: ["websocket"] });
const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
let pcPeers = {};
let container;
let localStream;
const initStream = () => {
let videoSourceId;
let isFront = true;
let constrains = {
audio: false,
video: {
mandatory: {
minWidth: 640,
minHeight: 360,
minFrameRate: 30,
},
facingMode: isFront ? "user" : "environment",
optional: videoSourceId ? [{ sourceId: videoSourceId }] : [],
},
};
let callback = stream => {
localStream = stream;
container.setState({
localStream: stream.toURL(),
status: "ready",
info: "Welcome to WebRTC demo",
});
};
getUserMedia(constrains, callback, logError);
};
const join = roomID => {
let state = 'join';
let callback = socketIds => {
for (const i in socketIds) {
if (socketIds.hasOwnProperty(i)) {
const socketId = socketIds[i];
createPC(socketId, true);
}
}
};
socket.emit(state, roomID, callback);
};
const createPC = (socketId, isOffer) => {
const peer = new RTCPeerConnection(configuration);
pcPeers = {
...pcPeers,
[socketId]: peer,
};
peer.addStream(localStream);
peer.onicecandidate = event => {
//console.log("onicecandidate", event.candidate);
if (event.candidate) {
socket.emit("exchange", { to: socketId, candidate: event.candidate });
}
};
peer.onnegotiationneeded = () => {
//console.log("onnegotiationneeded");
if (isOffer) {
createOffer();
}
};
peer.oniceconnectionstatechange = event => {
//console.log("oniceconnectionstatechange", event.target.iceConnectionState);
if (event.target.iceConnectionState === "completed") {
console.log('event.target.iceConnectionState === "completed"');
setTimeout(() => {
getStats();
}, 1000);
}
if (event.target.iceConnectionState === "connected") {
console.log('event.target.iceConnectionState === "connected"');
}
};
peer.onsignalingstatechange = event => {
console.log("on signaling state change", event.target.signalingState);
};
peer.onaddstream = event => {
//console.log("onaddstream", event.stream);
const remoteList = container.state.remoteList;
remoteList[socketId] = event.stream.toURL();
container.setState({
info: "One peer join!",
remoteList: remoteList,
});
};
peer.onremovestream = event => {
console.log("on remove stream", event.stream);
};
const createOffer = () => {
let callback = desc => {
//console.log("createOffer", desc);
peer.setLocalDescription(desc, callback2, logError);
};
let callback2 = () => {
//console.log("setLocalDescription", peer.localDescription);
socket.emit("exchange", { to: socketId, sdp: peer.localDescription });
};
peer.createOffer(callback, logError);
};
return peer;
};
socket.on("connect", () => {
console.log("connect");
});
socket.on("leave", socketId => {
leave(socketId);
});
socket.on("exchange", data => {
exchange(data);
});
const leave = socketId => {
console.log("leave", socketId);
const peer = pcPeers[socketId];
peer.close();
delete pcPeers[socketId];
const remoteList = container.state.remoteList;
delete remoteList[socketId];
container.setState({
info: "One peer leave!",
remoteList: remoteList,
});
};
const exchange = data => {
const fromId = data.from;
let pc;
if (fromId in pcPeers) {
pc = pcPeers[fromId];
} else {
pc = createPC(fromId, false);
}
if (data.sdp) {
//console.log("exchange sdp", data);
let sdp = new RTCSessionDescription(data.sdp);
let callback = () => pc.remoteDescription.type === "offer" ? pc.createAnswer(callback2, logError) : null;
let callback2 = desc => pc.setLocalDescription(desc, callback3, logError);
let callback3 = () => socket.emit("exchange", { to: fromId, sdp: pc.localDescription });
pc.setRemoteDescription(sdp, callback, logError);
} else {
//console.log("exchange candidate", data);
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
}
};
const logError = error => {
console.log("logError", error);
};
const mapHash = (hash, func) => {
const array = [];
for (const key in hash) {
if (hash.hasOwnProperty(key)) {
const obj = hash[key];
array.push(func(obj, key));
}
}
return array;
};
const getStats = () => {
const pc = pcPeers[Object.keys(pcPeers)[0]];
if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) {
const track = pc.getRemoteStreams()[0].getAudioTracks()[0];
//console.log("track", track);
pc.getStats(
track,
function (report) {
//console.log("getStats report", report);
},
logError,
);
}
};
class App extends Component {
state = {
info: "Initializing",
status: "init",
roomID: "abc",
isFront: true,
localStream: null,
remoteList: {},
};
componentDidMount() {
container = this;
initStream();
}
_press = () => {
this.setState({
status: "connect",
info: "Connecting",
});
join(this.state.roomID);
};
button = () => (
<TouchableHighlight style={s.button} onPress={this._press}>
<Text style={s.buttonText}>Enter room</Text>
</TouchableHighlight>
);
render() {
const { status, info, localStream, remoteList } = this.state;
return (
<View style={s.container}>
<Text style={s.welcome}>{info}</Text>
{status === "ready" ? this.button() : null}
<RTCView streamURL={localStream} style={s.selfView}/>
{
mapHash(remoteList, (remote, index) => {
return (<RTCView key={index} streamURL={remote} style={s.remoteView}/>);
})
}
</View>
);
}
}
export default App;
import { StyleSheet } from "react-native";
const s = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
selfView: {
flex: 0.5,
justifyContent: 'center',
alignItems: 'center',
height: 150,
},
remoteView: {
flex: 0.5,
justifyContent: 'center',
alignItems: 'center',
height: 150,
},
welcome: {
fontSize: 20,
textAlign: "center",
margin: 10,
borderRadius: 10,
},
buttonText: {
textAlign: 'center',
borderWidth: 1,
borderColor: 'black',
width: '50%',
margin: 10,
padding: 10,
borderRadius: 10,
},
button: {
alignItems: 'center',
},
});
export default s;
Link to repo: RCTWebRTCDemo
Consider that it is only configured for Android, if you want both iOS/Android configured check RCTWebRTCDemo2.
I have a model named as selected_category in this model is a array named chosen_features and in this array is an array named as selected_conditions ,
So when I loop chosen_features to modify the selected_conditions in each chosen_feature model binding in select input not working! Something like this in code:
<div v-for="chosen_feature in selected_category.chosen_features">
<div>
<div class="col-md-3">
<div class="card card-default">
<div class="card-header"> #{{ chosen_feature.label }}
</div>
<div class="card-block">
<div class="col-md-9">
<select v-model="chosen_feature.selected_conditions" multiple="multiple">
<option v-for="condition in chosen_feature.conditions" :value="condition.value">#{{condition.label}}</option>
</select>
In this case model of chosen_feature.selected_conditions by v-model not updating! But in vue developer inspector for chrome it has been updated but in dom! not anything!
EDITED!
Js code too:
new Vue({
el : '#myapp',
data : {
type : 'none',
category : {
count : '{{ $categories->count() }}',
error : false,
selected : 0,
validation: false,
show : true,
},
event : {
count : '{{ $events->count() }}',
error : false,
selected : 0,
validation: false,
show : true,
},
error : false,
discount : false,
coordinates : [],
slides : [],
categories : {!! $categories->toJson() !!},
features : {!! $features->toJson() !!},
selected_features : [],
selected_categories: [],
},
mounted: function () {
this.features.map(function (value, key) {
Object.assign(value, {
color : 'dark',
selected_conditions: [],
})
});
this.categories.map(function (value, key) {
Object.assign(value, {
color : 'dark',
chosen_features: [],
})
});
},
updated: function () {
switch (this.type) {
case('category'):
this.event.error = false;
if (this.category.count < 1) {
this.category.error = true;
this.error = true;
} else {
this.error = false;
}
break;
case('event'):
this.category.error = false;
if (this.event.count < 1) {
this.event.error = true;
this.error = true;
}
else {
this.error = false;
}
break;
}
},
methods: {
addCoordinate: function () {
if (this.coordinates.length === 0) {
this.coordinates.push({
id: this.coordinates.length + 1,
})
} else {
this.coordinates.push({
id: this.coordinates[this.coordinates.length - 1].id + 1,
})
}
},
removeCoordinate: function (coordinate) {
this.coordinates.splice(this.coordinates.indexOf(coordinate), 1)
},
addSlide: function () {
if (this.slides.length === 0) {
this.slides.push({
id : this.slides.length + 1,
show_image: false,
})
} else {
this.slides.push({
id : this.slides[this.slides.length - 1].id + 1,
show_image: false,
})
}
},
removeSlide: function (slide) {
this.slides.splice(this.slides.indexOf(slide), 1)
},
readURL: function (slide) {
console.log(slide);
var input = $('#slide-' + slide.id)[0]
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
slide.show_image = true;
$('#slide-image-' + slide.id).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
},
addFeature: function (feature) {
data = {
id : feature.id,
label : feature.label,
title : feature.title,
description : feature.description,
conditions : feature.conditions,
selected_conditions: [],
}
if (this.selected_features.length > 0) {
if (search(data.id, this.selected_features) === undefined) {
this.selected_features.push(data)
Object.assign(feature, {
color: 'success',
})
} else {
Object.assign(search(feature.id, this.features), {
color: 'dark',
})
this.selected_features.splice(this.selected_features.indexOf(feature), 1)
}
} else {
this.selected_features.push(data)
Object.assign(feature, {
color: 'success',
})
}
},
removeFeature: function (feature) {
Object.assign(search(feature.id, this.features), {
color: 'dark',
})
this.selected_features.splice(this.selected_features.indexOf(feature), 1)
},
addCategory: function (category) {
data = {
id : category.id,
title : category.title,
description : category.description,
chosen_features: [],
}
if (this.selected_categories.length > 0) {
if (search(data.id, this.selected_categories) === undefined) {
this.selected_categories.push(data)
Object.assign(category, {
color: 'success',
})
} else {
Object.assign(search(category.id, this.categories), {
color: 'dark',
})
this.selected_categories.splice(this.selected_categories.indexOf(category), 1)
}
} else {
this.selected_categories.push(data)
Object.assign(category, {
color: 'success',
})
}
},
removeCategory: function (category) {
Object.assign(search(category.id, this.categories), {
color: 'dark',
})
this.selected_categories.splice(this.selected_categories.indexOf(category), 1)
},
addConditionToChosenFeature: function (condition, feature, category) {
search(feature.id, category.chosen_features).selected_conditions.push(condition)
console.log(search(feature.id, category.chosen_features).selected_conditions);
},
Discounting: function () {
console.log('Changed')
if ($('#discount-button').is(":checked")) {
this.discount = true
} else {
this.discount = false
}
},
Searching: function (nameKey, myArray) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i].id === parseInt(nameKey)) {
return myArray[i];
}
}
}
},
});