I want to add a text chat system in a-frame. I'm using networked a-frame as connectivity can be anything possible with that or I've to do using some other tech.
There is a simple API to send custom messages with the network layer that is already setup:
// subscribe to custom data
NAF.connection.subscribeToDataChannel(dataType, (senderId, dataType, data, targetId) => {})
// send custom data
NAF.connection.broadcastData(dataType, data)
So sending an chat message is as simple as:
const btn = document.querySelector("button"); // SEND btn
const input = document.querySelector("input"); // input field with the text
const log = document.querySelector("#messages") // message log
// when you want to send a message
btn.addEventListener("click", evt => {
// log your own messages
messages.innerHTML += NAF.clientId + ": " + input.value + '<br>'
// broadcast the text as some unique dataType (like "chat")
NAF.connection.broadcastData("chat", {txt: input.value})
})
// when a "chat" type message arrives
NAF.connection.subscribeToDataChannel("chat", (senderId, dataType, data, targetId) => {
// append the data.txt to the message log
messages.innerHTML += senderId + ": " + data.txt + '<br>'
})
Check it out in this glitch:
Related
I am creating multiple consumers in the loop by which I am able to listen to multiple queues. The issue in this approach is I am able to get the events from the different queues but all the queues are using the same consumer so it is hard to recognize for which queue this event happens. It will be good if I will get the queue name under the consumer section.
consumer.Received += async (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
};
The ea variable has some interesting fields, have you check that?
ea.Exchange shows this message has published from which exchange.
ea.RoutingKey shows the route info of the message. probably have the queue name in it.
Also, you can put your headers in the message when your are defining them.
IBasicProperties props = channel.CreateBasicProperties();
props.Headers.Add("queueName", "myQueue1");
channel.BasicPublish(exchangeName,
routingKey, props,
messageBodyBytes);
and in the consumer function you can read them :
consumer.Received += async (model, ea) =>
{
var name = ea.BasicProperties.Headers["queueName"];
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
};
A solution is using "consumerTag" in channel.BasicConsume
channel.BasicConsume(..., consumerTag: "YourQueueName");
And then you can retrieve the queue name in message listner:
consumer.Received += async (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var queueName = ea.ConsumerTag;
};
Warning: In the case that ConsumerTag is important to your system, do not use this solution.
There is no queue field on the message headers or fields, based on my re-search of message object.
The only fields related to queue name are from dead-lettered messages, which save in the headers object of properties the x-first-death-queue.
I've customized Document Management System template in Appmaker as per my needs. Now instead of going to Appmaker every time to initiate an approval I want to provide functionality to initiate the workflow from Google Drive.So users can select file for Approval directly from Google Drive.
My question is is there any Rest call or something via which I can initiate DMS workflow from Third party app?
Well I found a way out to achieve the result.
Steps:
Drive API provides a way to add your App in 'Open With' menu in Google Drive.
So I've created my custom app and deployed it. This app will simply receive params from Google Drive 'Open with' menu and pass it to Appmaker Document Approval System.
In Appmaker Create request page parse if request contains these params, if yes then select files using these params.
This way my users can initiate the Document Approval Workflow from Google Drive.
References :
How to add/list your App in Google Drive
Step by Step video guideline to Create and publish your App
Code:
'Open With' App code for redirecting users from Google Drive to Appmaker.
code.gs:
var AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth';
var TOKEN_URL = 'https://accounts.google.com/o/oauth2/token';
var REDIRECT_URL= ScriptApp.getService().getUrl();
var tokenPropertyName = 'GOOGLE_OAUTH_TOKEN';
var CLIENT_ID = 'your client id';
var CLIENT_SECRET = 'your client secrect';
function doGet(e) {
var HTMLToOutput;
if(e.parameters.state){
var state = JSON.parse(e.parameters.state);
if(state.action === 'create'){
HTMLToOutput = "<html><h1>Creation Of Docs Not supported by this App!</h1></html>";
}
else {
var id = state.exportIds;
var file = DriveApp.getFileById(id);
//append params to your appmaker URL
var url = 'yourappmaker published url'+'?param1='+file.getName()+'¶m2='+file.getUrl()+'#AddRequest';
HTMLToOutput = HtmlService.createHtmlOutput('<html><script>'
+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
+'if(document.createEvent){'
+' var event=document.createEvent("MouseEvents");'
+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
+' event.initEvent("click",true,true); a.dispatchEvent(event);'
+'}else{ a.click() }'
+'close();'
+'</script>'
// Offer URL as clickable link in case above code fails.
+'<body style="word-break:break-word;font-family:sans-serif;">Failed to open automatically. Click here to proceed.</body>'
+'<script>google.script.host.setHeight(40);google.script.host.setWidth(410)</script>'
+'</html>')
.setWidth( 90 ).setHeight( 1 );
}
}
else if(e.parameters.code){//if we get "code" as a parameter in, then this is a callback. we can make this more explicit
getAndStoreAccessToken(e.parameters.code);
HTMLToOutput = '<html><h1>App is installed, you can close this window now or navigate to your Google Drive.</h1></html>';
}
else {//we are starting from scratch or resetting
HTMLToOutput = "<html><h1>Install this App into your Google Drive!</h1><a href='"+getURLForAuthorization()+"'>click here to start</a></html>";
}
console.log(getURLForAuthorization());
return HtmlService.createHtmlOutput(HTMLToOutput);
}
function getURLForAuthorization(){
return AUTHORIZE_URL + '?response_type=code&client_id='+CLIENT_ID+'&redirect_uri='+REDIRECT_URL +
'&scope=https://www.googleapis.com/auth/drive.install https://www.googleapis.com/auth/userinfo.email';
}
function getAndStoreAccessToken(code){
var parameters = { method : 'post',
payload : 'client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&grant_type=authorization_code&redirect_uri='+REDIRECT_URL+'&code=' + code};
var response = UrlFetchApp.fetch(TOKEN_URL,parameters).getContentText();
var tokenResponse = JSON.parse(response);
UserProperties.setProperty(tokenPropertyName, tokenResponse.access_token);
}
function getUrlFetchOptions() {
return {'contentType' : 'application/json',
'headers' : {'Authorization' : 'Bearer ' + UserProperties.getProperty(tokenPropertyName),
'Accept' : 'application/json'}};
}
//naive check, not using for now, use refresh tokens and add proper checking
function isTokenValid() {
return UserProperties.getProperty(tokenPropertyName);
}
In Document workflow 'Create Request' page, add event to onAttach() method. Write below function,
//client side
function checkIfRedirected(widget)
{
// console.log(location.origin);
google.script.url.getLocation(function(location) {
var params = location.parameter;
var param1 = params.param1;
var param2 = params.param2;
widget.datasource.item.DocumentName = param1;
widget.datasource.item.DocumentUrl = param2;
widget.datasource.item.Owner = app.user.email;
});
}
I am working on one project where we are using search bar to search data from server and for that we are calling API on every character type.
Due to that if user types 10-20 characters in search bar it will call 20 requests.
Among 19 request no required at all so I want to cancel all perior request from advanced HTTP plugin in ionic 3.
Is there any way to cancel that request which is already in process ?
I am using below code for HTTP call:
this.httpPlugin.setRequestTimeout(60);
// this.httpPlugin.setHeader("content-type", "application/json");
this.httpPlugin.setHeader('authorization', "Bearer " + token);
console.log('Requested URL :-', this.url + 'faqmsDetails/faList?caseNum=' + searchText);
this.httpPlugin.get(this.url + searchText, {}, {}).then((response) => {
console.log("Response Success : " + JSON.stringify(response));
let jsonResponse = JSON.parse(response.data);
console.log("JSON OBJECT RESPONSE : " + jsonResponse);
resolve(jsonResponse);
}).catch(error => {
reject(error);
});
I search a lot but didn't find any fruitful solution.
for Search delay i have used debounce property of searchebar which will help me to delay in search while user typing but still some request are unwanted in process so I want to cancel that requests.
Let me know if any one have any solution or suggestions.
debounce is what you need in the auto-search case. The tail input will be considered as the confirmed input, exactly same as submit button clicking, even though it turns out be unwanted eventually.
I'm not familiar with the httpPlugin underneath. If based on XMLHttpRequest, it is cancelable. If based on fetch API, it is not cancelable. Anyway you can cast the unwanted requests away.
One much more graceful solution is to introduce rxjs. See https://github.com/Reactive-Extensions/RxJS/blob/master/examples/autocomplete/autocomplete.js
Update:
Got stuck with controlling these event streams. I think rxjs is worth in this case.
<!doctype html>
<html>
<head>
<script src="https://cdn.bootcss.com/rxjs/5.4.3/Rx.js"></script>
</head>
<body>
<input />
<button>clear</button>
<span id="result"></span>
<script>
function searchData(keyword) {
console.log('to search: ' + keyword)
return new Promise(resolve => {
if (keyword) {
setTimeout(() => resolve('result of ' + keyword), 2000)
} else {
resolve('cleared')
}
})
}
const input = document.querySelector('input')
const clear = document.querySelector('button')
const result = document.getElementById('result')
const input$ = Rx.Observable.fromEvent(input, 'keyup')
.map(e => e.target.value)
.debounceTime(300)
const clear$ = Rx.Observable.fromEvent(clear, 'click')
.map(() => {
input.value = ''
return ''
})
const search$ = Rx.Observable.merge(input$, clear$)
.distinctUntilChanged()
.switchMap(searchData)
.subscribe(data => {
result.innerHTML = data
console.log(data)
})
</script>
</body>
</html>
I am trying to setup an email notification and my hope is to have a simple list of all the items in a datasource (separated into their fields).
Ex:
ItemName01, Cost01, Quantity01
ItemName02, Cost02, Quantity02
ItemName03, Cost03, Quantity03
Doing a projection of each (#datasources.Datasource.items..ItemName + #datasources.Datasource.items..Cost + #datasources.Datasource.items..Quantity) gives me everything, but not organized correctly.
Ex. [ItemName01,ItemName02,ItemName03],[Cost01,Cost02,Cost03],[Quantity01,Quantity02,Quantity03]
Any help/thoughts are appreciated.
Thanks!
I would recommend to use server script for this:
// query records
var records = app.models.Item.newQuery().run();
// generate email HTML body
var emailBody = records.reduce(function(str, item) {
str += '<p>' + item.Name + ', ' + item.Cost + ', ' + item.Quantity + '</p>'
});
// hand off generated HTML to other function
// that will actually send email
sendEmail(emailBody);
You can call this Server Script from Model Events or explicitly from client using google.script.run. You also can pass some filters to narrow records set to be sent.
I am using Firebase to develop an HTML5 mobile messaging app. I encountered an issue that I am unable to resolve. The app has multiple channels (chat rooms). When a message is added for the first time to a channel it works as expected but when I go to a different channel and post an new message to that channel then I return to the previous channel and post another message I get duplicates of the last posted message. When I reload the page the duplicates are gone but I'd prefer not to have duplicates showing at all. Below is my code:
function loadChatMessages(channelID) {
$('#chatMessages').html('');
var msgObj = {};
var channelRef = globals.channelsBase + '/' + channelID + '/messages';
var channelMessages = new Firebase(channelRef);
channelMessages.on('child_added', function (snapshot) {
msgObj = snapshot.val();
var id = snapshot.name().toString();
var messageTime = application.Functions.renderTime(msgObj.messageTime);
var tails = '<div class="message-tails-wrap"><div class="message-tails"></div></div>';
var html = '<li class="chatEl ' + sentByClass + '" id="'+id+'">';
html += tails;
html += msgObj.message;
html += '<span class="sender"> ' + by + ' </span> <span class="tmp-recipient"> ' + msgObj.recipient + ' </span>';
html += '<span class="time-stamp msg-time" >';
html += messageTime;
html += '</span></li>';
$(html).appendTo('#chatMessages');
/// TODO: TEMP solution!
var prevID = 0;
$('#chatMessages li').each(function(n) {
var id = $(this).attr('id');
if(id == prevID){
// console.log(id + ' is a duplicate. Remove it');
this.remove(); // the necessary evil....
}
prevID = id;
});
});
}
It sounds like you run loadChatMessages() every time you enter a room, and as your users move around rooms, you're seeing duplicate calls to your on('child_added' callback.
This is because you're adding a new callback every time you re-enter a room. To resolve this, do some clean up when your users leave a room. In that clean up function, make sure you remove the old listener using .off("child_added").
The event :
YOURREF.limitToLast(1).on('child_added', function (OBJECTADDED){
console.log(OBJECTADDED.key(), OBJECTADDED.val());
});
You may think the above code will e fired one time, this is not true. The above console.log will be fired twice, once with OBJECTADDED as the last object in the reference YOURREF before adding the new one, and another with the new one.