setState not changing UI data - reactjs

I have a react component which has componentWillMount and updates the state using setState method. I am updating the state variable rideTiles with an array of objects, though I am not getting ant data for rendering. I tried shouldComponentUpdate() by returning default true, still component doesn't render with updated values. I am not able to determine what is going wrong?
Here is my code
import React from 'react';
import {TouchableOpacity, Image, View, Text, StyleSheet, Dimensions, ScrollView} from 'react-native';
import Gradient from '../components/gradient';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import PRides from '../components/PRides'
import {GetRequest, GetUrl} from '../lib/net.js';
import Realm from '../lib/schemas.js';
import sqlite from '../lib/sqlite';
import config from '../../config.js';
import {DistanceAndTime} from '../lib/geoDistCal.js'
const AvroCodec = require('avroschema');
export default class Rides extends React.Component {
static navigationOptions = {
label: 'Rides',
tabBarIcon: () => (
<Image
source={require('../res/img/icn-ftrlnk2.png')}
style={[styles.img]}
/>
),
}
constructor(props){
super(props);
this.onRideData = this.onRideData.bind(this);
EE.addEventListener(EE.EVENT_TYPES.RIDE_DETAILS, this.onRideData);
this.eventCounts = 0;
this.insertedCount = 0;
this.rideTiles = [];
}
componentWillMount(){
var self = this;
GetRequest(GetUrl(config.glHost + "/gl/users/rides/findrides/?off=0&cnt=20&ist=false"), function(res){
if(res.status == 200){
var data = JSON.parse(res._response).data;
console.log("API data", data);
for(i = 0; i < data.length; i++){
Realm.write(() => {
Realm.create('Rides',{
idx: data[i].idx,
nm: data[i].nm,
sdt: data[i].sdt,
edt: data[i].edt,
flt: data[i].flt,
fln: data[i].fln,
tlt: data[i].tlt,
tln: data[i].tln,
uid: data[i].uid,
ist: data[i].ist,
isg: data[i].isg,
ec: data[i].ec,
// kms: data[i].kms,
// avs: data[i].avs,
// tps: data[i].tps
}, true);
self.insertLocationData(data[i]);
});
}
}
});
this.rideTiles = Array.from(Realm.objects('Rides'));
}
onRideData(event){
var self = this;
if(event){
var insertQuery = `insert into events (devId,rideId,ts,lat,lng,alt,spd,brg,hepe,vepe,ang,temp,motion,ignition,mainPower,relayState) values (
${event.devid}
,${event.rideid}
,${event.ts}
,${event.lat}
,${event.lng}
,${event.alt}
,${event.spd}
,${event.brg}
,${event.hepe}
,${event.vepe}
,${event.ang}
,${event.temp}
,${(event.motion ? 1 : 0)}
,${(event.ignition ? 1 : 0)}
,${(event.mainPower ? 1 : 0)}
,${(event.relayState ? 1 : 0)})`;
sqlite.runQuery(insertQuery, function(result){
console.log(result.rows);
if(result.rowsAffected === 1){
self.insertedCount += 1;
console.log("self.insertedCount, ", self.insertedCount, "self.eventCounts, ", self.eventCounts);
if(self.insertedCount === self.eventCounts){
//Completed Ride inserts, now compute tps avs and update in Realm
console.log("computing stats");
var tps, avs, kms, pingData = [];
sqlite.runQuery("select max(spd) as tps from events where rideId="+ event.rideid, function(result){
tps = result.rows.item(0).tps;
sqlite.runQuery(`select lat,lng,ts from events where rideId=${event.rideid} order by ts asc`, function(results){
var len = results.rows.length;
for (let i = 0; i < len; i++) {
pingData.push(results.rows.item(i));
}
console.log("pingData", pingData);
var stats = DistanceAndTime(pingData);
console.log("stats", stats);
kms = stats.D/1000;
avs = (5/18)*(stats.D/stats.T);
debugger
Realm.write(() => {
Realm.create('Rides', {idx:event.rideid, tps: Number(tps), kms: Number(kms), avs: Number(avs)}, true);
});
console.log("Before", self.rideTiles);
self.setState({rideTiles : Array.from(Realm.objects('Rides'))})
});
});
sqlite.runQuery('select distinct(ts) as ts from events', function(results){
var uniqueDate = [];
var len = results.rows.length;
for (let i = 0; i < len; i++) {
console.log(uniqueDate.indexOf(new Date(results.rows.item(i).ts*1000).toDateString()));
if(uniqueDate.indexOf(new Date(results.rows.item(i).ts*1000).toDateString()) === -1){
console.log("uniqueDate in if", uniqueDate);
uniqueDate.push(new Date(results.rows.item(i).ts*1000).toDateString());
}
}
uniqueDate.forEach(function(value){
var ts = ((new Date(value)).getTime()/1000).toFixed(0);
var tps, avs, kms, ang, dt = new Date(value);
sqlite.runQuery(`select lat,lng,ts from events where ts between ${ts} and ${ts+24*60*60} order by ts asc`, function(results){
var len = results.rows.length;
var pingData = [];
for (let i = 0; i < len; i++) {
pingData.push(results.rows.item(i));
}
var stats = DistanceAndTime(pingData);
console.log("stats", stats);
kms = stats.D/1000;
avs = (5/18)*(stats.D/stats.T);
console.log(`kms ${kms} avs : ${avs}`);
sqlite.runQuery(`select max(spd) as spd, max(ang) as ang from events where ts between ${ts} and ${ts+24*60*60}`, function(result){
tps = result.rows.item(0).spd;
ang = result.rows.item(0).ang;
console.log(`tps ${tps} ang : ${ang}`);
Realm.write(() => {
Realm.create('Stats', {top:tps, avg: avs, dis: kms, ang: ang, dt: dt }, true);
});
console.log('Stats', Array.from(Realm.objects('Stats')));
});
});
});
});
}
}
});
}
}
insertLocationData(ride){
var self = this;
sqlite.runQuery("select count(*) as count from events where rideId="+ ride.idx, function(result){
var sqlCount = result.rows.item(0).count;
console.log("sqlCount : ", sqlCount, "ride ec : ", ride.ec);
if(sqlCount !== ride.ec){
console.log("in ec eventCounts : ", self.eventCounts, "ride.ec, ", ride.ec);
self.eventCounts = ride.ec;
EE.emitEvent(EE.EVENT_TYPES.WS_REQ, AvroCodec.Encode({
devid: null,
rideid: ride.idx,
count: null,
offset: null,
tsFr: ride.sdt,
tsTo: Math.ceil(new Date()/1000)
}, AvroCodec.SCHEMA.A_REQ_FILTER)
);
}
});
}
render(){
return(
<View style={{flex:1}}>
<View style={[styles.posAbsolute, styles.gradient]}>
<Gradient.MainGradient height={430}/>
</View>
<View style={{zIndex:1,flex:1}}>
<PRides rides={this.rideTiles} navigation={this.props.navigation} />
</View>
</View>
)
}
}
const styles = {
img: {
height: 24,
width: 24,
},
gradient:{
left:0,
right:0
},
header:{
paddingTop:10,
paddingLeft:20,
paddingRight:20
},
posAbsolute: {
position: 'absolute',
},
}

