I am using converse.js to provide chat functionality. I am looking for a way to add reply to specific chat room message. any one know how can i do that?
for example:
in group user1 send: hello
and user2 wants reply to this message and say hello to
my initial code:
<script>
converse.initialize({
authentication: 'login',
//
auto_login: true,
allow_logout: false,
show_client_info: false,
allow_adhoc_commands: false,
allow_contact_requests: false,
hidden_occupants: true,
blacklisted_plugins: [
'converse-register',
'converse-rosterview',
'converse-bookmarks',
'converse-profile',
],
jid: "person#example.com",
password: "somepassword",
auto_join_rooms: [
{
'jid': 'group#conference.example.com',
'nick': 'myname',
'name': 'title',
'minimized': true
},
],
//
auto_reconnect: true,
bosh_service_url: 'https://example.com:7443/http-bind/',
message_archiving: 'always',
view_mode: 'fullscreen'
});
</script>
thanks all.
Finally I find way to solve this problem.
first in convers.js/src/plugins/chatview/view.js add this before onMessageEditButtonClicked:
onMessageReplyButtonClicked (message) {
const currently_correcting = this.model.messages.findWhere('correcting');
const unsent_text = this.el.querySelector('.chat-textarea')?.value;
if (unsent_text && (!currently_correcting || currently_correcting.get('message') !== unsent_text)) {
if (!confirm(__('You have an unsent message which will be lost if you continue. Are you sure?'))) {
return;
}
}
this.insertIntoTextArea(u.prefixMentions(message, true), true, false);
},
and in convers.js/src/components/message-actions.js file add below code before onMessageEditButtonClicked
onMessageReplyButtonClicked(ev) {
ev.preventDefault();
this.chatview.onMessageReplyButtonClicked(this.model);
}
and in convers.js/src/headless/utils/cores.js change the u.prefixMentions function to:
u.prefixMentions = function (message, reply = false) {
/* Given a message object, return its text with # chars
* inserted before the mentioned nicknames.
*/
let text = message.get('message');
(message.get('references') || [])
.sort((a, b) => b.begin - a.begin)
.forEach(ref => {
text = `${text.slice(0, ref.begin)}#${text.slice(ref.begin)}`
});
if (reply){
const lines = text.split('\n');
let newtxt = ""
for(let i = 0;i < lines.length;i++){
if(!lines[i].startsWith(">")){
newtxt += "> " + lines[i] + "\n"
}
}
return "> reply to " + message.get('nick') + ":\n" + newtxt
}
else
return text;
};
Related
index.js
const messageContainer = document.querySelector('.msg');
const append = (message, position) => {
console.log(message)
const messageElement = document.createElement('div');
messageElement.innerText = message;
messageElement.classList.add('message');
messageElement.classList.add(position);
console.log(messageElement)
messageContainer.append(messageElement);
console.log(messageContainer)
};
const SERVER = "http://localhost:3010";
var socket = io(SERVER);
socket.on('receive_message', (message) => {
console.log('Connected');
console.log(message.content);
setRMsg(message);
console.log(rmsg)
// append(`${message ? message.name : ''}: ${message ? message.content : ''}`, 'right');
// // if (message.sendBy === 'user') {
// append(`${message.content} `, 'left');
// };
});
console.log(rmsg);
if (rmsg && rmsg != '') {
append(`${rmsg.content} `, 'left');
setRMsg('')
}
const send = () => {
console.log('*95')
console.log(sending)
if (sending === '' || sending.senderChatID === '' || sending.content === '' || id === '') {
console.log('***')
toast.error('Missing Required Field', {
position: "top-right",
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
});
}
else {
let obj = {
senderChatID: sending.senderChatID,
receiverChatID: id,
content: sending.content,
sendBy: sendVia,
chatId: id,
name: name ? name.name : '',
profile_pic: name ? name.profile_pic : '',
role: name ? name.role : '',
createdAt: new Date(Date.now()),
user_id: id
};
append(`${name ? name.name : ''}: ${sending.content}`, 'right');
const input = document.getElementById('messageInp');
input.value = '';
socket.emit('send_message', obj);
}
'recieve_message' event is hitting multiple times but it must hit single time whereas on click of button send function works fine but while recieving message it gets hit multiple time don't know why as i am new to socket.io may be I was doing small mistake. Any help will be appreciated
You're probably re-registering the socket.on(...) at each re-render of your React component. Get that code inside a useEffect with [] array to do that just one time like so :-
const socketObj = useRef(io("http://localhost:3010"));
const socket = socketObj.current;
useEffect(()=>{
socket.on('receive_message', (message) => {
console.log('Connected');
console.log(message.content);
setRMsg(message);
console.log(rmsg)
// append(`${message ? message.name : ''}: ${message ? message.content : ''}`, 'right');
// // if (message.sendBy === 'user') {
// append(`${message.content} `, 'left');
// };
});
},[]);
Here I have used socket as a ref since I believe it won't change once initialized and participate in the render flow but you can even make it as a state as well if you need.
I have a project that I want to send an SMS to a list of contacts that I have.
unfortunately, I have no idea how to do this and everything I try just does not seem to work.
I want to iterate through all the numbers in the array and then send an SMS with the IONIC SMS Plugin to these numbers.
Here is the Component that stores that contacts
constructor(private contacts: Contacts, private storage: Storage) {
this.getContact();
}
key = 'contacts';
mContacts = [
{
id: [0],
name: '',
number: ''
},
{
id: [1],
name : [''],
number: ['']
},
{
id: [2],
name : [''],
number: ['']
},
{
id: [3],
name : [''],
number: ['']
},
{
id: [4],
name : [''],
number: ['']
}
];
ngOnInit() {}
pickContact(contactId) {
this.contacts.pickContact().then((contact) => {
this.mContacts[contactId].name = contact.name.givenName;
this.mContacts[contactId].number = contact.phoneNumbers[0].value;
this.saveContacts();
});
}
saveContacts() {
this.storage.set(this.key, JSON.stringify(this.mContacts));
}
getContact() {
this.storage.get(this.key).then((val) => {
console.log('Contact Name: ', val);
if (val != null && val !== undefined) {
this.mContacts = JSON.parse(val);
}
});
}
}
and here is the function that sends the SMS. it starts with getContacts() method, as the Contacts are originally stored with the IONIC storage Plugin
EDIT
I want to retrieve the contents of the the array list from the Contacts Component, however when I call the function sendSMS() it returns a null for the number
sendSMS()
{
this.contactComponent.getContact();
let number = this.contactComponent.mContacts[0][2];
let message = "Hello World";
console.log("number=" + number + ", message= " + message);
//CONFIGURATION
var options = {
replaceLineBreaks: false, // true to replace \n by a new line, false by default
android: {
intent: 'INTENT' // send SMS with the native android SMS messaging
//intent: '' // send SMS without opening any other app
}
};
this.sms.send(number, message, options);
}
Hope this helps someone.
I decided to do this instead to make things simplier.
this.checkSMSPermission();
this.contactComponent.getContact();
const numberOne = this.contactComponent.mContacts[0].number;
const numberTwo = this.contactComponent.mContacts[1].number;
const numbeThree = this.contactComponent.mContacts[2].number;
const numberFour = this.contactComponent.mContacts[3].number;
const numberFive = this.contactComponent.mContacts[4].number;
console.log(numberOne);
// tslint:disable-next-line: max-line-length
const message = this.messageComponent.dangerMessage + ' my location is: lat: ' + this.latitude.toString() + 'lng: ' + this.longitude.toString();
console.log('number=' + numberOne + ', message= ' + message);
// CONFIGURATION
const options = {
replaceLineBreaks: false, // true to replace \n by a new line, false by default
android: {
intent: '' // send SMS with the native android SMS messaging
// intent: '' // send SMS without opening any other app
}
};
this.sms.send(numberOne,numberTwo ,numberThree,numberFour,numberFive, message, options).then(() => {
this.presentAlert('Success', 'message has been sent');
})
.catch(error => {
this.presentAlert('Error', 'Failed: ' + error);
});
}
I am trying to add a Quick Launch functionality to a this homepage project which would, with a key press, launch all the URLs with quickLaunch property set to true.
I have set the scene by adding a quickLaunch property to the CONFIG object like so:
const CONFIG = {
commands: [{
{
category: 'General',
name: 'Mail',
key: 'm',
url: 'https://gmail.com',
search: '/#search/text={}',
color: 'linear-gradient(135deg, #dd5145, #dd5145)',
icon: 'mail.png',
quickLaunch: true,
},
{
category: 'General',
name: 'Drive',
key: 'd',
url: 'https://drive.google.com',
search: '/drive/search?q={}',
color: 'linear-gradient(135deg, #FFD04B, #1EA362, #4688F3)',
icon: 'drive.png',
quickLaunch: false,
},
{
category: 'Tech',
name: 'GitHub',
key: 'g',
url: 'https://github.com',
search: '/search?q={}',
color: 'linear-gradient(135deg, #2b2b2b, #3b3b3b)',
icon: 'github.png',
quickLaunch: true,
},
...and so on
and then added a condition to launch all the websites with quickLaunch option enabled:
class QueryParser {
constructor(options) {
this._commands = options.commands;
this._searchDelimiter = options.searchDelimiter;
this._pathDelimiter = options.pathDelimiter;
this._protocolRegex = /^[a-zA-Z]+:\/\//i;
this._urlRegex = /^((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)$/i;
this.parse = this.parse.bind(this);
}
parse(query) {
const res = {
query: query,
split: null
};
if (this._urlRegex.test(query)) {
const hasProtocol = this._protocolRegex.test(query);
res.redirect = hasProtocol ? query : 'http://' + query;
} else {
const trimmed = query.trim();
const splitSearch = trimmed.split(this._searchDelimiter);
const splitPath = trimmed.split(this._pathDelimiter);
this._commands.some(({
category,
key,
name,
search,
url,
quickLaunch
}) => {
if (query === key) {
res.key = key;
res.isKey = true;
res.redirect = url;
return true;
}
if (splitSearch[0] === key && search) {
res.key = key;
res.isSearch = true;
res.split = this._searchDelimiter;
res.query = QueryParser._shiftAndTrim(splitSearch, res.split);
res.redirect = QueryParser._prepSearch(url, search, res.query);
return true;
}
if (splitPath[0] === key) {
res.key = key;
res.isPath = true;
res.split = this._pathDelimiter;
res.path = QueryParser._shiftAndTrim(splitPath, res.split);
res.redirect = QueryParser._prepPath(url, res.path);
return true;
}
if (key === '*') {
res.redirect = QueryParser._prepSearch(url, search, query);
}
/* ---> */ if (query === 'q!') {
for (let i = 0; i < this._commands.length; i++) {
if (this._commands[i].quickLaunch === true) {
window.open(this._commands[i].url);
}
}
return true;
}
});
}
res.color = QueryParser._getColorFromUrl(this._commands, res.redirect);
return res;
}
static _getColorFromUrl(commands, url) {
const domain = new URL(url).hostname;
return (
commands
.filter(c => new URL(c.url).hostname.includes(domain))
.map(c => c.color)[0] || null
);
}
static _prepPath(url, path) {
return QueryParser._stripUrlPath(url) + '/' + path;
}
static _prepSearch(url, searchPath, query) {
if (!searchPath) return url;
const baseUrl = QueryParser._stripUrlPath(url);
const urlQuery = encodeURIComponent(query);
searchPath = searchPath.replace('{}', urlQuery);
return baseUrl + searchPath;
}
static _shiftAndTrim(arr, delimiter) {
arr.shift();
return arr.join(delimiter).trim();
}
static _stripUrlPath(url) {
const parser = document.createElement('a');
parser.href = url;
return `${parser.protocol}//${parser.hostname}`;
}
}
I expected the marked condition (commented with "-->") to fire only once but it is doing the whole process four times all over. I logged the URLs it is trying to launch and it looks like this:
Am I missing an obvious core concept?
Seems like this._commands.some( runs through all your this._commands, and then you check if query is query === 'q!' and i guess thats always true, and if thats the case then you loop through this._commands again. giving you this._commands.length * this._commands.length amount of output.
Hi I'm having some major issues trying to understand how to datatables to work with server side processing. For some background I'm using a service call Gamesparks to create the backend for a videogame and inside this service they have an implementation of mongodb.
I have an endpoint that fetchs all my users and I can see them in my table but the issue is that I fetch all of them, how can I achieve a pagination?. In the documentation they state that we must put serverSide to true but is not working. I really have no idea on how to proceed I need help.
Gamesparks event to fetch all users
require("LeaderboardMethods");
var playerList = Spark.runtimeCollection("playerList").find({},{"_id":0});
var finalData = [];
while(playerList.hasNext()){
var current = playerList.next();
var playerStats = Spark.runtimeCollection("playerStatistics").findOne({
"playerId":current.playerId
});
var loadedPlayer = Spark.loadPlayer(current.playerId);
var score = getScore(current.playerId);
if(loadedPlayer === null){
var toReturn = {
"playerId": current.playerId,
"displayName": current.displayName,
"email": "DELETED",
"rank": current.rank,
"coins": "DELETED",
"ban": "DELETED",
"score": score
}
finalData.push(toReturn);
} else{
var coins = loadedPlayer.getBalance("COIN");
var toReturn = {
"playerId": current.playerId,
"displayName": current.displayName,
"email": current.email,
"rank":playerStats.rank,
"coins": coins,
"ban": playerStats.isBlocked,
"score":score
}
finalData.push(toReturn);
}
}
Spark.setScriptData("playerList",finalData);
Datatables call
App.getUsers = function(){
var bodyData = {
"#class": ".LogEventRequest",
"eventKey": "GET_PLAYER_DATA",
"playerId": "MY_ID"
}
var table = $('#table1').DataTable({
"dom": "<'row be-datatable-header'<'col-sm-4'l><'col-sm-4'B><'col-sm-4'f>>" +
"<'row be-datatable-body'<'col-sm-12'tr>>" +
"<'row be-datatable-footer'<'col-sm-5'i><'col-sm-7'p>>",
"buttons": [
{
text: 'Edit',
action: function (e, dt, node, config) {
var sel_row = table.rows({
selected: true
}).data();
if (sel_row.length != 0) {
window.location.href = "edit-user.html";
localStorage.setItem("editUser", JSON.stringify(sel_row[0]));
}
}
},
{
text: 'Create',
action: function (e, dt, node, config) {
window.location.href = "create-user.html";
}
},
{
text: 'Delete',
className: 'delete-btn',
action: function (e, dt, node, config) {
var filtered = table.rows({
filter: 'applied',
selected: true
}).data();
// Only open modal when are users selected
if(filtered.length != 0){
$("#proceed-delete").prop('disabled', true)
$("#mod-danger-delete").modal();
if(filtered.length != 1) {
$('#length-users').append(document.createTextNode(filtered.length + " users"));
} else {
$('#length-users').append(document.createTextNode(filtered.length + " user"));
}
$("#delete-confirmation").change(function () {
if ($("#delete-confirmation").val() === "DELETE"){
$("#proceed-delete").prop('disabled', false)
$('#proceed-delete').on('click', function () {
if (filtered.length === 1) {
deleteUserRequest(filtered[0]);
} else {
for (let index = 0; index < filtered.length; index++) {
deleteUserRequest(filtered[index])
}
}
});
}
});
}
}
}, 'selectAll', 'selectNone'
],
"paging":true,
"pageLength":50,
"serverSide":true,
"ajax": {
"data": function (d) {
return JSON.stringify(bodyData);
},
"contentType": "application/json; charset=utf-8",
"url": config.REQUEST_API + '/rs/' + config.API_CREDENTIAL_SERVER + '/' + config.API_SERVER_SECRET + '/LogEventRequest',
"type":"POST",
"dataSrc":function(json){
console.log(json);
$('#loading-row').removeClass('be-loading-active');
return json.scriptData.playerList
},
},
"columns": [
{
data: null,
defaultContent: "<td></td>",
className: 'select-checkbox'
},
{ data: "playerId"},
{ data: "displayName" },
{ data: "email" },
{ data: "score"},
{ data: "rank" },
{ data: "isBlocked" },
{ data: "coins" },
{
"data": null,
"defaultContent": "<button class='btn btn-space btn-primary' onclick='App.click()'>View more</button>"
}
],
"select": {
style: 'multi',
selector: 'td:first-child'
},
}).on('error.dt', function(e, settings, techNote, message){
var err = settings.jqXHR.responseJSON.error;
// GS err
if(err === "UNAUTHORIZED"){
location.href = "pages-login.html";
return true;
} else{
$('#error-container-dt').show();
console.log(message);
return true;
}
});
}
Quick peek into Gamesparks SDK and found this for example:
ListTransactionsRequest
dateFrom Optional date constraint to list transactions from
dateTo Optional date constraint to list transactions to
entryCount The number of items to return in a page (default=50)
include An optional filter that limits the transaction types returned
offset The offset (page number) to start from (default=0)
Now, for paging you need entryCount and offset. First is size of one page, default 50, you can change it. Server returns 'entryCount' no of records.
Offset is the starting record. For example, initial list (1st page) does have 50 records, clicking "Next" button will send request "offset: 51" to the server. And server reply records from 50 (offset) to 100 (offset + entryCount).
var bodyData = {
"#class": ".LogEventRequest",
"eventKey": "GET_PLAYER_DATA",
"playerId": "MY_ID",
"entryCount": entryCount, // you have to send it if you dont like the default value
"offset": offset // gets his value from "NEXT" or "PREV" button
}
Thats how paging works. I'm not able to give more detailed answer as I dont use Gamesparks myself. Hope it gives you least some directon.
This is my code in javascript:
var user = {};
var handlerReference = null;
var myMessages = myApp.messages('.messages', {
autoLayout: true
});
function setListener(name, jid) {
user.name = name;
user.jid = jid;
$("#chatScreenTitle").text(user.name);
handlerReference = connection.addHandler(onMsgReceive, null, 'message', "chat", null, user.jid, {matchBare: true});
}
function onMsgReceive(stanza) {
console.log($(stanza).children("body").text());
myMessages.addMessage({
text: $(stanza).children("body").text(),
type: "received"
}, 'append', true);
}
function sendMessage(message) {
if (message.length == 0) {
myApp.alert("please enter some text", "alert");
}
else {
var uniqueID = connection.getUniqueId("my:code");
var reqChannelsItems = $msg({id: uniqueID, to: user.jid, from: xmpp_user + "#" + XMPP_DOMAIN, type: "chat"}).c("body").t(message);
connection.send(reqChannelsItems.tree());
myMessages.addMessage({
text: message,
type: "sent"
}, 'append', true);
}
}
I can only send and receive one message after that I am not able to send or receive any messsage.
Thanks.
I think you need to use return true at the end of onMsgReceive. If you don't return true it will finish this handler