Integrate webSDK with jQuery Inside Vue Component? - javascript

I am trying to integrate the webSDK from https://www.pollfish.com/docs/webplugin in our Vue app.
Ideally I want to load jquery only in one component.
I wrote the following code but when I click the button it doesnt work.
Here is an example with working code that does NOT use Vue https://github.com/pollfish/webplugin-rewarded-example/blob/master/index.html but does run locally.
I get no errors and I can console.log(Pollfish) inside the the showFullSurvey method.
My code is:
<template>
<div class="container" v-if="isFreePlan">
<h2>Remove ads and save unlimited projects for 5 days</h2>
<button #click="showFullSurvey">Take {{lengthOfInteraction}} Survey Now</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
data() {
return {
surveyAvailable: false,
lengthOfInteraction: ''
}
},
methods: {
showFullSurvey() {
Pollfish.showFullSurvey();
console.log('show survey')
}
},
mounted() {
const pollFishConfig = {
api_key: "api-key",
debug: process.env.NODE_ENV === 'production' ? false : true,
ready: () => {},
uuid: this.userId,
surveyAvailable: onSurveyAvailable,
surveyNotAvailable: onSurveyNotAvailable,
surveyCompletedCallback: onSurveyCompleted,
userNotEligibleCallback: onUserDisqualified
};
console.log('POllfish config');
const onSurveyAvailable = (data) => {
console.log('SUrvey Available');
};
const onSurveyNotAvailable = () => {
console.log('SUrvey Not Available');
};
const onSurveyCompleted = () => {
console.log('SUrvey Completed');
};
const onUserDisqualified = () => {
console.log('USer Disqualified');
};
this.addJQuery;
this.addPollFishSDK;
},
computed: {
...mapGetters("session", ['userId']),
...mapGetters("account", ["isFreePlan"]),
addJQuery() {
const url = 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js';
if(document.querySelector(`script[src='${url}']`)){ return; }
let jquery = document.createElement('script');
jquery.setAttribute('src', url);
document.body.appendChild(jquery);
console.log('jquery script')
},
addPollFishSDK() {
const url = 'https://storage.googleapis.com/pollfish_production/sdk/webplugin/pollfish.min.js';
if(document.querySelector(`script[src='${url}']`)){ return; }
let pollFishSdk = document.createElement('script');
pollFishSdk.setAttribute('src', url);
document.body.appendChild(pollFishSdk);
console.log('pollfish script')
}
}
}
</script>

In order to integrate our web plugin in your Vue.js app, you need to set the pollfishConfig object in the window. Please be careful with the object's name to be exactly the same as the following example.
window.pollfishConfig = {
api_key: "api-key",
debug: process.env.NODE_ENV === 'production' ? false : true,
ready: () => {},
uuid: this.userId,
surveyAvailable: onSurveyAvailable,
surveyNotAvailable: onSurveyNotAvailable,
surveyCompletedCallback: onSurveyCompleted,
userNotEligibleCallback: onUserDisqualified
};
Also, based on your example, you need to be sure that the jQuery library is loaded first and be available for our WebPlugin SDK. So you need to handle the onload event. An example solution based on your code is the following:
const addScript = (url, onLoad) => {
const scriptExists = document.querySelector(`script[src='${url}']`);
if (!scriptExists) {
let script = document.createElement('script');
document.body.appendChild(script);
script.onload = () => {
onLoad && onLoad();
}
script.src = url;
}
}
addScript('https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', () => {
addScript('https://storage.googleapis.com/pollfish_production/sdk/webplugin/pollfish.min.js')
});

Related

How to remove freshchat window from other pages in Vuejs?

