angular 8- webrtc-adapter in Firefox not working - webrtc

I try to run poc which created in angular 8 using webrtc-adpater. Problem is that it is not working in firefox and safari, but working in chrome.
What I observe connection is never connecting or connected in firefox.
I import webrtc in code so anything else we need to handle for firefox and safari.
Basically from Wowza I trying to consume streaming.
Below code sample which working in chrome but not in Firefox and safari:
const ICE_SERVERS: RTCIceServer[] = [
{ urls: ['stun:stun.example.com', 'stun:stun-1.example.com'] },
{ urls: 'stun:stun.l.google.com:19302' }
];
setupSignalingServer(ele) {
const self = this;
window.RTCPeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection;
let signalingConnection = new WebSocket('wss://local.geofabricdev.com/webrtc-session.json');
signalingConnection.binaryType = 'arraybuffer';
signalingConnection.onopen = function (res) {
console.log('connection open');
signalingConnection.onerror = self.errorHandler;
// window.RTCPeerConnection = self.getRTCPeerConnection();
const peerConnection = new RTCPeerConnection(PEER_CONNECTION_CONFIG);
peerConnection.addEventListener('connectionstatechange', event => {
console.log(event);
if (peerConnection.connectionState === 'connected') {
console.log('connected');
}
});
peerConnection.addEventListener('icecandidate', event => {
console.log('peerconnection state5:' + peerConnection.connectionState);
if (event.candidate) {
// signalingChannel.send({'new-ice-candidate': event.candidate});
}
});
peerConnection.addEventListener('track', async event => {
console.log('gotRemoteTrack: kind:' + event.track.kind + ' stream:' + event.streams[0]);
const video = self.createVideo();
// const remoteVideo1 = document.querySelector('#remoteVideo') as HTMLVideoElement;
try {
video.srcObject = event.streams[0];
// remoteVideo1.srcObject = event.streams[0];
} catch (error) {
video.src = window.URL.createObjectURL(event.streams[0]);
}
});
signalingConnection.addEventListener('message', async message => {
console.dir(message);
// self.getSignalMessageCallback(message);
console.log('wsConnection.onmessage: ' + message.data);
const signal = JSON.parse(message.data);
if(signal.status !== 200) {
const video = self.createVideo();
video.poster = '../assets/streaming-error.png';
}
const streamInfoResponse = signal['streamInfo'];
let g = [];
// self.socketStream.some((f) => {
g = self.streams.filter((str) => signal['streamInfo'].streamName === str.streamName);
// if(g.length > 0) {
// return true;
// }
// });
// });
if (streamInfoResponse !== undefined) {
g[0]['sessionId'] = streamInfoResponse.sessionId;
}
console.log('Received signal');
const msgCommand = signal['command'];
console.log(msgCommand);
if (signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp));
if (signal.sdp) {
const description = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(description);
signalingConnection.send('{"direction":"play", "command":"sendResponse", "streamInfo":' +
JSON.stringify(g[0]) + ', "sdp":' + JSON.stringify(description) + ',"userData":' + JSON.stringify(self.userData) + '}');
}
// })
// .catch(self.errorHandler);
} else if (signal.iceCandidates) {
console.log('ice: ' + JSON.stringify(signal.iceCandidates));
peerConnection.addIceCandidate(new RTCIceCandidate(signal.iceCandidates[0])).catch(self.errorHandler);
}
if ('sendResponse'.localeCompare(msgCommand) === 0) {
if (signalingConnection != null) {
signalingConnection.close();
}
signalingConnection = null;
}
// };
});
signalingConnection.send('{"direction":"play", "command":"getOffer", "streamInfo":' +
JSON.stringify(ele) + ', "userData":' + JSON.stringify(self.userData) + '}');
};
signalingConnection.onclose = function (r) {
console.log('close');
};
}
private createVideo() {
const video = document.createElement('video');
video.classList.add('cameraPanel');
video.autoplay = true;
video.preload = 'true';
video.muted = true;
video.width = 300;
const remoteVideo = document.querySelector('#videoContainer') as HTMLElement;
remoteVideo.appendChild(video);
return video;
}

Related

iPhone Chrome my dispatch method not working after dispatching