In your render code, rideTiles is not set as state.
<View style={{zIndex:1,flex:1}}>
<PRides rides={this.rideTiles} navigation={this.props.navigation} />
</View>
To be updated by setState function, this.rideTiles needs to be this.state.rideTiles and this should be initialized in your constructor as this:
this.state = {
rideTiles: []
};

Related

How can maintain the view in the bottom of the list when I add another element in my infinitescroll list ? angularjs

I have an issue I have a "chat" and when I add a new text in my infinite scroll container,
I go back to the top of the page, i'm not stuck to the bottom.
How can I maintain the page in the bottom of my page while people chat
my service
tabs.factory('chat', function ($http, $timeout, $q) {
return {
default: {
delay: 100
},
data: [],
dataScroll: [],
init: function (data) {
if (this.data.length == 0) {
for (var i = 0; i < data.length; i++) {
this.data[i] = data[i]
}
} else {
var tailleDataSaved = this.data.length
var dataAAjouter = data.slice(tailleDataSaved)
for (var i = 0; i < dataAAjouter.length; i++) {
this.data.push(dataAAjouter[i])
}
}
},
request: function (showAll) {
var self = this;
var deferred = $q.defer();
var index = this.dataScroll.length
var ndDataVisible = 7
var start = index;
var end = index + ndDataVisible - 1;
$timeout(function () {
if (!showAll) {
var item = []
if (start < end) {
for (var i = start; i < end; i++) {
console.log(start)
console.log(end)
console.log(self.data[i])
if (item = self.data[i]) {
self.dataScroll.push(item);
}
}
}
} else {
self.dataScroll = self.data
}
deferred.resolve(self.dataScroll);
}, 0);
return deferred.promise;
}
}
})
my js file
$scope.listChat= function () {
$scope.isActionLoaded = false;
$http.get(apiurl).then(function (response) {
chat.init(response.data)
$scope.isActionLoaded = true
})
}
$scope.listChatInfiniteScroll = function () {
$scope.isScrollDataLoaded = false
ticketListeActionScroll.request(false).then(function (response) {
$scope.actionsInfiniteScroll = response
$scope.isScrollDataLoaded = true
})
}
html file
<div ng-if="isActionLoaded" infinite-scroll='listChatInfiniteScroll ()' infinite-scroll-distance='1'>
<div ng-repeat="chat in actionsInfiniteScroll">
{{chat.text}}
</div>
</div>
Each time I add a new message it calls listChat

tinymce unable to bind correctly nestedmenuitem