I need to remove fresh chat window from all pages except the home page. I put the script inside the home component mounted part. But once it loads it never closes. If I refresh the page it disappears. I think it's because of the SPA. I think once it loads the JS it does not clear the header script.
mounted() {
function initFreshChat() {
window.fcWidget.init({
token: "***",
host: "https://wchat.freshchat.com"
});
}
function initialize(i, t) {
var e;
i.getElementById(t) ? initFreshChat() : ((e = i.createElement("script")).id = t, e.async = !0, e.src = "https://wchat.freshchat.com/js/widget.js", e.onload = initFreshChat, i.head.appendChild(e))
}
function initiateCall() {
initialize(document, "freshchat-js-sdk")
}
window.addEventListener ? window.addEventListener("load", initiateCall, !1) : window.attachEvent("load", initiateCall, !1);
}
Try window.fcWidget.destroy(); to unload freshchat. Learn more at https://developers.freshchat.com/web-sdk/ They provide all required events and functions to open and close chatbox.
I did something similar and added it to the useEffect hook in react, just specifying on each page the argument of true if I wanted it hidden.
const addFreshchat = (hiddenOnLoad) => {
const hideFreshChat = {
config: {
headerProperty: {
hideChatButton: true,
},
},
token: 'your-account-token',
host: 'https://<freshchatUrl>',
};
const showFreshChat = {
token: 'your-account-token',
host: 'https://<freshchatUrl>',
};
const freshChatConfig = hiddenOnLoad ? hideFreshChat : showFreshChat;
function initFreshChat() {
window?.fcWidget?.init(freshChatConfig);
}
function initialize(i, t) {
let e;
const initiate = i.getElementById(t)
? initFreshChat()
: ((e = i.createElement('script')).id = t, e.async = !0, e.src = 'https://wchat.eu.freshchat.com/js/widget.js', e.onload = initFreshChat, i.head.appendChild(e));
return initiate;
}
function initiateCall() {
initialize(document, 'freshchat-js-sdk');
}
function addSDK() {
const addListener = window.addEventListener ? window.addEventListener('load', initiateCall, !1) : window.attachEvent('load', initiateCall, !1);
return addListener;
}
addSDK();
};
I just integrated fresh chat in my app:
window.fcWidget.show() // show widget
window.fcWidget.hide() // hide widget
window.fcWidget.init({
token: 'your token',
host: 'host'
}) // initialize
window.fcWidget.destroy() //destroy

Amazon Connect Outbound CCP Softphone Number Prefill