I am working in Vue Js with Laravel. But specifically in iPhone Chrome my dispatch method is not working after dispatching.
Here the API call code.
ProviderProfileService.addAddress(this.$apiUrl, this.address).then(
(response) => {
let result = response.data.createServiceLocation;
if (result.status == "success") {
let steps_status = JSON.parse(
result.service_location.user.providerProfileItems.profile_steps_status
);
if (steps_status.addrestime == true) {
alert(1);
this.$store
.dispatch("update_provider_profile_status", {
providerProfileStatus: steps_status,
})
.then((res) => {
alert(2);
this.$store.dispatch("update_progress_bar").then((res1) => {
if (response.data.createServiceLocation.status == "error") {
alert(0);
self.updateAddress();
} else {
this.$router.push(
"/provider-profile/language/" + this.userConfig.unique_identifier
);
alert(3);
// this.$router.push({
// name: "Language",
// params: { id: this.userConfig.unique_identifier },
// });
alert(4);
// window.location.href =
// "https://19b0-2404-3100-1c81-8081-e0bc-c658-121c-836b.ngrok.io/provider-profile/language/" +
// this.userConfig.unique_identifier;
this.scrollToTop();
alert(5);
}
});
});
}
}
},
(error) => {
self.content =
(error.response && error.response.data) || error.message || error.toString();
}
);
Here is the dispatch method code.
state.providerProfileStatus.basicinfo = (payload.basicinfo)?true:false;
state.providerProfileStatus.rates = (payload.rates)?true:false;
state.providerProfileStatus.services = (payload.services)?true:false;
state.providerProfileStatus.addrestime = (payload.addrestime)?true:false;
state.providerProfileStatus.availability = (payload.availability)?true:false;
state.providerProfileStatus.integrations = (payload.integrations)?true:false;
state.providerProfileStatus.certifications = (payload.certifications)?true:false;
state.providerProfileStatus.payment_settings = (payload.payment_settings)?true:false;
state.providerProfileStatus.documents_and_vaccination = (payload.documents_and_vaccination)?true:false;
state.providerProfileStatus.language_and_introduction = (payload.language_and_introduction)?true:false;

Error integrating Agora.io with nuxt.js Error in created hook: "ReferenceError: AgoraRTC is not defined"