I have the following code written and I am not sure why the second level menu items are always the same for each primary level. I know this might be related with scope variables. Can you please suggest an alternative?
``
editor.ui.registry.addMenuButton(insertFields, {
text: My button,
fetch: function (callback) {
var temp = [];
for(var i = 0; i < k.state.ListItems.length; i++)
{
var obj = {};
var fieldItems = k.state.ListItems[i].description;
console.log(fieldItems);
obj["text"] = k.state.ListItems[i].Title;
obj["type"] = "nestedmenuitem";
obj["getSubmenuItems"] = () => {
console.log("fired for submenu");
var getMenuItem = t => {
return {
type: "menuitem",
text: t,
onAction: () => {
editor.insertContent("{{ " + t + "}}");
}
};
};
var nestedItems = [];
for (let k = 0; k < fieldItems.length; k++) {
nestedItems.push(getMenuItem(fieldItems[k].Title ));
}
return nestedItems;
}
temp.push(obj);
console.log(temp);
}
console.log(temp);
var items = temp;
callback(items);
}
});
}
}}
/>
was able to resolve this issue. Not sure if there was a better way.
setup: function (editor) {
for(let i = 0; i < k.state.ListItems.length;i++)
{
// let Fields = k.state.ListItems[i].Fields;
editor.ui.registry.addNestedMenuItem(k.state.ListItems[i].Id, {
text: k.state.ListItems[i].Title,
getSubmenuItems: () => {
var getMenuItem = (t,r) => {
return {
type: "menuitem",
text: t,
onAction: () => {
editor.insertContent("{{" + r +"|" + t + "}}");
}
};
};
var nestedItems = [];
for (let j = 0; j < k.state.ListItems[i].Fields.length; j++) {
// console.log(Fields[j].Title);
nestedItems.push(getMenuItem(k.state.ListItems[i].Fields[j].Title,k.state.ListItems[i].Title));
}
return nestedItems;
}
});
}
}

Camera streaming service works only with localhost but not with IP address