I have a pretty simple requirement to click on a phone number hyperlink and have my web-app open the AWS connect soft-phone dialer with the selected number, ready for the person to press the "call button"
I have enabled an AWS connect account and I am hosting a custom CCP site via an S3 bucket (as illustrated here)
My plan is to initiate a link to the CCP page and embed a URL Search Param
"?number=04125412,customTag=helloWorld"
I have used this code on the CCP Page
Also, within the index page, I add some code to receive the input params:
<script>
var urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.get('number')); //the phone number for the dialer
console.log(urlParams.get('customTag')); // the call notes for the CTR custom Attributes
</script>
I Am struggling to understand how I can interact with A: the Dialer to pre-fill the number and B: to post custom attributes to the AWS contact record during the call.
Any help would be appreciated.
I set this up in my React application but you should be able to repurpose for your needs
import React from "react";
import {connect} from 'react-redux'
import Button from "components/CustomButtons/Button.jsx";
import {receiveCallAttr, initCall, callFlow} from 'store/apps/AppSettings/actions';
class AmazonConnect extends React.Component {
constructor(props) {
super(props);
this.state = {
active:false,
reloadAttempts:0,
activeCall:{},
cip:false,
agentQueueNumber:"xxxxxxxxxx",
recordingQueueNumber:"xxxxxxxxxx"
};
this.awsConnect = this.awsConnect.bind(this)
this.loginWindow = this.loginWindow.bind(this);
this.activeWindow = this.activeWindow.bind(this);
this.initCall = this.initCall.bind(this)
this.initContact = this.initContact.bind(this)
this.redirect = this.redirect.bind(this)
}
componentWillReceiveProps(newProps){
const {AppSettings, initCall, callFlow} = newProps
const {cip, active} = this.state
if( active && !cip){
this.setState({activeCall: AppSettings.call})
if(AppSettings.call.number){
console.log("init call")
this.initCall(AppSettings.call.number)
initCall({})
}
else{
console.log("Invalid Phone number")
}
if( AppSettings.flow !== "" ){
this.setState({activeFlow: AppSettings.flow})
this.initCallFlow(AppSettings.flow)
callFlow("")
}
}
}
initCallFlow = flow => new Promise((res, rej) => {
if(this.contact){
console.log(this.contact)
let endpoint;
switch(flow){
case "agentQueue":
endpoint = window.connect.Endpoint.byPhoneNumber(this.state.agentQueueNumber);
this.contact.addConnection(endpoint, {
success: function() {
this.contact.conferenceConnections({
success: function() {
console.log("confrence success")
res("successfullly init ssn flow")
},
failure: function() {
console.log("confrence failure")
res("successfullly init ssn flow")
}
});
},
failure: function() {
rej("failed to init ssn flow")
}
});
break
case "recordingQueue":
endpoint = window.connect.Endpoint.byPhoneNumber(this.state.recordingQueueNumber);
this.contact.addConnection(endpoint, {
success: function() {
res("successfullly init recording flow")
},
failure: function() {
rej("failed to init recording flow")
}
});
break
default:
res()
break
}
}
else{
rej("no contact available")
}
})
awsConnect = () => new Promise((res, rej) => {
window.connect.core.initCCP(document.getElementById("softPhone"), {
ccpUrl: process.env.REACT_APP_AWS_CONNECT_URL, /*REQUIRED*/
loginPopup: true, /*optional, default TRUE*/
softphone: { /*optional*/
disableRingtone: false, /*optional*/
allowFramedSoftphone: true
}
});
this.bus = window.connect.core.getEventBus();
this.bus.subscribe(window.connect.AgentEvents.INIT, (agent) => {
this.activeWindow()
});
this.bus.subscribe(window.connect.EventType.TERMINATED, () => {
console.log("TERMINATED")
this.setState({cip:false})
this.logout()
});
this.bus.subscribe(window.connect.EventType.AUTH_FAIL, () => {
console.log("AUTH_FAIL")
this.logout()
})
window.connect.agent(function(agent) {
const w = window.open('', window.connect.MasterTopics.LOGIN_POPUP);
if (w) {
w.close()
}
});
window.connect.contact((contact) => {
this.contact = contact
const {receiveCallAttr} = this.props
try{
var attr = contact.getAttributes()
attr.active = true
console.log(attr)
receiveCallAttr(attr)
this.redirect()
}
catch(err){
console.log(err)
}
contact.onEnded(() => {
console.log("call ended")
receiveCallAttr({active:false})
this.setState({cip:false})
this.contact = null
})
});
res()
})
initContact = () => {
this.setState({cip:false})
}
redirect = () => {
const {location, auth, history} = this.props
switch(auth.user.type){
case "Agent":
if(location.pathname !== "/agent/management"){
history.push({
pathname: '/agent/management',
search: '',
state: {}
})
}
break;
case "Service":
//handle redirect to service page
if(location.pathname !== "/service/dashboard"){
history.push({
pathname: "/service/dashboard",
search: '',
state: {}
})
}
break;
default:
break
}
}
initCall = (phone) => {
this.initContact()
window.connect.agent(function(agent) {
const endpoint = window.connect.Endpoint.byPhoneNumber(phone)
agent.connect(endpoint , {
queueARN : process.env.CONNECT_QUEUE_ARN,
success : function(){
console.log("Success call!!!!!!")
},
failure : function(){
console.log("Call failed!!!!!!!")
}
});
});
}
logout(){
this.setState({cip:false})
this.loginWindow()
this.agent = null
this.contact = null
window.connect.core.terminate();
window.connect.core.client = new window.connect.NullClient();
window.connect.core.masterClient = new window.connect.NullClient();
window.connect.core.eventBus = new window.connect.EventBus();
window.connect.core.initialized = false;
this.bus = false;
var myNode = document.getElementById("softPhone")
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
}
componentWillUnmount() {
console.log("terminating aws connect session")
this.logout()
}
loginWindow(){
this.setState({active:false})
}
activeWindow(){
this.setState({active:true})
}
render() {
const displaylogin = this.state.active? "none":"block";
const displayConnect = this.state.active? "block":"none";
return (
<div>
<Button color={"rose"} onClick={this.awsConnect} style={{display:displaylogin, width:320}}>Login to AWS Connect</Button>
<div id="softPhone" style={{height:465,width:320, display:displayConnect}}>
</div>
</div>
);
}
}
function mapStateToProps(state){
return state
}
export default connect(mapStateToProps, {receiveCallAttr, initCall, callFlow})(AmazonConnect);
The previous answer by Ethan Harris helped me to reach the solution, but to distill it to allow a link to dial a number. You find the ARN in the Amazon Connect UI here:
Using the ARN copied from the Connect UI, this function seems to work for automating dialing a number. This took way more effort to figure out than I ever expected.
function dial_number(phone) {
connect.agent(function (agent) {
agent.connect(connect.Endpoint.byPhoneNumber(phone),
{
queueARN: arn
});
});
}

