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
2929647e
Commit
2929647e
authored
Jul 07, 2018
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for correcting the last message sent
fixes #421
parent
be58e2b9
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
285 additions
and
132 deletions
+285
-132
CHANGES.md
CHANGES.md
+1
-1
README.md
README.md
+1
-0
css/converse.css
css/converse.css
+33
-30
dist/converse.js
dist/converse.js
+56
-12
index.html
index.html
+1
-0
sass/_controlbox.scss
sass/_controlbox.scss
+64
-53
spec/chatbox.js
spec/chatbox.js
+10
-18
spec/messages.js
spec/messages.js
+64
-0
spec/presence.js
spec/presence.js
+5
-5
src/converse-chatboxes.js
src/converse-chatboxes.js
+26
-3
src/converse-chatview.js
src/converse-chatview.js
+17
-1
src/converse-core.js
src/converse-core.js
+0
-1
src/utils/core.js
src/utils/core.js
+7
-0
src/utils/form.js
src/utils/form.js
+0
-8
No files found.
CHANGES.md
View file @
2929647e
...
...
@@ -7,6 +7,7 @@
-
#161 XEP-0363: HTTP File Upload
-
#194 Include entity capabilities in outgoing presence stanzas
-
#337 API call to update a VCard
-
#421 XEP-0308: Last Message Correction
-
#968 Use nickname from VCard when joining a room
-
#1091 There's now only one CSS file for all view modes.
-
#1094 Show room members who aren't currently online
...
...
@@ -22,7 +23,6 @@
If the device is trusted, localStorage is used and user data is cached indefinitely.
-
Initial support for XEP-0357 Push Notifications, specifically registering an "App Server".
-
Add support for logging in via OAuth (see the
[
oauth_providers
](
https://conversejs.org/docs/html/configurations.html#oauth-providers
)
setting)
-
XEP-0308: Render message corrections
### Bugfixes
...
...
README.md
View file @
2929647e
...
...
@@ -60,6 +60,7 @@ which shows you how to use the CDN (content delivery network) to quickly get a d
-
Server-side archiving of messages
[
XEP 313
](
http://xmpp.org/extensions/xep-0313.html
)
-
Hidden Messages (aka Spoilers)
[
XEP 382
](
http://xmpp.org/extensions/xep-0382.html
)
-
Client state indication
[
XEP 352
](
http://xmpp.org/extensions/xep-0352.html
)
-
Last Message Correction
[
XEP 308
](
http://xmpp.org/extensions/xep-0308.html
)
-
Off-the-record encryption
-
Translated into 16 languages
...
...
css/converse.css
View file @
2929647e
...
...
@@ -8051,36 +8051,6 @@ body.reset {
#conversejs
.toggle-controlbox
span
{
color
:
white
;
}
@media
(
max-width
:
767.98px
)
{
#conversejs
:not
(
.converse-embedded
)
{
left
:
0
;
right
:
0
;
padding-left
:
env
(
safe-area-inset-left
);
padding-right
:
env
(
safe-area-inset-right
);
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
{
margin
:
0
!important
;
flex-direction
:
row
!important
;
justify-content
:
space-between
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
.converse-chatroom
{
font-size
:
14px
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
.chatbox
.box-flyout
{
margin-left
:
15px
;
left
:
0
;
bottom
:
0
;
border-radius
:
0
;
width
:
100vw
!important
;
height
:
100vh
!important
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
#controlbox
{
width
:
100vw
!important
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
#controlbox
.box-flyout
{
width
:
100vw
!important
;
height
:
100vh
!important
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
#controlbox
.sidebar
{
display
:
block
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes.sidebar-open
.chatbox
:not
(
#controlbox
)
{
display
:
none
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes.sidebar-open
#controlbox
.controlbox-pane
{
display
:
block
;
}
}
#conversejs
.converse-overlayed
#controlbox
{
order
:
-1
;
min-width
:
250px
!important
;
...
...
@@ -8257,6 +8227,39 @@ body.reset {
#conversejs
.converse-mobile
#controlbox
#converse-login
input
[
type
=
button
]
{
width
:
auto
;
}
@media
(
max-width
:
767.98px
)
{
#conversejs
:not
(
.converse-embedded
)
{
left
:
0
;
right
:
0
;
padding-left
:
env
(
safe-area-inset-left
);
padding-right
:
env
(
safe-area-inset-right
);
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
{
margin
:
0
!important
;
flex-direction
:
row
!important
;
justify-content
:
space-between
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
.converse-chatroom
{
font-size
:
14px
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
.chatbox
.box-flyout
{
margin-left
:
15px
;
left
:
0
;
bottom
:
0
;
border-radius
:
0
;
width
:
100vw
!important
;
height
:
100vh
!important
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
#controlbox
{
width
:
100vw
!important
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
#controlbox
.box-flyout
{
width
:
100vw
!important
;
height
:
100vh
!important
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes
#controlbox
.sidebar
{
display
:
block
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes.sidebar-open
.chatbox
:not
(
#controlbox
)
{
display
:
none
;
}
#conversejs
:not
(
.converse-embedded
)
.converse-chatboxes.sidebar-open
#controlbox
.controlbox-pane
{
display
:
block
;
}
#conversejs
.converse-overlayed
.converse-chatboxes
.chatbox
.box-flyout
{
margin-left
:
30px
;
}
}
#conversejs
#converse-roster
{
text-align
:
left
;
width
:
100%
;
...
...
dist/converse.js
View file @
2929647e
...
...
@@ -68305,6 +68305,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
utils = _converse$env.utils,
_ = _converse$env._;
const u = converse.env.utils;
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
converse.plugins.add('converse-chatboxes', {
dependencies: ["converse-roster", "converse-vcard"],
overrides: {
...
...
@@ -68615,7 +68616,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'from': _converse.connection.jid,
'to': this.get('jid'),
'type': this.get('message_type'),
'id': message.get('msgid')
'id': message.get('
edited') && _converse.connection.getUniqueId() || message.get('
msgid')
}).c('body').t(message.get('message')).up().c(_converse.ACTIVE, {
'xmlns': Strophe.NS.CHATSTATES
}).up();
...
...
@@ -68638,6 +68639,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}).c('url').t(message.get('message')).up();
}
if (message.get('edited')) {
stanza.c('replace', {
'xmlns': Strophe.NS.MESSAGE_CORRECT,
'id': message.get('msgid')
}).up();
}
return stanza;
},
...
...
@@ -68667,6 +68675,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return {
'fullname': fullname,
'replace': this.correction,
'from': _converse.bare_jid,
'sender': 'me',
'time': moment().format(),
...
...
@@ -68682,7 +68691,24 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
* Parameters:
* (Message) message - The chat message
*/
this.sendMessageStanza(this.messages.create(attrs));
if (attrs.replace) {
const message = this.messages.findWhere({
'id': attrs.replace
});
if (message) {
const older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': attrs.message,
'older_versions': older_versions,
'edited': true
});
return this.sendMessageStanza(message);
}
}
return this.sendMessageStanza(this.messages.create(attrs));
},
sendChatState() {
...
...
@@ -69177,6 +69203,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
});
_converse.on('addClientFeatures', () => {
_converse.api.disco.own.features.add(Strophe.NS.MESSAGE_CORRECT);
_converse.api.disco.own.features.add(Strophe.NS.HTTPUPLOAD);
_converse.api.disco.own.features.add(Strophe.NS.OUTOFBAND);
...
...
@@ -69315,6 +69343,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
const u = converse.env.utils;
const KEY = {
ENTER: 13,
UP_ARROW: 38,
FORWARD_SLASH: 47
};
converse.plugins.add('converse-chatview', {
...
...
@@ -69592,7 +69621,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
'click .toggle-smiley': 'toggleEmojiMenu',
'click .upload-file': 'toggleFileUpload',
'key
press
.chat-textarea': 'keyPressed',
'key
up
.chat-textarea': 'keyPressed',
'input .chat-textarea': 'inputChanged'
},
...
...
@@ -70113,6 +70142,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}
const attrs = this.model.getOutgoingMessageAttributes(text, spoiler_hint);
delete this.model.correction;
this.model.sendMessage(attrs);
},
...
...
@@ -70175,6 +70205,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
*/
if (ev.keyCode === KEY.ENTER && !ev.shiftKey) {
this.onFormSubmitted(ev);
} else if (ev.keyCode === KEY.UP_ARROW && !ev.shiftKey) {
this.editPreviousMessage();
} else if (ev.keyCode !== KEY.FORWARD_SLASH && this.model.get('chat_state') !== _converse.COMPOSING) {
// Set chat state to composing if keyCode is not a forward-slash
// (which would imply an internal command and not a message).
...
...
@@ -70182,6 +70214,19 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}
},
editPreviousMessage() {
const msg = _.findLast(this.model.messages.models, msg => msg.get('message'));
if (msg) {
const textbox_el = this.el.querySelector('.chat-textarea');
textbox_el.value = msg.get('message');
textbox_el.focus(); // We don't set "correcting" the Backbone-way, because
// we don't want it to persist to storage.
this.model.correction = msg.get('id');
}
},
inputChanged(ev) {
ev.target.style.height = 'auto'; // Fixes weirdness
...
...
@@ -71193,7 +71238,6 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob');
Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub');
...
...
@@ -87771,6 +87815,14 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return result;
};
u.getUniqueId = function () {
return 'xxxxxxxx-xxxx'.replace(/[x]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c === 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
};
return u;
});
...
...
@@ -87839,14 +87891,6 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}));
};
u.getUniqueId = function () {
return 'xxxxxxxx-xxxx'.replace(/[x]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c === 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
};
u.xForm2webForm = function (field, stanza, domain) {
/* Takes a field in XMPP XForm (XEP-004: Data Forms) format
* and turns it into an HTML field.
index.html
View file @
2929647e
...
...
@@ -174,6 +174,7 @@
<li>
Server-side archiving of messages (
<a
href=
"http://xmpp.org/extensions/xep-0313.html"
target=
"_blank"
rel=
"noopener"
>
XEP 313
</a>
)
</li>
<li>
Hidden messages (aka Spoilers) (
<a
href=
"http://xmpp.org/extensions/xep-0382.html"
target=
"_blank"
rel=
"noopener"
>
XEP 382
</a>
)
</li>
<li>
Client state indication (
<a
href=
"http://xmpp.org/extensions/xep-0352.html"
target=
"_blank"
rel=
"noopener"
>
XEP 352
</a>
)
</li>
<li>
Last Message Correction (
<a
href=
"http://xmpp.org/extensions/xep-0308.html"
target=
"_blank"
rel=
"noopener"
>
XEP 308
</a>
)
</li>
<li>
Off-the-record encryption
</li>
<li>
Supports anonymous logins, see the
<a
href=
"https://conversejs.org/demo/anonymous.html"
target=
"_blank"
rel=
"noopener"
>
anonymous login demo
</a>
.
</li>
<li>
Translated into 17 languages
</li>
...
...
sass/_controlbox.scss
View file @
2929647e
...
...
@@ -350,59 +350,6 @@
}
}
@include
media-breakpoint-down
(
sm
)
{
#conversejs
:not
(
.converse-embedded
)
{
left
:
0
;
right
:
0
;
padding-left
:
env
(
safe-area-inset-left
);
padding-right
:
env
(
safe-area-inset-right
);
.converse-chatboxes
{
margin
:
0
!
important
;
flex-direction
:
row
!
important
;
justify-content
:
space-between
;
.converse-chatroom
{
font-size
:
14px
;
}
.chatbox
{
.box-flyout
{
margin-left
:
15px
;
// Counteracts Bootstrap margins, but
// not clear why needed...
left
:
0
;
bottom
:
0
;
border-radius
:
0
;
width
:
100vw
!
important
;
height
:
100vh
!
important
;
}
}
#controlbox
{
width
:
100vw
!
important
;
.box-flyout
{
width
:
100vw
!
important
;
height
:
100vh
!
important
;
}
.sidebar
{
display
:
block
;
}
}
&
.sidebar-open
{
.chatbox
:not
(
#controlbox
)
{
display
:
none
;
}
#controlbox
{
.controlbox-pane
{
display
:
block
;
}
}
}
}
}
}
#conversejs
.converse-overlayed
{
#controlbox
{
...
...
@@ -563,3 +510,67 @@
}
}
}
@include
media-breakpoint-down
(
sm
)
{
#conversejs
:not
(
.converse-embedded
)
{
left
:
0
;
right
:
0
;
padding-left
:
env
(
safe-area-inset-left
);
padding-right
:
env
(
safe-area-inset-right
);
.converse-chatboxes
{
margin
:
0
!
important
;
flex-direction
:
row
!
important
;
justify-content
:
space-between
;
.converse-chatroom
{
font-size
:
14px
;
}
.chatbox
{
.box-flyout
{
margin-left
:
15px
;
// Counteracts Bootstrap margins, but
// not clear why needed...
left
:
0
;
bottom
:
0
;
border-radius
:
0
;
width
:
100vw
!
important
;
height
:
100vh
!
important
;
}
}
#controlbox
{
width
:
100vw
!
important
;
.box-flyout
{
width
:
100vw
!
important
;
height
:
100vh
!
important
;
}
.sidebar
{
display
:
block
;
}
}
&
.sidebar-open
{
.chatbox
:not
(
#controlbox
)
{
display
:
none
;
}
#controlbox
{
.controlbox-pane
{
display
:
block
;
}
}
}
}
}
#conversejs
.converse-overlayed
{
.converse-chatboxes
{
.chatbox
{
.box-flyout
{
margin-left
:
30px
;
// Counteracts Bootstrap margins, but
// not clear why needed...
}
}
}
}
}
spec/chatbox.js
View file @
2929647e
...
...
@@ -635,7 +635,7 @@
spyOn
(
_converse
.
connection
,
'
send
'
);
spyOn
(
_converse
,
'
emit
'
);
view
.
keyPressed
({
target
:
$
(
view
.
el
).
find
(
'
textarea.chat-textarea
'
),
target
:
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
),
keyCode
:
1
});
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
composing
'
);
...
...
@@ -648,7 +648,7 @@
// The notification is not sent again
view
.
keyPressed
({
target
:
$
(
view
.
el
).
find
(
'
textarea.chat-textarea
'
),
target
:
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
),
keyCode
:
1
});
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
composing
'
);
...
...
@@ -776,7 +776,7 @@
spyOn
(
view
,
'
setChatState
'
).
and
.
callThrough
();
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
active
'
);
view
.
keyPressed
({
target
:
$
(
view
.
el
).
find
(
'
textarea.chat-textarea
'
),
target
:
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
),
keyCode
:
1
});
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
composing
'
);
...
...
@@ -803,14 +803,14 @@
// out if the user simply types longer than the
// timeout.
view
.
keyPressed
({
target
:
$
(
view
.
el
).
find
(
'
textarea.chat-textarea
'
),
target
:
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
),
keyCode
:
1
});
expect
(
view
.
setChatState
).
toHaveBeenCalled
();
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
composing
'
);
view
.
keyPressed
({
target
:
$
(
view
.
el
).
find
(
'
textarea.chat-textarea
'
),
target
:
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
),
keyCode
:
1
});
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
composing
'
);
...
...
@@ -921,33 +921,25 @@
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
return
test_utils
.
waitUntil
(
function
()
{
return
view
.
model
.
get
(
'
chat_state
'
)
===
'
active
'
;
},
500
);
return
test_utils
.
waitUntil
(()
=>
view
.
model
.
get
(
'
chat_state
'
)
===
'
active
'
,
500
);
}).
then
(
function
()
{
console
.
log
(
'
chat_state set to active
'
);
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
active
'
);
view
.
keyPressed
({
target
:
$
(
view
.
el
).
find
(
'
textarea.chat-textarea
'
),
target
:
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
),
keyCode
:
1
});
return
test_utils
.
waitUntil
(
function
()
{
return
view
.
model
.
get
(
'
chat_state
'
)
===
'
composing
'
;
},
500
);
return
test_utils
.
waitUntil
(()
=>
view
.
model
.
get
(
'
chat_state
'
)
===
'
composing
'
,
500
);
}).
then
(
function
()
{
console
.
log
(
'
chat_state set to composing
'
);
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
expect
(
view
.
model
.
get
(
'
chat_state
'
)).
toBe
(
'
composing
'
);
spyOn
(
_converse
.
connection
,
'
send
'
);
return
test_utils
.
waitUntil
(
function
()
{
return
view
.
model
.
get
(
'
chat_state
'
)
===
'
paused
'
;
},
500
);
return
test_utils
.
waitUntil
(()
=>
view
.
model
.
get
(
'
chat_state
'
)
===
'
paused
'
,
500
);
}).
then
(
function
()
{
console
.
log
(
'
chat_state set to paused
'
);
return
test_utils
.
waitUntil
(
function
()
{
return
view
.
model
.
get
(
'
chat_state
'
)
===
'
inactive
'
;
},
500
);
return
test_utils
.
waitUntil
(()
=>
view
.
model
.
get
(
'
chat_state
'
)
===
'
inactive
'
,
500
);
}).
then
(
function
()
{
console
.
log
(
'
chat_state set to inactive
'
);
expect
(
_converse
.
connection
.
send
).
toHaveBeenCalled
();
...
...
spec/messages.js
View file @
2929647e
...
...
@@ -136,6 +136,70 @@
});
}));
it
(
"
can be sent as a correction
"
,
mock
.
initConverseWithPromises
(
null
,
[
'
rosterGroupsFetched
'
],
{},
function
(
done
,
_converse
)
{
test_utils
.
createContacts
(
_converse
,
'
current
'
,
1
);
test_utils
.
openControlBox
();
const
message
=
'
This is a received message
'
;
const
contact_jid
=
mock
.
cur_names
[
0
].
replace
(
/ /g
,
'
.
'
).
toLowerCase
()
+
'
@localhost
'
;
test_utils
.
openChatBoxFor
(
_converse
,
contact_jid
);
const
view
=
_converse
.
chatboxviews
.
get
(
contact_jid
);
const
textarea
=
view
.
el
.
querySelector
(
'
textarea.chat-textarea
'
);
expect
(
textarea
.
value
).
toBe
(
''
);
view
.
keyPressed
({
target
:
textarea
,
keyCode
:
38
});
expect
(
textarea
.
value
).
toBe
(
''
);
textarea
.
value
=
'
But soft, what light through yonder airlock breaks?
'
;
view
.
keyPressed
({
target
:
textarea
,
preventDefault
:
_
.
noop
,
keyCode
:
13
});
expect
(
view
.
el
.
querySelectorAll
(
'
.chat-msg
'
).
length
).
toBe
(
1
);
expect
(
view
.
el
.
querySelector
(
'
.chat-msg-text
'
).
textContent
)
.
toBe
(
'
But soft, what light through yonder airlock breaks?
'
);
const
first_msg
=
view
.
model
.
messages
.
findWhere
({
'
message
'
:
'
But soft, what light through yonder airlock breaks?
'
});
expect
(
textarea
.
value
).
toBe
(
''
);
view
.
keyPressed
({
target
:
textarea
,
keyCode
:
38
});
expect
(
textarea
.
value
).
toBe
(
'
But soft, what light through yonder airlock breaks?
'
);
spyOn
(
_converse
.
connection
,
'
send
'
);
textarea
.
value
=
'
But soft, what light through yonder window breaks?
'
;
view
.
keyPressed
({
target
:
textarea
,
preventDefault
:
_
.
noop
,
keyCode
:
13
});
expect
(
_converse
.
connection
.
send
).
toHaveBeenCalled
();
const
msg
=
_converse
.
connection
.
send
.
calls
.
all
()[
0
].
args
[
0
];
expect
(
msg
.
toLocaleString
())
.
toBe
(
`<message from='dummy@localhost/resource' `
+
`to='max.frankfurter@localhost' type='chat' id='
${
msg
.
nodeTree
.
getAttribute
(
'
id
'
)}
' `
+
`xmlns='jabber:client'>`
+
`<body>But soft, what light through yonder window breaks?</body>`
+
`<active xmlns='http://jabber.org/protocol/chatstates'/>`
+
`<replace xmlns='urn:xmpp:message-correct:0' id='
${
first_msg
.
get
(
'
msgid
'
)}
'/>`
+
`</message>`
);
expect
(
view
.
model
.
messages
.
models
.
length
).
toBe
(
1
);
const
corrected_message
=
view
.
model
.
messages
.
at
(
0
);
expect
(
corrected_message
.
get
(
'
msgid
'
)).
toBe
(
first_msg
.
get
(
'
msgid
'
));
expect
(
corrected_message
.
get
(
'
older_versions
'
).
length
).
toBe
(
1
);
expect
(
corrected_message
.
get
(
'
older_versions
'
)[
0
]).
toBe
(
'
But soft, what light through yonder airlock breaks?
'
);
done
();
}));
describe
(
"
when a chatbox is opened for someone who is not in the roster
"
,
function
()
{
it
(
"
the VCard for that user is fetched and the chatbox updated with the results
"
,
...
...
spec/presence.js
View file @
2929647e
...
...
@@ -47,7 +47,7 @@
"
<presence xmlns='jabber:client'>
"
+
"
<status>Hello world</status>
"
+
"
<priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
1J7kq1MEvnB6ea6vKcgCsSE37gw
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
wmJWAEmiBuDhg0VUoDmqHp3qXJ0
='/>
"
+
"
</presence>
"
);
_converse
.
priority
=
2
;
...
...
@@ -57,7 +57,7 @@
"
<show>away</show>
"
+
"
<status>Going jogging</status>
"
+
"
<priority>2</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
1J7kq1MEvnB6ea6vKcgCsSE37gw
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
wmJWAEmiBuDhg0VUoDmqHp3qXJ0
='/>
"
+
"
</presence>
"
);
...
...
@@ -68,7 +68,7 @@
"
<show>dnd</show>
"
+
"
<status>Doing taxes</status>
"
+
"
<priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
1J7kq1MEvnB6ea6vKcgCsSE37gw
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
wmJWAEmiBuDhg0VUoDmqHp3qXJ0
='/>
"
+
"
</presence>
"
);
}));
...
...
@@ -97,7 +97,7 @@
.
toBe
(
"
<presence xmlns='jabber:client'>
"
+
"
<status>My custom status</status>
"
+
"
<priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
1J7kq1MEvnB6ea6vKcgCsSE37gw
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
wmJWAEmiBuDhg0VUoDmqHp3qXJ0
='/>
"
+
"
</presence>
"
)
return
test_utils
.
waitUntil
(
function
()
{
...
...
@@ -113,7 +113,7 @@
modal
.
el
.
querySelector
(
'
[type="submit"]
'
).
click
();
expect
(
_converse
.
connection
.
send
.
calls
.
mostRecent
().
args
[
0
].
toLocaleString
())
.
toBe
(
"
<presence xmlns='jabber:client'><show>dnd</show><status>My custom status</status><priority>0</priority>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
1J7kq1MEvnB6ea6vKcgCsSE37gw
='/>
"
+
"
<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='
wmJWAEmiBuDhg0VUoDmqHp3qXJ0
='/>
"
+
"
</presence>
"
)
done
();
});
...
...
src/converse-chatboxes.js
View file @
2929647e
...
...
@@ -19,6 +19,8 @@
const
{
$msg
,
Backbone
,
Promise
,
Strophe
,
b64_sha1
,
moment
,
sizzle
,
utils
,
_
}
=
converse
.
env
;
const
u
=
converse
.
env
.
utils
;
Strophe
.
addNamespace
(
'
MESSAGE_CORRECT
'
,
'
urn:xmpp:message-correct:0
'
);
converse
.
plugins
.
add
(
'
converse-chatboxes
'
,
{
...
...
@@ -314,7 +316,7 @@
'
from
'
:
_converse
.
connection
.
jid
,
'
to
'
:
this
.
get
(
'
jid
'
),
'
type
'
:
this
.
get
(
'
message_type
'
),
'
id
'
:
message
.
get
(
'
msgid
'
)
'
id
'
:
message
.
get
(
'
edited
'
)
&&
_converse
.
connection
.
getUniqueId
()
||
message
.
get
(
'
msgid
'
),
}).
c
(
'
body
'
).
t
(
message
.
get
(
'
message
'
)).
up
()
.
c
(
_converse
.
ACTIVE
,
{
'
xmlns
'
:
Strophe
.
NS
.
CHATSTATES
}).
up
();
...
...
@@ -328,6 +330,12 @@
if
(
message
.
get
(
'
file
'
))
{
stanza
.
c
(
'
x
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
OUTOFBAND
}).
c
(
'
url
'
).
t
(
message
.
get
(
'
message
'
)).
up
();
}
if
(
message
.
get
(
'
edited
'
))
{
stanza
.
c
(
'
replace
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
MESSAGE_CORRECT
,
'
id
'
:
message
.
get
(
'
msgid
'
)
}).
up
();
}
return
stanza
;
},
...
...
@@ -357,6 +365,7 @@
return
{
'
fullname
'
:
fullname
,
'
replace
'
:
this
.
correction
,
'
from
'
:
_converse
.
bare_jid
,
'
sender
'
:
'
me
'
,
'
time
'
:
moment
().
format
(),
...
...
@@ -372,7 +381,20 @@
* Parameters:
* (Message) message - The chat message
*/
this
.
sendMessageStanza
(
this
.
messages
.
create
(
attrs
));
if
(
attrs
.
replace
)
{
const
message
=
this
.
messages
.
findWhere
({
'
id
'
:
attrs
.
replace
})
if
(
message
)
{
const
older_versions
=
message
.
get
(
'
older_versions
'
)
||
[];
older_versions
.
push
(
message
.
get
(
'
message
'
));
message
.
save
({
'
message
'
:
attrs
.
message
,
'
older_versions
'
:
older_versions
,
'
edited
'
:
true
});
return
this
.
sendMessageStanza
(
message
);
}
}
return
this
.
sendMessageStanza
(
this
.
messages
.
create
(
attrs
));
},
sendChatState
()
{
...
...
@@ -826,6 +848,7 @@
_converse
.
on
(
'
addClientFeatures
'
,
()
=>
{
_converse
.
api
.
disco
.
own
.
features
.
add
(
Strophe
.
NS
.
MESSAGE_CORRECT
);
_converse
.
api
.
disco
.
own
.
features
.
add
(
Strophe
.
NS
.
HTTPUPLOAD
);
_converse
.
api
.
disco
.
own
.
features
.
add
(
Strophe
.
NS
.
OUTOFBAND
);
});
...
...
src/converse-chatview.js
View file @
2929647e
...
...
@@ -54,6 +54,7 @@
const
u
=
converse
.
env
.
utils
;
const
KEY
=
{
ENTER
:
13
,
UP_ARROW
:
38
,
FORWARD_SLASH
:
47
};
...
...
@@ -333,7 +334,7 @@
'
click .toggle-smiley ul.emoji-picker li
'
:
'
insertEmoji
'
,
'
click .toggle-smiley
'
:
'
toggleEmojiMenu
'
,
'
click .upload-file
'
:
'
toggleFileUpload
'
,
'
key
press
.chat-textarea
'
:
'
keyPressed
'
,
'
key
up
.chat-textarea
'
:
'
keyPressed
'
,
'
input .chat-textarea
'
:
'
inputChanged
'
},
...
...
@@ -847,6 +848,7 @@
return
;
}
const
attrs
=
this
.
model
.
getOutgoingMessageAttributes
(
text
,
spoiler_hint
);
delete
this
.
model
.
correction
;
this
.
model
.
sendMessage
(
attrs
);
},
...
...
@@ -912,6 +914,8 @@
*/
if
(
ev
.
keyCode
===
KEY
.
ENTER
&&
!
ev
.
shiftKey
)
{
this
.
onFormSubmitted
(
ev
);
}
else
if
(
ev
.
keyCode
===
KEY
.
UP_ARROW
&&
!
ev
.
shiftKey
)
{
this
.
editPreviousMessage
();
}
else
if
(
ev
.
keyCode
!==
KEY
.
FORWARD_SLASH
&&
this
.
model
.
get
(
'
chat_state
'
)
!==
_converse
.
COMPOSING
)
{
// Set chat state to composing if keyCode is not a forward-slash
// (which would imply an internal command and not a message).
...
...
@@ -919,6 +923,18 @@
}
},
editPreviousMessage
()
{
const
msg
=
_
.
findLast
(
this
.
model
.
messages
.
models
,
(
msg
)
=>
msg
.
get
(
'
message
'
));
if
(
msg
)
{
const
textbox_el
=
this
.
el
.
querySelector
(
'
.chat-textarea
'
);
textbox_el
.
value
=
msg
.
get
(
'
message
'
);
textbox_el
.
focus
()
// We don't set "correcting" the Backbone-way, because
// we don't want it to persist to storage.
this
.
model
.
correction
=
msg
.
get
(
'
id
'
);
}
},
inputChanged
(
ev
)
{
ev
.
target
.
style
.
height
=
'
auto
'
;
// Fixes weirdness
ev
.
target
.
style
.
height
=
(
ev
.
target
.
scrollHeight
)
+
'
px
'
;
...
...
src/converse-core.js
View file @
2929647e
...
...
@@ -36,7 +36,6 @@
Strophe
.
addNamespace
(
'
HINTS
'
,
'
urn:xmpp:hints
'
);
Strophe
.
addNamespace
(
'
HTTPUPLOAD
'
,
'
urn:xmpp:http:upload:0
'
);
Strophe
.
addNamespace
(
'
MAM
'
,
'
urn:xmpp:mam:2
'
);
Strophe
.
addNamespace
(
'
MESSAGE_CORRECT
'
,
'
urn:xmpp:message-correct:0
'
);
Strophe
.
addNamespace
(
'
NICK
'
,
'
http://jabber.org/protocol/nick
'
);
Strophe
.
addNamespace
(
'
OUTOFBAND
'
,
'
jabber:x:oob
'
);
Strophe
.
addNamespace
(
'
PUBSUB
'
,
'
http://jabber.org/protocol/pubsub
'
);
...
...
src/utils/core.js
View file @
2929647e
...
...
@@ -844,5 +844,12 @@
return
result
;
};
u
.
getUniqueId
=
function
()
{
return
'
xxxxxxxx-xxxx
'
.
replace
(
/
[
x
]
/g
,
function
(
c
)
{
var
r
=
Math
.
random
()
*
16
|
0
,
v
=
c
===
'
x
'
?
r
:
r
&
0x3
|
0x8
;
return
v
.
toString
(
16
);
});
};
return
u
;
}));
src/utils/form.js
View file @
2929647e
...
...
@@ -73,14 +73,6 @@
);
};
u
.
getUniqueId
=
function
()
{
return
'
xxxxxxxx-xxxx
'
.
replace
(
/
[
x
]
/g
,
function
(
c
)
{
var
r
=
Math
.
random
()
*
16
|
0
,
v
=
c
===
'
x
'
?
r
:
r
&
0x3
|
0x8
;
return
v
.
toString
(
16
);
});
};
u
.
xForm2webForm
=
function
(
field
,
stanza
,
domain
)
{
/* Takes a field in XMPP XForm (XEP-004: Data Forms) format
* and turns it into an HTML field.
...
...
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