I have implemented a service which streams camera output on html5. But it works only if I use localhost:8080 localhost if I use IP address or machine name then it does not even detect the camera.
/*global logger*/
/*
VisualInspection
========================
#file : VisualInspection.js
#version : 1.0.0
#author :
#date : 7/28/2019
#copyright :
#license : Apache 2
Documentation
========================
Describe your widget here.
*/
// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.
define([
"dojo/_base/declare",
"mxui/widget/_WidgetBase",
"dijit/_TemplatedMixin",
"mxui/dom",
"dojo/dom",
"dojo/dom-prop",
"dojo/dom-geometry",
"dojo/dom-class",
"dojo/dom-style",
"dojo/dom-construct",
"dojo/_base/array",
"dojo/_base/lang",
"dojo/text",
"dojo/html",
"dojo/_base/event",
"VisualInspection/lib/jquery-1.11.2",
"dojo/text!VisualInspection/widget/template/VisualInspection.html",
"VisualInspection/widget/template/tf.min",
// "dojo/text!VisualInspection/widget/template/labels.json",
// "dojo/text!VisualInspection/widget/template/model.json"
], function (declare, _WidgetBase, _TemplatedMixin, dom, dojoDom, dojoProp, dojoGeometry, dojoClass, dojoStyle, dojoConstruct, dojoArray, lang, dojoText, dojoHtml, dojoEvent, _jQuery, widgetTemplate, tf) {
"use strict";
var $ = _jQuery.noConflict(true);
var LABELS_URL = "http://pni6w2465:7777/EasyPlan/model_web/labels.json"
var MODEL_JSON = "http://pni6w2465:7777/EasyPlan/model_web/model.json"
// var tf = require(['../../VisualInspection/node_modules/#tensorflow/tfjs']);
//////////////
const TFWrapper = model => {
const calculateMaxScores = (scores, numBoxes, numClasses) => {
const maxes = []
const classes = []
for (let i = 0; i < numBoxes; i++) {
let max = Number.MIN_VALUE
let index = -1
for (let j = 0; j < numClasses; j++) {
if (scores[i * numClasses + j] > max) {
max = scores[i * numClasses + j]
index = j
}
}
maxes[i] = max
classes[i] = index
}
return [maxes, classes]
}
const buildDetectedObjects = (
width,
height,
boxes,
scores,
indexes,
classes
) => {
const count = indexes.length
const objects = []
for (let i = 0; i < count; i++) {
const bbox = []
for (let j = 0; j < 4; j++) {
bbox[j] = boxes[indexes[i] * 4 + j]
}
const minY = bbox[0] * height
const minX = bbox[1] * width
const maxY = bbox[2] * height
const maxX = bbox[3] * width
bbox[0] = minX
bbox[1] = minY
bbox[2] = maxX - minX
bbox[3] = maxY - minY
objects.push({
bbox: bbox,
class: classes[indexes[i]],
score: scores[indexes[i]]
})
}
return objects
}
var img = null;
const detect = input => {
const batched = tf.tidy(() => {
const img = tf.browser.fromPixels(input)
// Reshape to a single-element batch so we can pass it to executeAsync.
// var img = null;
// //sid
// var canvas = document.querySelector("#canvasElement");
// if (canvas.getContext) {
// var ctx = canvas.getContext("2d");
// img = canvas.toDataURL("image/png");
// }
return img.expandDims(0)
})
const height = batched.shape[1]
const width = batched.shape[2]
// const height = img.height
// const width = img.width
return model.executeAsync(batched).then(result => {
const scores = result[0].dataSync()
const boxes = result[1].dataSync()
// clean the webgl tensors
batched.dispose()
tf.dispose(result)
const [maxScores, classes] = calculateMaxScores(
scores,
result[0].shape[1],
result[0].shape[2]
)
const prevBackend = tf.getBackend()
// run post process in cpu
tf.setBackend('cpu')
const indexTensor = tf.tidy(() => {
const boxes2 = tf.tensor2d(boxes, [
result[1].shape[1],
result[1].shape[3]
])
return tf.image.nonMaxSuppression(
boxes2,
maxScores,
20, // maxNumBoxes
0.5, // iou_threshold
0.5 // score_threshold
)
})
const indexes = indexTensor.dataSync()
indexTensor.dispose()
// restore previous backend
tf.setBackend(prevBackend)
return buildDetectedObjects(
width,
height,
boxes,
maxScores,
indexes,
classes
)
})
}
return {
detect: detect
}
}
//////////////////////
// Declare widget's prototype.
return declare("VisualInspection.widget.VisualInspection", [_WidgetBase, _TemplatedMixin], {
// _TemplatedMixin will create our dom node using this HTML template.
templateString: widgetTemplate,
// DOM elements
inputNodes: null,
colorSelectNode: null,
colorInputNode: null,
infoTextNode: null,
// Parameters configured in the Modeler.
mfToExecute: "",
messageString: "",
backgroundColor: "",
// Internal variables. Non-primitives created in the prototype are shared between all widget instances.
_handles: null,
_contextObj: null,
_alertDiv: null,
_readOnly: false,
// dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
constructor: function () {
logger.debug(this.id + ".constructor");
this._handles = [];
},
// dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
postCreate: function () {
logger.debug(this.id + ".postCreate");
if (this.readOnly || this.get("disabled") || this.readonly) {
this._readOnly = true;
}
this._updateRendering();
this._setupEvents();
var video = document.querySelector("#videoElement");
var canvas = document.querySelector("#canvasElement");
// if (navigator.mediaDevices.getUserMedia) {
// navigator.mediaDevices.getUserMedia({ video: true })
// .then(function (stream) {
// video.srcObject = stream;
// })
// .catch(function (err0r) {
// console.log("Something went wrong!");
// });
// }
this.componentDidMount();
},
////////////////////////////////////////////////////////
componentDidMount: function () {
var video = document.querySelector("#videoElement");
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const webCamPromise = navigator.mediaDevices
.getUserMedia({
audio: false,
video: {
facingMode: 'user'
}
})
.then(stream => {
window.stream = stream
video.srcObject = stream
return new Promise((resolve, _) => {
video.onloadedmetadata = () => {
resolve()
}
})
})
const modelPromise = tf.loadGraphModel(MODEL_JSON)
const labelsPromise = fetch(LABELS_URL).then(data => data.json())
Promise.all([modelPromise, labelsPromise, webCamPromise])
.then(values => {
const [model, labels] = values
this.detectFrame(video, model, labels)
})
.catch(error => {
console.error(error)
})
}
},
detectFrame: function (video, model, labels) {
TFWrapper(model)
.detect(video)
.then(predictions => {
this.renderPredictions(predictions, labels)
requestAnimationFrame(() => {
this.detectFrame(video, model, labels)
})
})
},
renderPredictions: function (predictions, labels) {
var canvas = document.querySelector("#canvasElement");
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
// Font options.
const font = '16px sans-serif'
ctx.font = font
ctx.textBaseline = 'top'
predictions.forEach(prediction => {
const x = prediction.bbox[0]
const y = prediction.bbox[1]
const width = prediction.bbox[2]
const height = prediction.bbox[3]
const label = labels[parseInt(prediction.class)]
// Draw the bounding box.
ctx.strokeStyle = '#00FFFF'
ctx.lineWidth = 4
ctx.strokeRect(x, y, width, height)
// Draw the label background.
ctx.fillStyle = '#00FFFF'
const textWidth = ctx.measureText(label).width
const textHeight = parseInt(font, 10) // base 10
ctx.fillRect(x, y, textWidth + 4, textHeight + 4)
})
predictions.forEach(prediction => {
const x = prediction.bbox[0]
const y = prediction.bbox[1]
const label = labels[parseInt(prediction.class)]
// Draw the text last to ensure it's on top.
ctx.fillStyle = '#000000'
ctx.fillText(label, x, y)
})
},
///////////////////////////////////////////////////////////
// mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
update: function (obj, callback) {
logger.debug(this.id + ".update");
this._contextObj = obj;
this._resetSubscriptions();
this._updateRendering(callback); // We're passing the callback to updateRendering to be called after DOM-manipulation
},
// mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
enable: function () {
logger.debug(this.id + ".enable");
},
// mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
disable: function () {
logger.debug(this.id + ".disable");
},
// mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
resize: function (box) {
logger.debug(this.id + ".resize");
},
// mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
uninitialize: function () {
logger.debug(this.id + ".uninitialize");
// Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
},
// We want to stop events on a mobile device
_stopBubblingEventOnMobile: function (e) {
logger.debug(this.id + "._stopBubblingEventOnMobile");
if (typeof document.ontouchstart !== "undefined") {
dojoEvent.stop(e);
}
},
// Attach events to HTML dom elements
_setupEvents: function () {
logger.debug(this.id + "._setupEvents");
this.connect(this.colorSelectNode, "change", function (e) {
// Function from mendix object to set an attribute.
this._contextObj.set(this.backgroundColor, this.colorSelectNode.value);
});
this.connect(this.infoTextNode, "click", function (e) {
// Only on mobile stop event bubbling!
this._stopBubblingEventOnMobile(e);
// If a microflow has been set execute the microflow on a click.
if (this.mfToExecute !== "") {
this._execMf(this.mfToExecute, this._contextObj.getGuid());
}
});
},
_execMf: function (mf, guid, cb) {
logger.debug(this.id + "._execMf");
if (mf && guid) {
mx.ui.action(mf, {
params: {
applyto: "selection",
guids: [guid]
},
callback: lang.hitch(this, function (objs) {
if (cb && typeof cb === "function") {
cb(objs);
}
}),
error: function (error) {
console.debug(error.description);
}
}, this);
}
},
// Rerender the interface.
_updateRendering: function (callback) {
logger.debug(this.id + "._updateRendering");
// Important to clear all validations!
this._clearValidations();
// The callback, coming from update, needs to be executed, to let the page know it finished rendering
this._executeCallback(callback, "_updateRendering");
},
// Handle validations.
_handleValidation: function (validations) {
logger.debug(this.id + "._handleValidation");
this._clearValidations();
var validation = validations[0],
message = validation.getReasonByAttribute(this.backgroundColor);
if (this._readOnly) {
validation.removeAttribute(this.backgroundColor);
} else if (message) {
this._addValidation(message);
validation.removeAttribute(this.backgroundColor);
}
},
// Clear validations.
_clearValidations: function () {
logger.debug(this.id + "._clearValidations");
dojoConstruct.destroy(this._alertDiv);
this._alertDiv = null;
},
// Show an error message.
_showError: function (message) {
logger.debug(this.id + "._showError");
if (this._alertDiv !== null) {
dojoHtml.set(this._alertDiv, message);
return true;
}
this._alertDiv = dojoConstruct.create("div", {
"class": "alert alert-danger",
"innerHTML": message
});
dojoConstruct.place(this._alertDiv, this.domNode);
},
// Add a validation.
_addValidation: function (message) {
logger.debug(this.id + "._addValidation");
this._showError(message);
},
// Reset subscriptions.
_resetSubscriptions: function () {
logger.debug(this.id + "._resetSubscriptions");
// Release handles on previous object, if any.
this.unsubscribeAll();
// When a mendix object exists create subscribtions.
if (this._contextObj) {
this.subscribe({
guid: this._contextObj.getGuid(),
callback: lang.hitch(this, function (guid) {
this._updateRendering();
})
});
this.subscribe({
guid: this._contextObj.getGuid(),
attr: this.backgroundColor,
callback: lang.hitch(this, function (guid, attr, attrValue) {
this._updateRendering();
})
});
this.subscribe({
guid: this._contextObj.getGuid(),
val: true,
callback: lang.hitch(this, this._handleValidation)
});
}
},
_executeCallback: function (cb, from) {
logger.debug(this.id + "._executeCallback" + (from ? " from " + from : ""));
if (cb && typeof cb === "function") {
cb();
}
}
});
});
require(["VisualInspection/widget/VisualInspection"]);
<div id="container">
<video autoplay="true" playsInline="true" width="600" height="500" id="videoElement" style="position: fixed;" >
</video>
<canvas id= "canvasElement" width="600" height="500" style="position: absolute;">
</canvas>
</div>
From Above code, When I run using localhost this code will execute :
<!-- begin snippet: js hide: false console: true babel: false -->
When I run using machine name or IP name :
This code is not considered in developer tool of chrome due to security or some reason