Load third party js in head tag using angular json + environment based

I want to add my google analytics JS in the HEAD tag. so I can try to configure with angular.json
"scripts": [
"src/assets/js/jquery-3.4.1.min.js",
"src/assets/js/general.js",
{ "input": "src/assets/js/google-analytics.js", "lazy": false }
]
but, not add in the head tag, all js are added at the end of the body tag.
one other option is, will add directly in index.html. but I need to add base on the environment.
Please share if any way to add JS in HEAD tag directly using angular.json
try to add your JavaScript file at the top of head tag inside the index.html file of our main app.
you will find it at project-name/src/index.html
To load js file externally from assets
create service file add file to the assets and write path in array.
import { Injectable } from "#angular/core";
import { Injectable } from "#angular/core";
declare var document: any;
#Injectable({
providedIn:'root'
})
export class ScriptService {
private scripts: any = {};
constructor() {
ScriptConstant.forEach((script: any) => {
this.scripts[script.name] = {
loaded: false,
src: script.src
};
});
}
load(...scripts: string[]) {
var promises: any[] = [];
scripts.forEach(script => promises.push(this.loadScript(script)));
return Promise.all(promises);
}
loadAll() {
var promises: any[] = [];
ScriptConstant.forEach(script => {
// promises.push(delay(1000));
promises.push(this.loadScript(script.name));
});
return Promise.all(promises);
}
loadScript(name: string) {
return new Promise((resolve, reject) => {
//resolve if already loaded
if (this.scripts[name].loaded) {
resolve({ script: name, loaded: true, status: "Already Loaded" });
} else {
//load script
let script = document.createElement("script");
script.type = "text/javascript";
script.src = this.scripts[name].src;
if (script.readyState) {
//IE
script.onreadystatechange = () => {
if (
script.readyState === "loaded" ||
script.readyState === "complete"
) {
script.onreadystatechange = null;
this.scripts[name].loaded = true;
resolve({ script: name, loaded: true, status: "Loaded" });
}
};
} else {
//Others
script.onload = () => {
this.scripts[name].loaded = true;
resolve({ script: name, loaded: true, status: "Loaded" });
};
}
script.onerror = (error: any) =>
resolve({ script: name, loaded: false, status: "Loaded" });
document.getElementsByTagName("body")[0].appendChild(script);
}
});
}
}
interface Scripts {
name: string;
src: string;
}
export const ScriptConstant: Scripts[] = [
{ name: "multislider", src: "assets/js/multislider.js" },
];
Inject this ScriptService wherever you need it and load js libs like this
this.script.load('multislider').then(data => {
console.log('script loaded ', data);
}).catch(error => console.log(error));

Vanilla JS vs React Class Binding for Listener Functions