I am integrating Agora Web SDK with nuxt.js.
I have included all the methods I need and my page has the following methods and lifecycle hooks:
methods: {
streamInit(uid, attendeeMode, videoProfile, config) {
let defaultConfig = {
streamID: uid,
audio: true,
video: true,
screen: false
};
switch (attendeeMode) {
case "audio-only":
defaultConfig.video = false;
break;
case "audience":
defaultConfig.video = false;
defaultConfig.audio = false;
break;
default:
case "video":
break;
}
let stream = AgoraRTC.createStream(merge(defaultConfig, config));
stream.setVideoProfile(videoProfile);
return stream;
},
subscribeStreamEvents() {
let rt = this;
rt.client.on("stream-added", function(evt) {
let stream = evt.stream;
console.log("New stream added: " + stream.getId());
console.log("At " + new Date().toLocaleTimeString());
console.log("Subscribe ", stream);
rt.client.subscribe(stream, function(err) {
console.log("Subscribe stream failed", err);
});
});
rt.client.on("peer-leave", function(evt) {
console.log("Peer has left: " + evt.uid);
console.log(new Date().toLocaleTimeString());
console.log(evt);
rt.removeStream(evt.uid);
});
rt.client.on("stream-subscribed", function(evt) {
let stream = evt.stream;
console.log("Got stream-subscribed event");
console.log(new Date().toLocaleTimeString());
console.log("Subscribe remote stream successfully: " + stream.getId());
console.log(evt);
rt.addStream(stream);
});
rt.client.on("stream-removed", function(evt) {
let stream = evt.stream;
console.log("Stream removed: " + stream.getId());
console.log(new Date().toLocaleTimeString());
console.log(evt);
rt.removeStream(stream.getId());
});
},
removeStream(uid) {
this.streamList.map((item, index) => {
if (item.getId() === uid) {
item.close();
let element = document.querySelector("#ag-item-" + uid);
if (element) {
element.parentNode.removeChild(element);
}
let tempList = [...this.streamList];
tempList.splice(index, 1);
this.streamList = tempList;
}
});
},
addStream(stream, push = false) {
let repeatition = this.streamList.some(item => {
return item.getId() === stream.getId();
});
if (repeatition) {
return;
}
if (push) {
this.streamList = this.streamList.concat([stream]);
} else {
this.streamList = [stream].concat(this.streamList);
}
},
handleCamera(e) {
e.currentTarget.classList.toggle("off");
this.localStream.isVideoOn()
? this.localStream.disableVideo()
: this.localStream.enableVideo();
},
handleMic(e) {
e.currentTarget.classList.toggle("off");
this.localStream.isAudioOn()
? this.localStream.disableAudio()
: this.localStream.enableAudio();
},
switchDisplay(e) {
if (
e.currentTarget.classList.contains("disabled") ||
this.streamList.length <= 1
) {
return;
}
if (this.displayMode === "pip") {
this.displayMode = "tile";
} else if (this.displayMode === "tile") {
this.displayMode = "pip";
} else if (this.displayMode === "share") {
// do nothing or alert, tbd
} else {
console.error("Display Mode can only be tile/pip/share");
}
},
hideRemote(e) {
if (
e.currentTarget.classList.contains("disabled") ||
this.streamList.length <= 1
) {
return;
}
let list;
let id = this.streamList[this.streamList.length - 1].getId();
list = Array.from(
document.querySelectorAll(`.ag-item:not(#ag-item-${id})`)
);
list.map(item => {
if (item.style.display !== "none") {
item.style.display = "none";
} else {
item.style.display = "block";
}
});
},
handleExit(e) {
if (e.currentTarget.classList.contains("disabled")) {
return;
}
try {
this.client && this.client.unpublish(this.localStream);
this.localStream && this.localStream.close();
this.client &&
this.client.leave(
() => {
console.log("Client succeed to leave.");
},
() => {
console.log("Client failed to leave.");
}
);
} finally {
this.readyState = false;
this.client = null;
this.localStream = null;
// redirect to index
this.$router.push("/");
}
}
},
created() {
let $ = this;
// init AgoraRTC local client
$.client = AgoraRTC.createClient({ mode: $.transcode });
$.client.init($.appId, () => {
console.log("AgoraRTC client initialized");
$.subscribeStreamEvents();
$.client.join($.appId, $.channel, $.uid, uid => {
console.log("User " + uid + " join channel successfully");
console.log("At " + new Date().toLocaleTimeString());
// create local stream
// It is not recommended to setState in function addStream
$.localStream = this.streamInit(uid, $.attendeeMode, $.videoProfile);
$.localStream.init(
() => {
if ($.attendeeMode !== "audience") {
$.addStream($.localStream, true);
$.client.publish($.localStream, err => {
console.log("Publish local stream error: " + err);
});
}
$.readyState = true;
},
err => {
console.log("getUserMedia failed", err);
$.readyState = true;
}
);
});
});
},
mounted() {
this.$nextTick(() => {
// add listener to control btn group
let canvas = document.querySelector("#ag-canvas");
let btnGroup = document.querySelector(".ag-btn-group");
canvas.addEventListener("mousemove", () => {
if (global._toolbarToggle) {
clearTimeout(global._toolbarToggle);
}
btnGroup.classList.add("active");
global._toolbarToggle = setTimeout(function() {
btnGroup.classList.remove("active");
}, 2000);
});
});
},
beforeUpdate() {
let $ = this;
// rerendering
let canvas = document.querySelector("#ag-canvas");
// pip mode (can only use when less than 4 people in channel)
if ($.displayMode === "pip") {
let no = $.streamList.length;
if (no > 4) {
$.displayMode = "tile";
return;
}
$.streamList.map((item, index) => {
let id = item.getId();
let dom = document.querySelector("#ag-item-" + id);
if (!dom) {
dom = document.createElement("section");
dom.setAttribute("id", "ag-item-" + id);
dom.setAttribute("class", "ag-item");
canvas.appendChild(dom);
item.play("ag-item-" + id);
}
if (index === no - 1) {
dom.setAttribute("style", `grid-area: span 12/span 24/13/25`);
} else {
dom.setAttribute(
"style",
`grid-area: span 3/span 4/${4 + 3 * index}/25;
z-index:1;width:calc(100% - 20px);height:calc(100% - 20px)`
);
}
item.player.resize && item.player.resize();
});
} else if ($.displayMode === "tile") {
// tile mode
let no = $.streamList.length;
$.streamList.map((item, index) => {
let id = item.getId();
let dom = document.querySelector("#ag-item-" + id);
if (!dom) {
dom = document.createElement("section");
dom.setAttribute("id", "ag-item-" + id);
dom.setAttribute("class", "ag-item");
canvas.appendChild(dom);
item.play("ag-item-" + id);
}
dom.setAttribute("style", `grid-area: ${tile_canvas[no][index]}`);
item.player.resize && item.player.resize();
});
} else if ($.displayMode === "share") {
// screen share mode (tbd)
}
},
beforeDestroy () {
this.client && this.client.unpublish(this.localStream);
this.localStream && this.localStream.close();
this.client &&
this.client.leave(
() => {
console.log("Client succeed to leave.");
},
() => {
console.log("Client failed to leave.");
}
);
}
I have installed agora-rtc-sdk from npm.
My plugins/agora.js file looks like this
import Vue from "vue";
import AgoraRTC from 'agora-rtc-sdk';
Vue.use(AgoraRTC);
My nuxt.config.js has plugins declared as:
{
src: "~/plugins/agora.js",
ssr: false
}
The application on loading the page gives AgoraRTC is not defined. How do I add this AgoraRTC to my nuxt.js application?
Agora works only on the client side, fully independent of a server and hence you need to define the mode as client in the nuxt.config.js like this:
{ src: '~/plugins/agora.js', mode: 'client' },