Drift chat opening in every page

I have drift's async script code in the index.html file of the react app.
<script>
"use strict";
!function () {
var t = window.driftt = window.drift = window.driftt || [];
if (!t.init) {
if (t.invoked) return void (window.console && console.error && console.error("Drift snippet included twice."));
t.invoked = !0, t.methods = ["identify", "config", "track", "reset", "debug", "show", "ping", "page", "hide", "off", "on"],
t.factory = function (e) {
return function () {
var n = Array.prototype.slice.call(arguments);
return n.unshift(e), t.push(n), t;
};
}, t.methods.forEach(function (e) {
t[e] = t.factory(e);
}), t.load = function (t) {
var e = 3e5, n = Math.ceil(new Date() / e) * e, o = document.createElement("script");
o.type = "text/javascript", o.async = !0, o.crossorigin = "anonymous", o.src = "https://js.driftt.com/include/" + n + "/" + t + ".js";
var i = document.getElementsByTagName("script")[0];
i.parentNode.insertBefore(o, i);
};
}
}();
drift.SNIPPET_VERSION = '0.3.1';
drift.load('----api----');
drift.on('ready', api => {
api.widget.hide();
})
</script>
The issue is, it is getting popped up in every page of the app whereas I want it only when I click a button(onClick)
The function to trigger onClick :
openDriftChat = () =>{
const { setDriftState } = this.props;
if (window.drift.api) {
//this needs to happen only once but currently happening on every page load
if (!this.props.driftInit) {
if (localStorage.token) {
var tokenBase64 = localStorage.token.split(".")[1];
var tokenBase64_1 = tokenBase64.replace("-", "+").replace("_", "/");
var token = JSON.parse(window.atob(tokenBase64_1));
window.drift.identify(token.email, {
email: token.email,
nickname: token.name
});
setDriftState(true);
}
}
window.drift.api.openChat();
}
}
I basically want it pop up only when I call the function.
Hello I had the same issue:
To hide the welcome message use the following css code
iframe#drift-widget.drift-widget-welcome-expanded-online {
display: none !important;
}
iframe#drift-widget.drift-widget-welcome-expanded-away {
display: none !important;
}
The welcome message will only be shown when your drift button. Some extra info:
To hide the drift button icon use the following js code
drift.on('ready', function (api) {
api.widget.hide()
drift.on('message', function (e) {
if (!e.data.sidebarOpen) {
api.widget.show()
}
})
drift.on('sidebarClose', function (e) {
if (e.data.widgetVisible) {
api.widget.hide()
}
})
})
To call for the sidebar from a specific button use the following
Javascript
(function () {
var DRIFT_CHAT_SELECTOR = '.drift-open-chat'
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', fn);
} else {
document.attachEvent('onreadystatechange', function () {
if (document.readyState != 'loading')
fn();
});
}
}
function forEachElement(selector, fn) {
var elements = document.querySelectorAll(selector);
for (var i = 0; i < elements.length; i++)
fn(elements[i], i);
}
function openSidebar(driftApi, event) {
event.preventDefault();
driftApi.sidebar.open();
return false;
}
ready(function () {
drift.on('ready', function (api) {
var handleClick = openSidebar.bind(this, api)
forEachElement(DRIFT_CHAT_SELECTOR, function (el) {
el.addEventListener('click', handleClick);
});
});
});
})();
HTML
<a class="drift-open-chat">Open Chat</a>
I hope this helps someone out there.
PS: The above javascript code must be included after you have initialized your drift widget.
You need to disable that through the application: turn off the Playbooks.
Here is the link to do so: https://app.drift.com/playbooks
Hope it helps.