I am following some api docs where the only code examples are in vanilla JS but I am trying to use them in React Native. They give fully functional React Native apps for reference but I can't figure out how to repurpose the methods for my needs.
In the api docs it gives the example:
ConnectyCube.videochat.onCallListener = function(session, extension) {
// here show some UI with 2 buttons - accept & reject, and by accept -> run the following code:
var extension = {};
session.accept(extension);
};
ConnectyCube is an module import and I need to use this particular method in React Native. In the app they provide as an example, it looks like this in a class component:
class AppRoot extends React.Component {
componentDidMount() {
ConnectyCube.init(...config)
this.setupListeners();
}
setupListeners() {
ConnectyCube.videochat.onCallListener = this.onCallListener.bind(this);
ConnectyCube.videochat.onUserNotAnswerListener = this.onUserNotAnswerListener.bind(this);
ConnectyCube.videochat.onAcceptCallListener = this.onAcceptCallListener.bind(this);
ConnectyCube.videochat.onRemoteStreamListener = this.onRemoteStreamListener.bind(this);
ConnectyCube.videochat.onRejectCallListener = this.onRejectCallListener.bind(this);
ConnectyCube.videochat.onStopCallListener = this.onStopCallListener.bind(this);
ConnectyCube.videochat.onSessionConnectionStateChangedListener = this.onSessionConnectionStateChangedListener.bind(this);
}
onCallListener(session, extension) {
console.log('onCallListener, extension: ', extension);
const {
videoSessionObtained,
setMediaDevices,
localVideoStreamObtained,
callInProgress
} = this.props
videoSessionObtained(session);
Alert.alert(
'Incoming call',
'from user',
[
{text: 'Accept', onPress: () => {
console.log('Accepted call request');
CallingService.getVideoDevices()
.then(setMediaDevices);
CallingService.getUserMedia(session).then(stream => {
console.log(stream)
localVideoStreamObtained(stream);
CallingService.acceptCall(session);
callInProgress(true);
});
}},
{
text: 'Reject',
onPress: () => {
console.log('Rejected call request');
CallingService.rejectCall(session);
},
style: 'cancel',
},
],
{cancelable: false},
);
}
onUserNotAnswerListener(session, userId) {
CallingService.processOnUserNotAnswer(session, userId);
this.props.userIsCalling(false);
}
onAcceptCallListener(session, userId, extension) {
CallingService.processOnAcceptCallListener(session, extension);
this.props.callInProgress(true);
}
onRemoteStreamListener(session, userID, remoteStream){
this.props.remoteVideoStreamObtained(remoteStream, userID);
this.props.userIsCalling(false);
}
onRejectCallListener(session, userId, extension){
CallingService.processOnRejectCallListener(session, extension);
this.props.userIsCalling(false);
this.props.clearVideoSession();
this.props.clearVideoStreams();
}
onStopCallListener(session, userId, extension){
this.props.userIsCalling(false);
this.props.callInProgress(false);
this.props.clearVideoSession();
this.props.clearVideoStreams();
CallingService.processOnStopCallListener(session, extension);
}
onSessionConnectionStateChangedListener(session, userID, connectionState){
console.log('onSessionConnectionStateChangedListener', userID, connectionState);
}
render() {
console.log('hey');
return <AppRouter />
}
}
function mapDispatchToProps(dispatch) {
return {
videoSessionObtained: videoSession => dispatch(videoSessionObtained(videoSession)),
userIsCalling: isCalling => dispatch(userIsCalling(isCalling)),
callInProgress: inProgress => dispatch(callInProgress(inProgress)),
remoteVideoStreamObtained: remoteStream => dispatch(remoteVideoStreamObtained(remoteStream)),
localVideoStreamObtained: localStream => dispatch(localVideoStreamObtained(localStream)),
clearVideoSession: () => dispatch(clearVideoSession()),
clearVideoStreams: () => dispatch(clearVideoStreams()),
setMediaDevices: mediaDevices => dispatch(setMediaDevices(mediaDevices)),
setActiveVideoDevice: videoDevice => dispatch(setActiveVideoDevice(videoDevice))
}
}
export default connect(null, mapDispatchToProps)(AppRoot)
I want to set up the listeners but I am not using classes like the one in the component above called CallingService or using the same redux actions - I'm taking a functional approach. When I paste the code from the docs in to a service which is just a normal function, I get the error:
Cannot set property 'onCallListener' of undefined.
Any ideas welcome!
componentDidMount() {
document.addEventListener("keyup",this.login,false);
}
login = (event) => {
console.log('i have been activated on keyup event from the componentDidMount()');
};