Webrtc video chat not working in different networks

I am trying to implement video chat with webrtc using code from google code labs . It works fine on same network but does not work on different network. i have installed coturn on my server. I am not sure if it is working correctly.
Any one used google codelabs code and made it working?
This is the modified main.js file
'use strict';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pcConfig = {
"iceServers":[
{'urls': 'stun:stun.l.google.com:19302'},
{"urls":["turn:78.129.167.90"],"username":"wyzturner1","credential":"wyzturnp2ss"}]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
/////////////////////////////////////////////
var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');
var socket = io.connect('https://www.samplesite.com:8080');
if (room !== '') {
socket.emit('create or join', room);
console.log('Attempted to create or join room', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isChannelReady = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message);
}
// This client receives a message
socket.on('message', function(message) {
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
navigator.mediaDevices.getUserMedia({
audio: false,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
function gotStream(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
var constraints = {
video: true
};
console.log('Getting user media with constraints', constraints);
//if (location.hostname !== 'localhost') {
// requestTurn(
// 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
// );
//}
function maybeStart() {
console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
console.log('>>>>>> creating peer connection');
createPeerConnection();
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function() {
sendMessage('bye');
};
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(pcConfig);
pc.addStream(localStream);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.oniceconnectionstatechange = function(){
console.log('ICE state: ',pc.iceConnectionState);
}
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('icecandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log('End of candidates.');
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event) {
console.log('createOffer() error: ', event);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer().then(
setLocalAndSendMessage,
onCreateSessionDescriptionError
);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
// sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message', sessionDescription);
sendMessage(sessionDescription);
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function requestTurn(turnURL) {
var turnExists = false;
for (var i in pcConfig.iceServers) {
if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
turnExists = true;
turnReady = true;
break;
}
}
// if (!turnExists) {
// console.log('Getting TURN server from ', turnURL);
// // No TURN server. Get one from computeengineondemand.appspot.com:
// var xhr = new XMLHttpRequest();
// xhr.onreadystatechange = function() {
// if (xhr.readyState === 4 && xhr.status === 200) {
// var turnServer = JSON.parse(xhr.responseText);
// console.log('Got TURN server: ', turnServer);
// pcConfig.iceServers.push({
// 'url': 'turn:' + turnServer.username + '#' + turnServer.turn,
// 'credential': turnServer.password
// });
// turnReady = true;
// }
// };
// xhr.open('GET', turnURL, true);
// xhr.send();
// }
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
console.log('Session terminated.');
stop();
isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length - 1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}

Trello API add base64 data image as an attachement

I'd like to send base64 image as an attachment to a trello card through the API
POST /1/cards/[card id or shortlink]/attachments
There's a file field but it does not specify how should look the data there.
Refs: https://developers.trello.com/advanced-reference/card#post-1-cards-card-id-or-shortlink-attachments
Any idea?
Short answer: Trello's API only works to attach binary data via multipart/form-data. Examples below.
Long answer:
The Trello API to add attachments and images is frustratingly under-documented. They do have a coffeescript Client.js for those of us using Javascript to simplify all the basic operations: https://trello.com/1/client.coffee
Using the vanilla Client.js file I have been able to attach links and text files. While the CURL example shows pulling a binary file in from a hard drive, that doesn't work for those of us completely on a server or client where we don't have permissions to create a file.
From a LOT of trial and error, I've determined all binary data (images, documents, etc.) must be attached using multipart/form-data. Here is a jQuery snippet that will take a URL to an item, get it into memory, and then send it to Trello.
var opts = {'key': 'YOUR TRELLO KEY', 'token': 'YOUR TRELLO TOKEN'};
var xhr = new XMLHttpRequest();
xhr.open('get', params); // params is a URL to a file to grab
xhr.responseType = 'blob'; // Use blob to get the file's mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData(); // this is the formdata Trello needs
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
I have submitted a new coffeescript for Trello developer support to consider uploading to replace Client.js. It adds a "Trello.upload(url)" that does this work.
I've also attached here for convenience in JS form.
// Generated by CoffeeScript 1.12.4
(function() {
var opts={"version":1,"apiEndpoint":"https://api.trello.com","authEndpoint":"https://trello.com"};
var deferred, isFunction, isReady, ready, waitUntil, wrapper,
slice = [].slice;
wrapper = function(window, jQuery, opts) {
var $, Trello, apiEndpoint, authEndpoint, authorizeURL, baseURL, collection, fn, fn1, i, intentEndpoint, j, key, len, len1, localStorage, location, parseRestArgs, readStorage, ref, ref1, storagePrefix, token, type, version, writeStorage;
$ = jQuery;
key = opts.key, token = opts.token, apiEndpoint = opts.apiEndpoint, authEndpoint = opts.authEndpoint, intentEndpoint = opts.intentEndpoint, version = opts.version;
baseURL = apiEndpoint + "/" + version + "/";
location = window.location;
Trello = {
version: function() {
return version;
},
key: function() {
return key;
},
setKey: function(newKey) {
key = newKey;
},
token: function() {
return token;
},
setToken: function(newToken) {
token = newToken;
},
rest: function() {
var args, error, method, params, path, ref, success;
method = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = parseRestArgs(args), path = ref[0], params = ref[1], success = ref[2], error = ref[3];
opts = {
url: "" + baseURL + path,
type: method,
data: {},
dataType: "json",
success: success,
error: error
};
if (!$.support.cors) {
opts.dataType = "jsonp";
if (method !== "GET") {
opts.type = "GET";
$.extend(opts.data, {
_method: method
});
}
}
if (key) {
opts.data.key = key;
}
if (token) {
opts.data.token = token;
}
if (params != null) {
$.extend(opts.data, params);
}
if (method === 'UPLOAD' && typeof (params) === "string" && params.length > 5) {
var xhr = new XMLHttpRequest();
xhr.open('get', params);
xhr.responseType = 'blob'; // Use blob to get the mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData();
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
} else {
return $.ajax(opts);
}
},
authorized: function() {
return token != null;
},
deauthorize: function() {
token = null;
writeStorage("token", token);
},
authorize: function(userOpts) {
var k, persistToken, ref, regexToken, scope, v;
opts = $.extend(true, {
type: "redirect",
persist: true,
interactive: true,
scope: {
read: true,
write: false,
account: false
},
expiration: "30days"
}, userOpts);
regexToken = /[&#]?token=([0-9a-f]{64})/;
persistToken = function() {
if (opts.persist && (token != null)) {
return writeStorage("token", token);
}
};
if (opts.persist) {
if (token == null) {
token = readStorage("token");
}
}
if (token == null) {
token = (ref = regexToken.exec(location.hash)) != null ? ref[1] : void 0;
}
if (this.authorized()) {
persistToken();
location.hash = location.hash.replace(regexToken, "");
return typeof opts.success === "function" ? opts.success() : void 0;
}
if (!opts.interactive) {
return typeof opts.error === "function" ? opts.error() : void 0;
}
scope = ((function() {
var ref1, results;
ref1 = opts.scope;
results = [];
for (k in ref1) {
v = ref1[k];
if (v) {
results.push(k);
}
}
return results;
})()).join(",");
switch (opts.type) {
case "popup":
(function() {
var authWindow, height, left, origin, receiveMessage, ref1, top, width;
waitUntil("authorized", (function(_this) {
return function(isAuthorized) {
if (isAuthorized) {
persistToken();
return typeof opts.success === "function" ? opts.success() : void 0;
} else {
return typeof opts.error === "function" ? opts.error() : void 0;
}
};
})(this));
width = 420;
height = 470;
left = window.screenX + (window.innerWidth - width) / 2;
top = window.screenY + (window.innerHeight - height) / 2;
origin = (ref1 = /^[a-z]+:\/\/[^\/]*/.exec(location)) != null ? ref1[0] : void 0;
authWindow = window.open(authorizeURL({
return_url: origin,
callback_method: "postMessage",
scope: scope,
expiration: opts.expiration,
name: opts.name
}), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
receiveMessage = function(event) {
var ref2;
if (event.origin !== authEndpoint || event.source !== authWindow) {
return;
}
if ((ref2 = event.source) != null) {
ref2.close();
}
if ((event.data != null) && /[0-9a-f]{64}/.test(event.data)) {
token = event.data;
} else {
token = null;
}
if (typeof window.removeEventListener === "function") {
window.removeEventListener("message", receiveMessage, false);
}
isReady("authorized", Trello.authorized());
};
return typeof window.addEventListener === "function" ? window.addEventListener("message", receiveMessage, false) : void 0;
})();
break;
default:
window.location = authorizeURL({
redirect_uri: location.href,
callback_method: "fragment",
scope: scope,
expiration: opts.expiration,
name: opts.name
});
}
},
addCard: function(options, next) {
var baseArgs, getCard;
baseArgs = {
mode: 'popup',
source: key || window.location.host
};
getCard = function(callback) {
var height, left, returnUrl, top, width;
returnUrl = function(e) {
var data;
window.removeEventListener('message', returnUrl);
try {
data = JSON.parse(e.data);
if (data.success) {
return callback(null, data.card);
} else {
return callback(new Error(data.error));
}
} catch (error1) {}
};
if (typeof window.addEventListener === "function") {
window.addEventListener('message', returnUrl, false);
}
width = 500;
height = 600;
left = window.screenX + (window.outerWidth - width) / 2;
top = window.screenY + (window.outerHeight - height) / 2;
return window.open(intentEndpoint + "/add-card?" + $.param($.extend(baseArgs, options)), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
};
if (next != null) {
return getCard(next);
} else if (window.Promise) {
return new Promise(function(resolve, reject) {
return getCard(function(err, card) {
if (err) {
return reject(err);
} else {
return resolve(card);
}
});
});
} else {
return getCard(function() {});
}
}
};
ref = ["GET", "PUT", "POST", "DELETE", "UPLOAD"];
fn = function(type) {
return Trello[type.toLowerCase()] = function() {
return this.rest.apply(this, [type].concat(slice.call(arguments)));
};
};
for (i = 0, len = ref.length; i < len; i++) {
type = ref[i];
fn(type);
}
Trello.del = Trello["delete"];
ref1 = ["actions", "cards", "checklists", "boards", "lists", "members", "organizations", "lists"];
fn1 = function(collection) {
return Trello[collection] = {
get: function(id, params, success, error) {
return Trello.get(collection + "/" + id, params, success, error);
}
};
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
collection = ref1[j];
fn1(collection);
}
window.Trello = Trello;
authorizeURL = function(args) {
var baseArgs;
baseArgs = {
response_type: "token",
key: key
};
return authEndpoint + "/" + version + "/authorize?" + $.param($.extend(baseArgs, args));
};
parseRestArgs = function(arg) {
var error, params, path, success;
path = arg[0], params = arg[1], success = arg[2], error = arg[3];
if (isFunction(params)) {
error = success;
success = params;
params = {};
}
path = path.replace(/^\/*/, "");
return [path, params, success, error];
};
localStorage = window.localStorage;
if (localStorage != null) {
storagePrefix = "trello_";
readStorage = function(key) {
return localStorage[storagePrefix + key];
};
writeStorage = function(key, value) {
if (value === null) {
return delete localStorage[storagePrefix + key];
} else {
return localStorage[storagePrefix + key] = value;
}
};
} else {
readStorage = writeStorage = function() {};
}
};
deferred = {};
ready = {};
waitUntil = function(name, fx) {
if (ready[name] != null) {
return fx(ready[name]);
} else {
return (deferred[name] != null ? deferred[name] : deferred[name] = []).push(fx);
}
};
isReady = function(name, value) {
var fx, fxs, i, len;
ready[name] = value;
if (deferred[name]) {
fxs = deferred[name];
delete deferred[name];
for (i = 0, len = fxs.length; i < len; i++) {
fx = fxs[i];
fx(value);
}
}
};
isFunction = function(val) {
return typeof val === "function";
};
wrapper(window, jQuery, opts);
}).call(this);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>

Knockout JS - foreach: data-bind not get refreshed

SPEC : Building a customer support application.
First time, when a user send a message able to save to DB and show in the view without any issues. And from my end, if i send I am able to push the data to the UI as well DB and the user is able to get the message.
But the issue is when the user send back response again, its not showing up refreshing the UI.
Below is my knockout code,
var csendpoints = {
"Active": "/cs/oc/",
"ActiveClientData": "/cs/gnm/",
"Archive": "/cw/archive/",
"Dequeue": function (id) { return "/cw/dequeue/" + id; },
"GetQueue": "/cs/q/",
"GetArchive": "/cs/h/",
"GetActive": "/cs/active/",
"RecentlyQueued": "/cs/rq/",
"Send": "/cs/send",
"SitRep": "/cw/sitrep",
};
var customerSupportViewModel;
function PagedViewModels(model) {
var self = this;
self.Items = ko.observableArray(model.Items || []);
self.MetaData = ko.observable(model.MetaData || "");
}
function PagedListMetaDataViewModel(model) {
var self = this;
self.FirstItemOnPage = ko.observable(model.FirstItemOnPage || "");
self.HasNextPage = ko.observable(model.HasNextPage || "");
self.HasPreviousPage = ko.observable(model.HasPreviousPage || "");
self.IsFirstPage = ko.observable(model.IsFirstPage || "");
self.IsLastPage = ko.observable(model.IsLastPage || "");
self.LastItemOnPage = ko.observable(model.LastItemOnPage || "");
self.PageCount = ko.observable(model.PageCount || "");
self.PageNumber = ko.observable(model.PageNumber || "");
self.PageSize = ko.observable(model.PageSize || "");
self.TotalItemCount = ko.observable(model.TotalItemCount || "");
}
function ConversationMessageViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientContent = ko.observable(model.ClientContent || "");
self.Content = ko.observable(model.Content || "");
self.ConversationMessageId = ko.observable(model.ConversationMessageId || "");
self.dummy = ko.observable();
self.Sender = ko.observable(model.Sender || "");
self.SentOn = ko.observable(model.SentOn || "");
self.SentOnUniversalSortable = ko.observable(model.SentOnUniversalSortable || "");
self.SentTimeFromNow = ko.computed(function () {
self.dummy();
return moment.utc(self.SentOnUniversalSortable()).fromNow();
});
function updateTimeFromNow() {
setTimeout(function () {
self.dummy.notifySubscribers();
updateTimeFromNow();
},
60 * 1000);
}
updateTimeFromNow();
}
function ConversationViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientNameInitial = ko.observable(model.ClientName.substring(0, 1).toUpperCase());
self.ClientContent = ko.observable(model.ClientContent || "");
self.ConversationId = ko.observable(model.ConversationId || "");
self.CreatedOn = ko.observable(model.CreatedOn || "");
self.MessagePreview = ko.observable(model.MessagePreview || "");
self.Messages = ko.observableArray([]);
self.ProfileImageUrl = ko.observable(model.ProfileImageUrl || "");
self.Sender = ko.observable(model.Sender || "");
self.SupportName = ko.observable(customerSupportViewModel.CustomerSupportName());
self.TextBoxContent = ko.observable("");
self.TextBoxEnabled = ko.observable(true);
self.MakeActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
customerSupportViewModel.CurrentConversations.push(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
};
self.MakeRemoveMultiActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
//customerSupportViewModel.CurrentConversations.push(self);
//customerSupportViewModel.CurrentConversations.remove(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
$("#chat-wrap").mCustomScrollbar("destroy");
$("#chat-wrap").height($(window).height() - 314);
$("#chat-wrap")
.mCustomScrollbar({
axis: "y",
scrollEasing: "linear",
scrollButtons: { enable: true },
theme: "dark-thick",
scrollbarPosition: "inside",
mouseWheelPixels: 400
});
initMap()
};
self.Archive = function () {
customerSupportViewModel.ArchivedConversations.push(self);
customerSupportViewModel.ActiveConversation('');
customerSupportViewModel.CurrentConversations.remove(self);
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.RefreshWaiting();
customerSupportViewModel.RefreshActive();
customerSupportViewModel.RefreshArchive();
$.ajax(csendpoints.Archive + self.ConversationId(),
{
success: console.log("Debug: Now Archived")
});
};
self.SendMessage = function (data) {
self.TextBoxEnabled(false);
var msj = {
"SentOn": moment.utc().format(),
"SentOnUniversalSortable": moment().format(),
"Sender": 1,
"SenderName": customerSupportViewModel.CustomerSupportName(),
"Content": self.TextBoxContent(),
"ClientId": self.ClientId(),
"ClientName": self.ClientName()
};
self.Messages.push(new ConversationMessageViewModels(msj));
$.ajax({
type: "POST",
url: csendpoints.Send,
data: $(data).serialize(),
success: function () { },
complete: function () {
self.TextBoxContent("");
self.TextBoxEnabled(true);
}
});
};
_.each(model.Messages, function (item) {
self.Messages.push(new ConversationMessageViewModels(item));
});
var sortedMessages = _.sortBy(self.Messages(),
function (iteratedItem) { return iteratedItem.SentOnUniversalSortable(); });
self.Messages(sortedMessages);
}
function CustomerSupportViewModel(model) {
var self = this;
customerSupportViewModel = self;
function getWaitingList(page) {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data) {
self.WaitingList.removeAll();
_.each(data.Items, function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
self.WaitingListPaging(data.MetaData);
}
});
}
function getActiveList(page) {
$.ajax(csendpoints.GetActive + page,
{
success: function (data) {
self.CurrentConversations.removeAll();
_.each(data.Items, function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
self.CurrentConversationsPaging(data.MetaData);
}
});
}
function getArchiveList(page) {
$.ajax(csendpoints.GetArchive + page,
{
success: function (data) {
self.ArchivedConversations.removeAll();
_.each(data.Items, function (item) {
self.ArchivedConversations.push(new ConversationViewModels(item));
});
self.ArchivedConversations(data.MetaData);
}
});
}
// Conversations in Queue(i.e. waiting), Current(i.e. active) and Archive (i.e. history)
self.WaitingList = ko.observableArray([]);
self.ArchivedConversations = ko.observableArray([]);
self.CurrentConversations = ko.observableArray([]);
// Paging models
self.WaitingListPaging = ko.observable(model.ConversationsInQueue.MetaData);
self.ArchiveListPaging = ko.observable(model.ArchivedConversations.MetaData);
self.CurrentConversationsPaging = ko.observable(model.CurrentConversations.MetaData);
// The conversation currently active.
self.ActiveConversation = ko.observable();
//setInterval(self.ActiveConversation, 500);
// The name of the WeChatAccount serving the client
self.CustomerSupportName = ko.observable(model.CustomerSupportName);
self.RefreshWaiting = function () {
var page = self.WaitingListPaging().PageNumber;
getWaitingList(page);
};
self.RefreshActive = function () {
var page = self.CurrentConversationsPaging().PageNumber;
getActiveList(page);
//getWaitingList(page);
};
self.RefreshArchive = function () {
var page = self.ArchiveListPaging().PageNumber;
//getArchiveList(page);
getWaitingList(page);
};
//self.RefreshChatActive = function () {
// getActiveList()
//};
// ###### PAGING for Waiting / Queue ######
// Forward paging for the queue / waiting list
self.NextPageWaitingList = function () {
if (self.WaitingListPaging().IsLastPage) return;
var page = self.WaitingListPaging().PageNumber + 1;
getWaitingList(page);
};
self.PreviousPageWaitingList = function (data, e) {
//console.log($(data))
//console.log((e.target) ? e.target : e.srcElement)
if (self.WaitingListPaging().IsFirstPage) return;
var page = self.WaitingListPaging().PageNumber - 1;
getWaitingList(page);
};
// ###### PAGING for Active / Current conversations ######
self.NextPageActiveList = function () {
if (self.CurrentConversations().IsLastPage) return;
var page = self.CurrentConversations().PageNumber + 1;
getActiveList(page);
};
self.PreviousPageActiveList = function () {
if (self.CurrentConversations().IsFirstPage) return;
var page = self.CurrentConversations().PageNumber - 1;
getActiveList(page);
};
// ###### PAGING for History / Archived conversations ######
self.NextPageArchiveList = function () {
if (self.ArchivedConversations().IsLastPage) return;
var page = self.ArchivedConversations().PageNumber + 1;
getArchiveList(page);
};
self.PreviousPageArchiveList = function (data, e) {
if (self.ArchivedConversations().IsFirstPage) return;
var page = self.ArchivedConversations().PageNumber - 1;
getArchiveList(page);
};
_.each(model.ConversationsInQueue.Items,
function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
_.each(model.CurrentConversations.Items,
function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
_.each(model.ArchivedConversations.Items,
function (item) { self.ArchivedConversations.push(new ConversationViewModels(item)); });
function getNewQueue(page) {
setTimeout(function () {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data, odata) { getNewQueue(); }
});
},
15 * 1000);
}
}
var customerSupportViewModel = new CustomerSupportViewModel(jsonViewModel);
window.setInterval(customerSupportViewModel.RefreshWaiting, 1000);
window.setInterval(customerSupportViewModel.RefreshActive, 200);
window.setInterval(customerSupportViewModel.Messages, 200);
ko.applyBindings(customerSupportViewModel);
var conversationMessageViewModels = new ConversationMessageViewModels(jsonViewModel);
Here MakeRemoveMultiActive is used as a click function, and in the view, it load the data what user sent as well the response sent from my end. Note, this happens only on click event.
I want to update the UI after the click event so the view is open with the data and autoupdates.
Request experts to shed some light.