Jump(seek) to markers in videojs on button click

I have added markers to my videojs player. I want to jump to start time of every marker every time I click a particular button (say a next button). How should I do this. I know I have to change the currentTime but I am not getting how to solve the complete problem. I have start time of each marker. Any kind of input would be helpful.
Player.js
import assign from 'object-assign'
import cx from 'classnames'
import blacklist from 'blacklist'
import React from 'react'
module.exports = React.createClass({
displayName: 'VideoJS',
componentDidMount() {
var self = this;
var player = videojs(this.refs.video, this.props.options).ready(function() {
self.player = this;
self.player.on('play', self.handlePlay);
});
if(this.props.onPlayerInit) this.props.onPlayerInit(player);
player.markers({
markerStyle: {},
markers: [
{ startTime:10, endTime:15, time: 9.5, text: "compliance"},
{ startTime:20, endTime:25, time: 16, text: "compliance"},
{ startTime:30, endTime:38, time: 23.6,text: "compliance"},
{ startTime:51, endTime:55, time: 28, text: "compliance"}
]
});
},
handlePlay: function() {
if(this.props.onPlay) {
this.props.onPlay(this.player);
}
},
render() {
var props = blacklist(this.props, 'children', 'className', 'src', 'type', 'onPlay');
props.className = cx(this.props.className, 'videojs', 'video-js vjs-default-skin', 'vjs-big-play-centered');
assign(props, {
ref: 'video',
controls: true
});
return (
<div>
<video {... props}>
<source src={this.props.src} type={this.props.type}/>
</video>
</div>
)
}
});
Marker.js
(function($, video, undefined) {
//default setting
var defaultSetting = {
markerStyle: {
'border-radius': '0%',
},
markerTip: {
display: true,
text: function(marker) {
return "Break: "+ marker.text;
},
time: function(marker) {
return marker.time;
}
},
breakOverlay:{
display: false,
displayTime: 3,
text: function(marker) {
return "Break overlay: " + marker.overlayText;
},
style: {
'width':'100%',
'height': '20%',
'background-color': 'rgba(0,0,0,0.7)',
'color': 'white',
'font-size': '17px'
}
},
onMarkerClick: function(marker) {},
onMarkerReached: function(marker) {},
markers: []
};
// create a non-colliding random number
function generateUUID() {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
};
function registerVideoJsMarkersPlugin(options) {
/**
* register the markers plugin (dependent on jquery)
*/
var setting = $.extend(true, {}, defaultSetting, options),
markersMap = {},
markersList = [], // list of markers sorted by time
videoWrapper = $(this.el()),
currentMarkerIndex = -1,
player = this,
markerTip = null,
breakOverlay = null,
overlayIndex = -1;
function sortMarkersList() {
// sort the list by time in asc order
markersList.sort(function(a, b){
return setting.markerTip.time(a) - setting.markerTip.time(b);
});
}
function addMarkers(newMarkers) {
// create the markers
$.each(newMarkers, function(index, marker) {
//console.log(index);
// console.log(marker);
marker.key = generateUUID();
//console.log(marker.key);
videoWrapper.find('.vjs-progress-control').append(
createMarkerDiv(marker));
// store marker in an internal hash map
markersMap[marker.key] = marker;
console.log(markersMap);
markersList.push(marker);
});
sortMarkersList();
}
function getPosition(marker){
return (setting.markerTip.time(marker) / player.duration()) * 100
}
function createMarkerDiv(marker, duration) {
var markerDiv = $("<div class='vjs-marker'></div>");
console.log(marker.length);
markerDiv.css(setting.markerStyle)
.css({"margin-left" : -parseFloat(markerDiv.css("width"))/2 + 'px',
"left" : getPosition(marker) + '%', })
.attr("data-marker-key", marker.key)
.attr("data-marker-time", setting.markerTip.time(marker));
console.log(setting.markerTip.time(marker));
// add user-defined class to marker
if (marker.class) {
markerDiv.addClass(marker.class);
}
// bind click event to seek to marker time
markerDiv.on('click', function(e) {
var preventDefault = false;
if (typeof setting.onMarkerClick === "function") {
// if return false, prevent default behavior
preventDefault = setting.onMarkerClick(marker) == false;
}
if (!preventDefault) {
var key = $(this).data('marker-key');
player.currentTime(setting.markerTip.time(markersMap[key]));
}
});
if (setting.markerTip.display) {
registerMarkerTipHandler(markerDiv);
}
return markerDiv;
}
function updateMarkers() {
// update UI for markers whose time changed
for (var i = 0; i< markersList.length; i++) {
var marker = markersList[i];
var markerDiv = videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key +"']");
var markerTime = setting.markerTip.time(marker);
if (markerDiv.data('marker-time') != markerTime) {
markerDiv.css({"left": getPosition(marker) + '%'})
.attr("data-marker-time", markerTime);
}
}
sortMarkersList();
}
function removeMarkers(indexArray) {
// reset overlay
if (breakOverlay){
overlayIndex = -1;
breakOverlay.css("visibility", "hidden");
}
currentMarkerIndex = -1;
for (var i = 0; i < indexArray.length; i++) {
var index = indexArray[i];
var marker = markersList[index];
if (marker) {
// delete from memory
delete markersMap[marker.key];
markersList[index] = null;
// delete from dom
videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key +"']").remove();
}
}
// clean up array
for (var i = markersList.length - 1; i >=0; i--) {
if (markersList[i] === null) {
markersList.splice(i, 1);
}
}
// sort again
sortMarkersList();
}
// attach hover event handler
function registerMarkerTipHandler(markerDiv) {
markerDiv.on('mouseover', function(){
var marker = markersMap[$(this).data('marker-key')];
markerTip.find('.vjs-tip-inner').text(setting.markerTip.text(marker));
// margin-left needs to minus the padding length to align correctly with the marker
markerTip.css({"left" : getPosition(marker) + '%',
"margin-left" : -parseFloat(markerTip.css("width"))/2 - 5 + 'px',
"visibility" : "visible"});
}).on('mouseout',function(){
markerTip.css("visibility", "hidden");
});
}
function initializeMarkerTip() {
markerTip = $("<div class='vjs-tip'><div class='vjs-tip-arrow'></div><div class='vjs-tip-inner'></div></div>");
videoWrapper.find('.vjs-progress-control').append(markerTip);
}
// show or hide break overlays
function updateBreakOverlay() {
if(!setting.breakOverlay.display || currentMarkerIndex < 0){
return;
}
var currentTime = player.currentTime();
var marker = markersList[currentMarkerIndex];
var markerTime = setting.markerTip.time(marker);
if (currentTime >= markerTime &&
currentTime <= (markerTime + setting.breakOverlay.displayTime)) {
if (overlayIndex != currentMarkerIndex){
overlayIndex = currentMarkerIndex;
breakOverlay.find('.vjs-break-overlay-text').html(setting.breakOverlay.text(marker));
}
breakOverlay.css('visibility', "visible");
} else {
overlayIndex = -1;
breakOverlay.css("visibility", "hidden");
}
}
// problem when the next marker is within the overlay display time from the previous marker
function initializeOverlay() {
breakOverlay = $("<div class='vjs-break-overlay'><div class='vjs-break-overlay-text'></div></div>")
.css(setting.breakOverlay.style);
videoWrapper.append(breakOverlay);
overlayIndex = -1;
}
function onTimeUpdate() {
onUpdateMarker();
updateBreakOverlay();
}
function onUpdateMarker() {
/*
check marker reached in between markers
the logic here is that it triggers a new marker reached event only if the player
enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
*/
var getNextMarkerTime = function(index) {
if (index < markersList.length - 1) {
return setting.markerTip.time(markersList[index + 1]);
}
// next marker time of last marker would be end of video time
return player.duration();
}
var currentTime = player.currentTime();
var newMarkerIndex;
if (currentMarkerIndex != -1) {
// check if staying at same marker
var nextMarkerTime = getNextMarkerTime(currentMarkerIndex);
if(currentTime >= setting.markerTip.time(markersList[currentMarkerIndex]) &&
currentTime < nextMarkerTime) {
return;
}
// check for ending (at the end current time equals player duration)
if (currentMarkerIndex === markersList.length -1 &&
currentTime === player.duration()) {
return;
}
}
// check first marker, no marker is selected
if (markersList.length > 0 &&
currentTime < setting.markerTip.time(markersList[0])) {
newMarkerIndex = -1;
} else {
// look for new index
for (var i = 0; i < markersList.length; i++) {
nextMarkerTime = getNextMarkerTime(i);
if(currentTime >= setting.markerTip.time(markersList[i]) &&
currentTime < nextMarkerTime) {
newMarkerIndex = i;
break;
}
}
}
// set new marker index
if (newMarkerIndex != currentMarkerIndex) {
// trigger event
if (newMarkerIndex != -1 && options.onMarkerReached) {
options.onMarkerReached(markersList[newMarkerIndex]);
}
currentMarkerIndex = newMarkerIndex;
}
}
// setup the whole thing
function initialize() {
if (setting.markerTip.display) {
initializeMarkerTip();
}
// remove existing markers if already initialized
player.markers.removeAll();
addMarkers(options.markers);
if (setting.breakOverlay.display) {
initializeOverlay();
}
onTimeUpdate();
player.on("timeupdate", onTimeUpdate);
}
// setup the plugin after we loaded video's meta data
player.on("loadedmetadata", function() {
initialize();
});
// exposed plugin API
player.markers = {
getMarkers: function() {
return markersList;
},
next : function() {
// go to the next marker from current timestamp
var currentTime = player.currentTime();
for (var i = 0; i < markersList.length; i++) {
var markerTime = setting.markerTip.time(markersList[i]);
if (markerTime > currentTime) {
player.currentTime(markerTime);
break;
}
}
},
prev : function() {
// go to previous marker
var currentTime = player.currentTime();
for (var i = markersList.length - 1; i >=0 ; i--) {
var markerTime = setting.markerTip.time(markersList[i]);
// add a threshold
if (markerTime + 0.5 < currentTime) {
player.currentTime(markerTime);
break;
}
}
},
add : function(newMarkers) {
// add new markers given an array of index
addMarkers(newMarkers);
},
remove: function(indexArray) {
// remove markers given an array of index
removeMarkers(indexArray);
},
removeAll: function(){
var indexArray = [];
for (var i = 0; i < markersList.length; i++) {
indexArray.push(i);
}
removeMarkers(indexArray);
},
updateTime: function(){
// notify the plugin to update the UI for changes in marker times
updateMarkers();
},
reset: function(newMarkers){
// remove all the existing markers and add new ones
player.markers.removeAll();
addMarkers(newMarkers);
},
destroy: function(){
// unregister the plugins and clean up even handlers
player.markers.removeAll();
breakOverlay.remove();
markerTip.remove();
player.off("timeupdate", updateBreakOverlay);
delete player.markers;
},
};
}
videojs.plugin('markers', registerVideoJsMarkersPlugin);
})(jQuery, window.videojs);

Resources