Global loaded data in VueJs is occasionally null

I'm new to VueJs and currently trying to load some data only once and make it globally available to all vue components. What would be the best way to achieve this?
I'm a little bit stuck because the global variables occasionally seem to become null and I can't figure out why.
In my main.js I make three global Vue instance variables:
let globalData = new Vue({
data: {
$serviceDiscoveryUrl: 'http://localhost:40000/api/v1',
$serviceCollection: null,
$clientConfiguration: null
}
});
Vue.mixin({
computed: {
$serviceDiscoveryUrl: {
get: function () { return globalData.$data.$serviceDiscoveryUrl },
set: function (newUrl) { globalData.$data.$serviceDiscoveryUrl = newUrl; }
},
$serviceCollection: {
get: function () { return globalData.$data.$serviceCollection },
set: function (newCollection) { globalData.$data.$serviceCollection = newCollection; }
},
$clientConfiguration: {
get: function () { return globalData.$data.$clientConfiguration },
set: function (newConfiguration) { globalData.$data.$clientConfiguration = newConfiguration; }
}
}
})
and in my App.vue component I load all the data:
<script>
export default {
name: 'app',
data: function () {
return {
isLoading: true,
isError: false
};
},
methods: {
loadAllData: function () {
this.$axios.get(this.$serviceDiscoveryUrl)
.then(
response => {
this.$serviceCollection = response.data;
let configurationService = this.$serviceCollection.services.find(obj => obj.key == "ProcessConfigurationService");
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
}
);
this.isLoading = false;
})
}
},
created: function m() {
this.loadAllData();
}
}
</script>
But when I try to access the $clientConfiguration it seems to be null from time to time and I can't figure out why. For example when I try to build the navigation sidebar:
beforeMount: function () {
let $ = JQuery;
let clients = [];
if (this.$clientConfiguration === null)
console.error("client config is <null>");
$.each(this.$clientConfiguration, function (key, clientValue) {
let processes = [];
$.each(clientValue.processConfigurations, function (k, processValue) {
processes.push(
{
name: processValue.name,
url: '/process/' + processValue.id,
icon: 'fal fa-project-diagram'
});
});
clients.push(
{
name: clientValue.name,
url: '/client/' + clientValue.id,
icon: 'fal fa-building',
children: processes
});
});
this.nav.find(obj => obj.name == 'Processes').children = clients;
The most likely cause is that the null is just the initial value. Loading the data is asynchronous so you'll need to wait for loading to finish before trying to create any components that rely on that data.
You have an isLoading flag, which I would guess is your attempt to wait for loading to complete before showing any components (maybe via a suitable v-if). However, it currently only waits for the first request and not the second. So this:
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
}
);
this.isLoading = false;
would need to be:
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
this.isLoading = false;
}
);
If it isn't that initial value that's the problem then you need to figure out what is setting it to null. That should be prety easy, just put a debugger statement in your setter:
$clientConfiguration: {
get: function () { return globalData.$data.$clientConfiguration },
set: function (newConfiguration) {
if (!newConfiguration) {
debugger;
}
globalData.$data.$clientConfiguration = newConfiguration;
}
}
Beyond the problem with the null, if you're using Vue 2.6+ I would suggest taking a look at Vue.observable, which is a simpler way of creating a reactive object than creating a new Vue instance.
Personally I would probably implement all of this by putting a reactive object on Vue.prototype rather than using a global mixin. That assumes that you even need the object to be reactive, if you don't then this is all somewhat more complicated than it needs to be.

Categories