Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
e1f8d53c
Commit
e1f8d53c
authored
Jan 31, 2019
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial support for chat markers. Updates #324
parent
4c964c56
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
316 additions
and
97 deletions
+316
-97
dist/converse.js
dist/converse.js
+104
-41
spec/chatbox.js
spec/chatbox.js
+41
-8
spec/messages.js
spec/messages.js
+88
-12
src/headless/converse-chatboxes.js
src/headless/converse-chatboxes.js
+83
-36
No files found.
dist/converse.js
View file @
e1f8d53c
...
...
@@ -61304,6 +61304,7 @@ const u = _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].env.utils;
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');
_converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-chatboxes', {
dependencies: ["converse-roster", "converse-vcard"],
...
...
@@ -61634,7 +61635,84 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
return false;
},
handleReceipt(stanza) {
sendMarker(to_jid, id, type) {
const stanza = $msg({
'from': _converse.connection.jid,
'id': _converse.connection.getUniqueId(),
'to': to_jid,
'type': 'chat'
}).c(type, {
'xmlns': Strophe.NS.MARKERS,
'id': id
});
_converse.api.send(stanza);
},
handleChatMarker(stanza, from_jid, is_carbon) {
const to_bare_jid = Strophe.getBareJidFromJid(stanza.getAttribute('to'));
if (to_bare_jid !== _converse.bare_jid) {
return false;
}
const markers = sizzle(`[xmlns="${Strophe.NS.MARKERS}"]`, stanza);
if (markers.length === 0) {
return false;
} else if (markers.length > 1) {
_converse.log('onMessage: Ignoring incoming stanza with multiple message markers', Strophe.LogLevel.ERROR);
_converse.log(stanza, Strophe.LogLevel.ERROR);
return false;
} else {
const marker = markers.pop();
if (marker.nodeName === 'markable' && !is_carbon) {
this.sendMarker(from_jid, stanza.getAttribute('id'), 'received');
return false;
} else {
const msgid = marker && marker.getAttribute('id'),
message = msgid && this.messages.findWhere({
msgid
}),
field_name = `marker_${marker.nodeName}`;
if (message && !message.get(field_name)) {
message.save({
field_name: moment().format()
});
}
return true;
}
}
},
sendReceiptStanza(to_jid, id) {
const receipt_stanza = $msg({
'from': _converse.connection.jid,
'id': _converse.connection.getUniqueId(),
'to': to_jid,
'type': 'chat'
}).c('received', {
'xmlns': Strophe.NS.RECEIPTS,
'id': id
}).up().c('store', {
'xmlns': Strophe.NS.HINTS
}).up();
_converse.api.send(receipt_stanza);
},
handleReceipt(stanza, from_jid, is_carbon, is_me) {
const requests_receipt = !_.isUndefined(sizzle(`request[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop());
if (requests_receipt && !is_carbon && !is_me) {
this.sendReceiptStanza(from_jid, stanza.getAttribute('id'));
}
const to_bare_jid = Strophe.getBareJidFromJid(stanza.getAttribute('to'));
if (to_bare_jid === _converse.bare_jid) {
...
...
@@ -61927,6 +62005,23 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
return attrs;
},
async createMessage(stanza, original_stanza) {
const msgid = stanza.getAttribute('id'),
message = msgid && this.messages.findWhere({
msgid
});
if (!message) {
// Only create the message when we're sure it's not a duplicate
const attrs = await this.getMessageAttributesFromStanza(stanza, original_stanza);
if (attrs['chat_state'] || !u.isEmptyMessage(attrs)) {
const msg = this.messages.create(attrs);
this.incrementUnreadMsgCounter(msg);
}
}
},
isHidden() {
/* Returns a boolean to indicate whether a newly received
* message will be visible to the user or not.
...
...
@@ -62078,22 +62173,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
}
},
sendReceiptStanza(to_jid, id) {
const receipt_stanza = $msg({
'from': _converse.connection.jid,
'id': _converse.connection.getUniqueId(),
'to': to_jid,
'type': 'chat'
}).c('received', {
'xmlns': Strophe.NS.RECEIPTS,
'id': id
}).up().c('store', {
'xmlns': Strophe.NS.HINTS
}).up();
_converse.api.send(receipt_stanza);
},
async onMessage(stanza) {
/* Handler method for all incoming single-user chat "message"
* stanzas.
...
...
@@ -62141,12 +62220,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
from_resource = Strophe.getResourceFromJid(from_jid),
is_me = from_bare_jid === _converse.bare_jid;
const requests_receipt = !_.isUndefined(sizzle(`request[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop());
if (requests_receipt && !is_carbon && !is_me) {
this.sendReceiptStanza(from_jid, stanza.getAttribute('id'));
}
let contact_jid;
if (is_me) {
...
...
@@ -62158,27 +62231,17 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
contact_jid = Strophe.getBareJidFromJid(to_jid);
} else {
contact_jid = from_bare_jid;
}
const attrs = {
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname') // Get chat box, but only create a new one when the message has a body.
} // Get chat box, but only create when the message has something to show to the user
};
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0;
const chatbox = this.getChatBox(contact_jid, attrs, has_body);
if (chatbox && !chatbox.handleMessageCorrection(stanza) && !chatbox.handleReceipt(stanza)) {
const msgid = stanza.getAttribute('id'),
message = msgid && chatbox.messages.findWhere({
msgid
}
);
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0,
chatbox_attrs = {
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')
},
chatbox = this.getChatBox(contact_jid, chatbox_attrs, has_body
);
if (!message) {
// Only create the message when we're sure it's not a duplicate
const attrs = await chatbox.getMessageAttributesFromStanza(stanza, original_stanza);
const msg = chatbox.messages.create(attrs);
chatbox.incrementUnreadMsgCounter(msg);
}
if (chatbox && !chatbox.handleMessageCorrection(stanza) && !chatbox.handleReceipt(stanza, from_jid, is_carbon, is_me) && !chatbox.handleChatMarker(stanza, from_jid, is_carbon)) {
await chatbox.createMessage(stanza, original_stanza);
}
_converse.emit('message', {
spec/chatbox.js
View file @
e1f8d53c
...
...
@@ -530,6 +530,38 @@
describe
(
"
A Chat Status Notification
"
,
function
()
{
it
(
"
is ignored when it's a carbon copy of one of my own
"
,
mock
.
initConverseWithPromises
(
null
,
[
'
rosterGroupsFetched
'
],
{},
async
function
(
done
,
_converse
)
{
test_utils
.
createContacts
(
_converse
,
'
current
'
);
test_utils
.
openControlBox
();
const
sender_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
await
test_utils
.
openChatBoxFor
(
_converse
,
sender_jid
);
let
stanza
=
Strophe
.
xmlHtmlNode
(
`<message from="
${
sender_jid
}
"
type="chat"
to="dummy@localhost/resource">
<composing xmlns="http://jabber.org/protocol/chatstates"/>
<no-store xmlns="urn:xmpp:hints"/>
<no-permanent-store xmlns="urn:xmpp:hints"/>
</message>`
).
firstChild
;
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
stanza
=
Strophe
.
xmlHtmlNode
(
`<message from="
${
sender_jid
}
"
type="chat"
to="dummy@localhost/resource">
<paused xmlns="http://jabber.org/protocol/chatstates"/>
<no-store xmlns="urn:xmpp:hints"/>
<no-permanent-store xmlns="urn:xmpp:hints"/>
</message>`
).
firstChild
;
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
done
();
}));
it
(
"
does not open a new chatbox
"
,
mock
.
initConverseWithPromises
(
null
,
[
'
rosterGroupsFetched
'
],
{},
...
...
@@ -656,12 +688,14 @@
async
function
(
done
,
_converse
)
{
test_utils
.
createContacts
(
_converse
,
'
current
'
);
_converse
.
emit
(
'
rosterContactsFetched
'
);
test_utils
.
openControlBox
();
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
spyOn
(
_converse
,
'
emit
'
);
const
sender_jid
=
mock
.
cur_names
[
1
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
openChatBoxFor
(
_converse
,
sender_jid
);
await
test_utils
.
waitUntil
(()
=>
_converse
.
rosterview
.
el
.
querySelectorAll
(
'
.roster-group
'
).
length
);
await
test_utils
.
openChatBoxFor
(
_converse
,
sender_jid
);
// <composing> state
let
msg
=
$msg
({
...
...
@@ -678,7 +712,6 @@
await
test_utils
.
waitUntil
(()
=>
view
.
model
.
vcard
.
get
(
'
fullname
'
)
===
mock
.
cur_names
[
1
])
// Check that the notification appears inside the chatbox in the DOM
let
events
=
view
.
el
.
querySelectorAll
(
'
.chat-state-notification
'
);
expect
(
events
.
length
).
toBe
(
1
);
expect
(
events
[
0
].
textContent
).
toEqual
(
mock
.
cur_names
[
1
]
+
'
is typing
'
);
// Check that it doesn't appear twice
...
...
@@ -687,7 +720,7 @@
to
:
_converse
.
connection
.
jid
,
type
:
'
chat
'
,
id
:
(
new
Date
()).
getTime
()
}).
c
(
'
body
'
).
c
(
'
composing
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
CHATSTATES
}).
tree
();
}).
c
(
'
composing
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
CHATSTATES
}).
tree
();
await
_converse
.
chatboxes
.
onMessage
(
msg
);
events
=
view
.
el
.
querySelectorAll
(
'
.chat-state-notification
'
);
expect
(
events
.
length
).
toBe
(
1
);
...
...
@@ -802,24 +835,24 @@
null
,
[
'
rosterGroupsFetched
'
],
{},
async
function
(
done
,
_converse
)
{
test_utils
.
createContacts
(
_converse
,
'
current
'
);
test_utils
.
createContacts
(
_converse
,
'
current
'
,
2
);
_converse
.
emit
(
'
rosterContactsFetched
'
);
test_utils
.
openControlBox
();
await
test_utils
.
waitUntil
(()
=>
_converse
.
rosterview
.
el
.
querySelectorAll
(
'
.roster-group
'
).
length
);
// TODO: only show paused state if the previous state was composing
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
spyOn
(
_converse
,
'
emit
'
);
spyOn
(
_converse
,
'
emit
'
)
.
and
.
callThrough
()
;
const
sender_jid
=
mock
.
cur_names
[
1
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
const
view
=
await
test_utils
.
openChatBoxFor
(
_converse
,
sender_jid
);
// <paused> state
const
msg
=
$msg
({
from
:
sender_jid
,
to
:
_converse
.
connection
.
jid
,
type
:
'
chat
'
,
id
:
(
new
Date
()).
getTime
()
}).
c
(
'
body
'
).
c
(
'
paused
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
CHATSTATES
}).
tree
();
}).
c
(
'
paused
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
CHATSTATES
}).
tree
();
await
_converse
.
chatboxes
.
onMessage
(
msg
);
expect
(
_converse
.
emit
).
toHaveBeenCalledWith
(
'
message
'
,
jasmine
.
any
(
Object
));
const
view
=
_converse
.
chatboxviews
.
get
(
sender_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
await
test_utils
.
waitUntil
(()
=>
view
.
model
.
vcard
.
get
(
'
fullname
'
)
===
mock
.
cur_names
[
1
])
var
event
=
view
.
el
.
querySelector
(
'
.chat-info.chat-state-notification
'
);
expect
(
event
.
textContent
).
toEqual
(
mock
.
cur_names
[
1
]
+
'
has stopped typing
'
);
...
...
spec/messages.js
View file @
e1f8d53c
...
...
@@ -313,8 +313,6 @@
await
_converse
.
chatboxes
.
onMessage
(
msg
);
await
test_utils
.
waitUntil
(()
=>
_converse
.
api
.
chats
.
get
().
length
);
const
view
=
_converse
.
chatboxviews
.
get
(
sender_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
msg
=
$msg
({
'
id
'
:
'
aeb214
'
,
'
to
'
:
_converse
.
bare_jid
})
.
c
(
'
forwarded
'
,
{
'
xmlns
'
:
'
urn:xmpp:forward:0
'
})
.
c
(
'
delay
'
,
{
'
xmlns
'
:
'
urn:xmpp:delay
'
,
'
stamp
'
:
'
2017-12-31T22:08:25Z
'
}).
up
()
...
...
@@ -392,7 +390,6 @@
.
c
(
'
composing
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
CHATSTATES
}).
up
()
.
tree
();
await
_converse
.
chatboxes
.
onMessage
(
msg
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
msg
=
$msg
({
'
id
'
:
'
aeb220
'
,
...
...
@@ -527,7 +524,6 @@
await
test_utils
.
waitUntil
(()
=>
_converse
.
api
.
chats
.
get
().
length
)
const
chatbox
=
_converse
.
chatboxes
.
get
(
sender_jid
);
const
view
=
_converse
.
chatboxviews
.
get
(
sender_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
expect
(
chatbox
).
toBeDefined
();
expect
(
view
).
toBeDefined
();
...
...
@@ -579,7 +575,6 @@
// Check that the chatbox and its view now exist
const
chatbox
=
_converse
.
chatboxes
.
get
(
recipient_jid
);
const
view
=
_converse
.
chatboxviews
.
get
(
recipient_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
expect
(
chatbox
).
toBeDefined
();
expect
(
view
).
toBeDefined
();
// Check that the message was received and check the message parameters
...
...
@@ -1225,7 +1220,8 @@
const
sender_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
const
msg_id
=
u
.
getUniqueId
();
const
sent_stanzas
=
[];
spyOn
(
_converse
.
chatboxes
,
'
sendReceiptStanza
'
).
and
.
callThrough
();
const
view
=
await
test_utils
.
openChatBoxFor
(
_converse
,
sender_jid
);
spyOn
(
view
.
model
,
'
sendReceiptStanza
'
).
and
.
callThrough
();
const
msg
=
$msg
({
'
from
'
:
sender_jid
,
'
to
'
:
_converse
.
connection
.
jid
,
...
...
@@ -1243,7 +1239,7 @@
.
c
(
'
request
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
RECEIPTS
}).
tree
();
await
_converse
.
chatboxes
.
onMessage
(
msg
);
await
test_utils
.
waitUntil
(()
=>
_converse
.
api
.
chats
.
get
().
length
);
expect
(
_converse
.
chatboxes
.
sendReceiptStanza
).
not
.
toHaveBeenCalled
();
expect
(
view
.
model
.
sendReceiptStanza
).
not
.
toHaveBeenCalled
();
done
();
}));
...
...
@@ -1255,7 +1251,8 @@
const
recipient_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
const
msg_id
=
u
.
getUniqueId
();
const
sent_stanzas
=
[];
spyOn
(
_converse
.
chatboxes
,
'
sendReceiptStanza
'
).
and
.
callThrough
();
const
view
=
await
test_utils
.
openChatBoxFor
(
_converse
,
recipient_jid
);
spyOn
(
view
.
model
,
'
sendReceiptStanza
'
).
and
.
callThrough
();
const
msg
=
$msg
({
'
from
'
:
converse
.
bare_jid
,
'
to
'
:
_converse
.
connection
.
jid
,
...
...
@@ -1272,7 +1269,7 @@
.
c
(
'
request
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
RECEIPTS
}).
tree
();
await
_converse
.
chatboxes
.
onMessage
(
msg
);
await
test_utils
.
waitUntil
(()
=>
_converse
.
api
.
chats
.
get
().
length
);
expect
(
_converse
.
chatboxes
.
sendReceiptStanza
).
not
.
toHaveBeenCalled
();
expect
(
view
.
model
.
sendReceiptStanza
).
not
.
toHaveBeenCalled
();
done
();
}));
...
...
@@ -1482,7 +1479,6 @@
// Check that the chatbox and its view now exist
const
chatbox
=
_converse
.
chatboxes
.
get
(
sender_jid
);
const
view
=
_converse
.
chatboxviews
.
get
(
sender_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
expect
(
chatbox
).
toBeDefined
();
expect
(
view
).
toBeDefined
();
...
...
@@ -1531,7 +1527,6 @@
await
test_utils
.
waitUntil
(()
=>
_converse
.
api
.
chats
.
get
().
length
)
const
view
=
_converse
.
chatboxviews
.
get
(
sender_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
expect
(
_converse
.
emit
).
toHaveBeenCalledWith
(
'
message
'
,
jasmine
.
any
(
Object
));
// onMessage is a handler for received XMPP messages
...
...
@@ -1843,7 +1838,6 @@
await
_converse
.
chatboxes
.
onMessage
(
msg
);
await
test_utils
.
waitUntil
(()
=>
_converse
.
chatboxviews
.
keys
().
length
>
1
,
1000
);
const
view
=
_converse
.
chatboxviews
.
get
(
sender_jid
);
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
await
test_utils
.
waitUntil
(()
=>
view
.
model
.
messages
.
length
);
expect
(
_converse
.
chatboxes
.
getChatBox
).
toHaveBeenCalled
();
var
chat_content
=
$
(
view
.
el
).
find
(
'
.chat-content:last
'
)[
0
];
...
...
@@ -2023,6 +2017,88 @@
});
});
describe
(
"
A XEP-0333 Chat Marker
"
,
function
()
{
it
(
"
is sent when a markable message is received
"
,
mock
.
initConverseWithPromises
(
null
,
[
'
rosterGroupsFetched
'
],
{},
async
function
(
done
,
_converse
)
{
test_utils
.
createContacts
(
_converse
,
'
current
'
,
1
);
const
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
await
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
const
view
=
await
_converse
.
api
.
chatviews
.
get
(
contact_jid
);
const
msgid
=
u
.
getUniqueId
();
const
stanza
=
Strophe
.
xmlHtmlNode
(
`
<message from='
${
contact_jid
}
'
id='
${
msgid
}
'
type="chat"
to='
${
_converse
.
jid
}
'>
<body>My lord, dispatch; read o'er these articles.</body>
<markable xmlns='urn:xmpp:chat-markers:0'/>
</message>`
).
firstElementChild
;
const
sent_stanzas
=
[];
spyOn
(
_converse
.
connection
,
'
send
'
).
and
.
callFake
(
s
=>
sent_stanzas
.
push
(
s
));
spyOn
(
view
.
model
,
'
sendMarker
'
).
and
.
callThrough
();
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
await
test_utils
.
waitUntil
(()
=>
view
.
model
.
sendMarker
.
calls
.
count
()
===
1
);
expect
(
Strophe
.
serialize
(
sent_stanzas
[
0
])).
toBe
(
`<message from="dummy@localhost/resource" `
+
`id="
${
sent_stanzas
[
0
].
nodeTree
.
getAttribute
(
'
id
'
)}
" `
+
`to="
${
contact_jid
}
" type="chat" xmlns="jabber:client">`
+
`<received id="
${
msgid
}
" xmlns="urn:xmpp:chat-markers:0"/>`
+
`</message>`
);
done
();
}));
it
(
"
is ignored if it's a carbon copy of one that I sent from a different client
"
,
mock
.
initConverseWithPromises
(
null
,
[
'
rosterGroupsFetched
'
],
{},
async
function
(
done
,
_converse
)
{
test_utils
.
createContacts
(
_converse
,
'
current
'
,
1
);
const
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
await
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
const
view
=
await
_converse
.
api
.
chatviews
.
get
(
contact_jid
);
let
stanza
=
Strophe
.
xmlHtmlNode
(
`
<message xmlns="jabber:client"
to="
${
_converse
.
bare_jid
}
"
type="chat"
id="2e972ea0-0050-44b7-a830-f6638a2595b3"
from="
${
contact_jid
}
">
<body>😊</body>
<markable xmlns="urn:xmpp:chat-markers:0"/>
<origin-id xmlns="urn:xmpp:sid:0" id="2e972ea0-0050-44b7-a830-f6638a2595b3"/>
<stanza-id xmlns="urn:xmpp:sid:0" id="IxVDLJ0RYbWcWvqC" by="
${
_converse
.
bare_jid
}
"/>
</message>`
).
firstElementChild
;
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
await
new
Promise
((
resolve
,
reject
)
=>
view
.
once
(
'
messageInserted
'
,
resolve
));
expect
(
view
.
el
.
querySelectorAll
(
'
.chat-msg
'
).
length
).
toBe
(
1
);
expect
(
view
.
model
.
messages
.
length
).
toBe
(
1
);
stanza
=
Strophe
.
xmlHtmlNode
(
`<message xmlns="jabber:client" to="
${
_converse
.
bare_jid
}
" type="chat" from="
${
contact_jid
}
">
<sent xmlns="urn:xmpp:carbons:2">
<forwarded xmlns="urn:xmpp:forward:0">
<message xmlns="jabber:client" to="
${
contact_jid
}
" type="chat" from="
${
_converse
.
bare_jid
}
/other-resource">
<received xmlns="urn:xmpp:chat-markers:0" id="2e972ea0-0050-44b7-a830-f6638a2595b3"/>
<store xmlns="urn:xmpp:hints"/>
<stanza-id xmlns="urn:xmpp:sid:0" id="F4TC6CvHwzqRbeHb" by="jc@opkode.com"/>
</message>
</forwarded>
</sent>
</message>`
).
firstElementChild
;
spyOn
(
_converse
,
'
emit
'
).
and
.
callThrough
();
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
stanza
));
await
test_utils
.
waitUntil
(()
=>
_converse
.
emit
.
calls
.
count
()
===
1
);
expect
(
view
.
el
.
querySelectorAll
(
'
.chat-msg
'
).
length
).
toBe
(
1
);
expect
(
view
.
model
.
messages
.
length
).
toBe
(
1
);
done
();
}));
});
describe
(
"
A Groupchat Message
"
,
function
()
{
...
...
src/headless/converse-chatboxes.js
View file @
e1f8d53c
...
...
@@ -15,6 +15,7 @@ const u = converse.env.utils;
Strophe
.
addNamespace
(
'
MESSAGE_CORRECT
'
,
'
urn:xmpp:message-correct:0
'
);
Strophe
.
addNamespace
(
'
RECEIPTS
'
,
'
urn:xmpp:receipts
'
);
Strophe
.
addNamespace
(
'
REFERENCE
'
,
'
urn:xmpp:reference:0
'
);
Strophe
.
addNamespace
(
'
MARKERS
'
,
'
urn:xmpp:chat-markers:0
'
);
converse
.
plugins
.
add
(
'
converse-chatboxes
'
,
{
...
...
@@ -312,8 +313,66 @@ converse.plugins.add('converse-chatboxes', {
}
return
false
;
},
sendMarker
(
to_jid
,
id
,
type
)
{
const
stanza
=
$msg
({
'
from
'
:
_converse
.
connection
.
jid
,
'
id
'
:
_converse
.
connection
.
getUniqueId
(),
'
to
'
:
to_jid
,
'
type
'
:
'
chat
'
,
}).
c
(
type
,
{
'
xmlns
'
:
Strophe
.
NS
.
MARKERS
,
'
id
'
:
id
});
_converse
.
api
.
send
(
stanza
);
},
handleReceipt
(
stanza
)
{
handleChatMarker
(
stanza
,
from_jid
,
is_carbon
)
{
const
to_bare_jid
=
Strophe
.
getBareJidFromJid
(
stanza
.
getAttribute
(
'
to
'
));
if
(
to_bare_jid
!==
_converse
.
bare_jid
)
{
return
false
;
}
const
markers
=
sizzle
(
`[xmlns="
${
Strophe
.
NS
.
MARKERS
}
"]`
,
stanza
);
if
(
markers
.
length
===
0
)
{
return
false
;
}
else
if
(
markers
.
length
>
1
)
{
_converse
.
log
(
'
onMessage: Ignoring incoming stanza with multiple message markers
'
,
Strophe
.
LogLevel
.
ERROR
);
_converse
.
log
(
stanza
,
Strophe
.
LogLevel
.
ERROR
);
return
false
;
}
else
{
const
marker
=
markers
.
pop
();
if
(
marker
.
nodeName
===
'
markable
'
&&
!
is_carbon
)
{
this
.
sendMarker
(
from_jid
,
stanza
.
getAttribute
(
'
id
'
),
'
received
'
);
return
false
;
}
else
{
const
msgid
=
marker
&&
marker
.
getAttribute
(
'
id
'
),
message
=
msgid
&&
this
.
messages
.
findWhere
({
msgid
}),
field_name
=
`marker_
${
marker
.
nodeName
}
`
;
if
(
message
&&
!
message
.
get
(
field_name
))
{
message
.
save
({
field_name
:
moment
().
format
()});
}
return
true
;
}
}
},
sendReceiptStanza
(
to_jid
,
id
)
{
const
receipt_stanza
=
$msg
({
'
from
'
:
_converse
.
connection
.
jid
,
'
id
'
:
_converse
.
connection
.
getUniqueId
(),
'
to
'
:
to_jid
,
'
type
'
:
'
chat
'
,
}).
c
(
'
received
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
RECEIPTS
,
'
id
'
:
id
}).
up
()
.
c
(
'
store
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
HINTS
}).
up
();
_converse
.
api
.
send
(
receipt_stanza
);
},
handleReceipt
(
stanza
,
from_jid
,
is_carbon
,
is_me
)
{
const
requests_receipt
=
!
_
.
isUndefined
(
sizzle
(
`request[xmlns="
${
Strophe
.
NS
.
RECEIPTS
}
"]`
,
stanza
).
pop
());
if
(
requests_receipt
&&
!
is_carbon
&&
!
is_me
)
{
this
.
sendReceiptStanza
(
from_jid
,
stanza
.
getAttribute
(
'
id
'
));
}
const
to_bare_jid
=
Strophe
.
getBareJidFromJid
(
stanza
.
getAttribute
(
'
to
'
));
if
(
to_bare_jid
===
_converse
.
bare_jid
)
{
const
receipt
=
sizzle
(
`received[xmlns="
${
Strophe
.
NS
.
RECEIPTS
}
"]`
,
stanza
).
pop
();
...
...
@@ -321,9 +380,7 @@ converse.plugins.add('converse-chatboxes', {
const
msgid
=
receipt
&&
receipt
.
getAttribute
(
'
id
'
),
message
=
msgid
&&
this
.
messages
.
findWhere
({
msgid
});
if
(
message
&&
!
message
.
get
(
'
received
'
))
{
message
.
save
({
'
received
'
:
moment
().
format
()
});
message
.
save
({
'
received
'
:
moment
().
format
()});
}
return
true
;
}
...
...
@@ -575,6 +632,19 @@ converse.plugins.add('converse-chatboxes', {
return
attrs
;
},
async
createMessage
(
stanza
,
original_stanza
)
{
const
msgid
=
stanza
.
getAttribute
(
'
id
'
),
message
=
msgid
&&
this
.
messages
.
findWhere
({
msgid
});
if
(
!
message
)
{
// Only create the message when we're sure it's not a duplicate
const
attrs
=
await
this
.
getMessageAttributesFromStanza
(
stanza
,
original_stanza
);
if
(
attrs
[
'
chat_state
'
]
||
!
u
.
isEmptyMessage
(
attrs
))
{
const
msg
=
this
.
messages
.
create
(
attrs
);
this
.
incrementUnreadMsgCounter
(
msg
);
}
}
},
isHidden
()
{
/* Returns a boolean to indicate whether a newly received
* message will be visible to the user or not.
...
...
@@ -708,17 +778,6 @@ converse.plugins.add('converse-chatboxes', {
}
},
sendReceiptStanza
(
to_jid
,
id
)
{
const
receipt_stanza
=
$msg
({
'
from
'
:
_converse
.
connection
.
jid
,
'
id
'
:
_converse
.
connection
.
getUniqueId
(),
'
to
'
:
to_jid
,
'
type
'
:
'
chat
'
,
}).
c
(
'
received
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
RECEIPTS
,
'
id
'
:
id
}).
up
()
.
c
(
'
store
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
HINTS
}).
up
();
_converse
.
api
.
send
(
receipt_stanza
);
},
async
onMessage
(
stanza
)
{
/* Handler method for all incoming single-user chat "message"
* stanzas.
...
...
@@ -765,16 +824,10 @@ converse.plugins.add('converse-chatboxes', {
from_jid
=
stanza
.
getAttribute
(
'
from
'
);
to_jid
=
stanza
.
getAttribute
(
'
to
'
);
}
const
from_bare_jid
=
Strophe
.
getBareJidFromJid
(
from_jid
),
from_resource
=
Strophe
.
getResourceFromJid
(
from_jid
),
is_me
=
from_bare_jid
===
_converse
.
bare_jid
;
const
requests_receipt
=
!
_
.
isUndefined
(
sizzle
(
`request[xmlns="
${
Strophe
.
NS
.
RECEIPTS
}
"]`
,
stanza
).
pop
());
if
(
requests_receipt
&&
!
is_carbon
&&
!
is_me
)
{
this
.
sendReceiptStanza
(
from_jid
,
stanza
.
getAttribute
(
'
id
'
));
}
let
contact_jid
;
if
(
is_me
)
{
// I am the sender, so this must be a forwarded message...
...
...
@@ -788,21 +841,15 @@ converse.plugins.add('converse-chatboxes', {
}
else
{
contact_jid
=
from_bare_jid
;
}
const
attrs
=
{
'
fullname
'
:
_
.
get
(
_converse
.
api
.
contacts
.
get
(
contact_jid
),
'
attributes.fullname
'
)
}
// Get chat box, but only create a new one when the message has a body.
const
has_body
=
sizzle
(
`body, encrypted[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"]`
,
stanza
).
length
>
0
;
const
chatbox
=
this
.
getChatBox
(
contact_jid
,
attrs
,
has_body
);
if
(
chatbox
&&
!
chatbox
.
handleMessageCorrection
(
stanza
)
&&
!
chatbox
.
handleReceipt
(
stanza
))
{
const
msgid
=
stanza
.
getAttribute
(
'
id
'
),
message
=
msgid
&&
chatbox
.
messages
.
findWhere
({
msgid
});
if
(
!
message
)
{
// Only create the message when we're sure it's not a duplicate
const
attrs
=
await
chatbox
.
getMessageAttributesFromStanza
(
stanza
,
original_stanza
);
const
msg
=
chatbox
.
messages
.
create
(
attrs
);
chatbox
.
incrementUnreadMsgCounter
(
msg
);
}
// Get chat box, but only create when the message has something to show to the user
const
has_body
=
sizzle
(
`body, encrypted[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"]`
,
stanza
).
length
>
0
,
chatbox_attrs
=
{
'
fullname
'
:
_
.
get
(
_converse
.
api
.
contacts
.
get
(
contact_jid
),
'
attributes.fullname
'
)},
chatbox
=
this
.
getChatBox
(
contact_jid
,
chatbox_attrs
,
has_body
);
if
(
chatbox
&&
!
chatbox
.
handleMessageCorrection
(
stanza
)
&&
!
chatbox
.
handleReceipt
(
stanza
,
from_jid
,
is_carbon
,
is_me
)
&&
!
chatbox
.
handleChatMarker
(
stanza
,
from_jid
,
is_carbon
))
{
await
chatbox
.
createMessage
(
stanza
,
original_stanza
);
}
_converse
.
emit
(
'
message
'
,
{
'
stanza
'
:
original_stanza
,
'
chatbox
'
:
chatbox
});
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment