Cancel message correction by pressing the down arrow
Also, add a class `correcting` to the message being corrected, to provide a visual cue. updates #421
This commit is contained in:
parent
39c85db181
commit
84a10d77d9
@ -34,7 +34,7 @@
|
||||
"array-bracket-spacing": "off",
|
||||
"array-callback-return": "error",
|
||||
"arrow-body-style": "off",
|
||||
"arrow-parens": "error",
|
||||
"arrow-parens": "off",
|
||||
"arrow-spacing": "error",
|
||||
"block-scoped-var": "off",
|
||||
"block-spacing": "off",
|
||||
|
@ -8859,6 +8859,10 @@ body.reset {
|
||||
-webkit-animation: colorchange-chatmessage 1s; }
|
||||
#conversejs .message.chat-msg:hover {
|
||||
background-color: rgba(0, 0, 0, 0.035); }
|
||||
#conversejs .message.chat-msg.correcting.groupchat {
|
||||
background-color: #fdf1ee; }
|
||||
#conversejs .message.chat-msg.correcting:not(.groupchat) {
|
||||
background-color: #e7f7ee; }
|
||||
#conversejs .message.chat-msg .spoiler {
|
||||
margin-top: 0.5em; }
|
||||
#conversejs .message.chat-msg .spoiler-hint {
|
||||
|
60
dist/converse.js
vendored
60
dist/converse.js
vendored
@ -68675,7 +68675,6 @@ 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(),
|
||||
@ -68691,10 +68690,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
* Parameters:
|
||||
* (Message) message - The chat message
|
||||
*/
|
||||
if (attrs.replace) {
|
||||
const message = this.messages.findWhere({
|
||||
'id': attrs.replace
|
||||
});
|
||||
const message = this.messages.findWhere('correcting');
|
||||
|
||||
if (message) {
|
||||
const older_versions = message.get('older_versions') || [];
|
||||
@ -68702,11 +68698,11 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
message.save({
|
||||
'message': attrs.message,
|
||||
'older_versions': older_versions,
|
||||
'edited': true
|
||||
'edited': true,
|
||||
'correcting': false
|
||||
});
|
||||
return this.sendMessageStanza(message);
|
||||
}
|
||||
}
|
||||
|
||||
return this.sendMessageStanza(this.messages.create(attrs));
|
||||
},
|
||||
@ -69344,6 +69340,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
const KEY = {
|
||||
ENTER: 13,
|
||||
UP_ARROW: 38,
|
||||
DOWN_ARROW: 40,
|
||||
FORWARD_SLASH: 47
|
||||
};
|
||||
converse.plugins.add('converse-chatview', {
|
||||
@ -69621,7 +69618,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',
|
||||
'keyup .chat-textarea': 'keyPressed',
|
||||
'keydown .chat-textarea': 'keyPressed',
|
||||
'input .chat-textarea': 'inputChanged'
|
||||
},
|
||||
|
||||
@ -70103,6 +70100,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
*/
|
||||
this.showMessage(message);
|
||||
|
||||
if (message.get('correcting')) {
|
||||
this.insertIntoTextArea(message.get('message'), true);
|
||||
}
|
||||
|
||||
_converse.emit('messageAdded', {
|
||||
'message': message,
|
||||
'chatbox': this.model
|
||||
@ -70142,7 +70143,6 @@ 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);
|
||||
},
|
||||
|
||||
@ -70203,10 +70203,16 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
keyPressed(ev) {
|
||||
/* Event handler for when a key is pressed in a chat box textarea.
|
||||
*/
|
||||
if (ev.keyCode === KEY.ENTER && !ev.shiftKey) {
|
||||
if (ev.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.keyCode === KEY.ENTER) {
|
||||
this.onFormSubmitted(ev);
|
||||
} else if (ev.keyCode === KEY.UP_ARROW && !ev.shiftKey) {
|
||||
} else if (ev.keyCode === KEY.UP_ARROW && !ev.target.selectionEnd) {
|
||||
this.editPreviousMessage();
|
||||
} else if (ev.keyCode === KEY.DOWN_ARROW && ev.target.selectionEnd === ev.target.value.length) {
|
||||
this.cancelMessageCorrection();
|
||||
} 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).
|
||||
@ -70214,16 +70220,19 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
}
|
||||
},
|
||||
|
||||
cancelMessageCorrection() {
|
||||
this.insertIntoTextArea('', true);
|
||||
this.model.messages.where('correcting').forEach(msg => msg.save('correcting', false));
|
||||
},
|
||||
|
||||
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
|
||||
this.insertIntoTextArea(msg.get('message'), true); // We don't set "correcting" the Backbone-way, because
|
||||
// we don't want it to persist to storage.
|
||||
|
||||
this.model.correction = msg.get('id');
|
||||
msg.save('correcting', true);
|
||||
}
|
||||
},
|
||||
|
||||
@ -70250,8 +70259,12 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
return this;
|
||||
},
|
||||
|
||||
insertIntoTextArea(value) {
|
||||
insertIntoTextArea(value, replace = false) {
|
||||
const textbox_el = this.el.querySelector('.chat-textarea');
|
||||
|
||||
if (replace) {
|
||||
textbox_el.value = value;
|
||||
} else {
|
||||
let existing = textbox_el.value;
|
||||
|
||||
if (existing && existing[existing.length - 1] !== ' ') {
|
||||
@ -70259,6 +70272,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
}
|
||||
|
||||
textbox_el.value = existing + value + ' ';
|
||||
}
|
||||
|
||||
textbox_el.focus();
|
||||
},
|
||||
|
||||
@ -74572,10 +74587,11 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
|
||||
initialize() {
|
||||
this.model.vcard.on('change', this.render, this);
|
||||
this.model.on('change:correcting', this.render, this);
|
||||
this.model.on('change:message', this.render, this);
|
||||
this.model.on('change:progress', this.renderFileUploadProgresBar, this);
|
||||
this.model.on('change:type', this.render, this);
|
||||
this.model.on('change:upload', this.render, this);
|
||||
this.model.on('change:message', this.render, this);
|
||||
this.model.on('destroy', this.remove, this);
|
||||
this.render();
|
||||
},
|
||||
@ -74746,6 +74762,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
|
||||
}
|
||||
}
|
||||
|
||||
if (this.model.get('correcting')) {
|
||||
extra_classes += ' correcting';
|
||||
}
|
||||
|
||||
return extra_classes;
|
||||
}
|
||||
|
||||
@ -75997,7 +76017,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',
|
||||
'keypress .chat-textarea': 'keyPressed',
|
||||
'keydown .chat-textarea': 'keyPressed',
|
||||
'input .chat-textarea': 'inputChanged'
|
||||
},
|
||||
|
||||
@ -85632,13 +85652,13 @@ __p += '\n </span>\n <time timestamp="' +
|
||||
__e(o.isodate) +
|
||||
'" class="chat-msg-time">' +
|
||||
__e(o.pretty_time) +
|
||||
'</time>\n </span>\n <span class="chat-msg-text"></span>\n <div class="chat-msg-media"></div>\n ';
|
||||
'</time>\n </span>\n ';
|
||||
if (o.edited) { ;
|
||||
__p += ' <i title="' +
|
||||
__e(o.__('This message has been edited')) +
|
||||
'" class="fa fa-edit chat-msg-edited"></i> ';
|
||||
} ;
|
||||
__p += '\n </div>\n</div>\n';
|
||||
__p += '\n <span class="chat-msg-text"></span>\n <div class="chat-msg-media"></div>\n </div>\n</div>\n';
|
||||
return __p
|
||||
};
|
||||
|
||||
|
@ -73,6 +73,14 @@
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.035);
|
||||
}
|
||||
&.correcting {
|
||||
&.groupchat {
|
||||
background-color: lighten($chatroom-head-color, 35%);
|
||||
}
|
||||
&:not(.groupchat) {
|
||||
background-color: lighten($chat-head-color, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
.spoiler {
|
||||
margin-top: 0.5em;
|
||||
|
249
spec/messages.js
249
spec/messages.js
@ -19,6 +19,97 @@
|
||||
|
||||
describe("A Chat Message", function () {
|
||||
|
||||
it("can be sent as a correction",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
test_utils.openControlBox();
|
||||
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 // Up arrow
|
||||
});
|
||||
expect(textarea.value).toBe('');
|
||||
|
||||
textarea.value = 'But soft, what light through yonder airlock breaks?';
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
preventDefault: _.noop,
|
||||
keyCode: 13 // Enter
|
||||
});
|
||||
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 // Up arrow
|
||||
});
|
||||
expect(textarea.value).toBe('But soft, what light through yonder airlock breaks?');
|
||||
expect(view.model.messages.at(0).get('correcting')).toBe(true);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
|
||||
|
||||
spyOn(_converse.connection, 'send');
|
||||
textarea.value = 'But soft, what light through yonder window breaks?';
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
preventDefault: _.noop,
|
||||
keyCode: 13 // Enter
|
||||
});
|
||||
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('correcting')).toBe(false);
|
||||
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?');
|
||||
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
|
||||
|
||||
// Test that pressing the down arrow cancels message correction
|
||||
expect(textarea.value).toBe('');
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
keyCode: 38 // Up arrow
|
||||
});
|
||||
expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
|
||||
expect(view.model.messages.at(0).get('correcting')).toBe(true);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
|
||||
expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
keyCode: 40 // Down arrow
|
||||
});
|
||||
expect(textarea.value).toBe('');
|
||||
expect(view.model.messages.at(0).get('correcting')).toBe(false);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
describe("when received from someone else", function () {
|
||||
|
||||
it("will open a chatbox and be displayed inside it",
|
||||
@ -136,70 +227,6 @@
|
||||
});
|
||||
}));
|
||||
|
||||
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",
|
||||
@ -1663,7 +1690,6 @@
|
||||
function (done, _converse) {
|
||||
|
||||
let msg_id, view;
|
||||
|
||||
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy')
|
||||
.then(() => {
|
||||
const jid = 'lounge@localhost';
|
||||
@ -1733,5 +1759,98 @@
|
||||
done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("can be sent as a correction",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let msg_id, view;
|
||||
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy')
|
||||
.then(() => {
|
||||
const jid = 'lounge@localhost';
|
||||
const room = _converse.api.rooms.get(jid);
|
||||
view = _converse.chatboxviews.get(jid);
|
||||
|
||||
const textarea = view.el.querySelector('textarea.chat-textarea');
|
||||
expect(textarea.value).toBe('');
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
keyCode: 38 // Up arrow
|
||||
});
|
||||
expect(textarea.value).toBe('');
|
||||
|
||||
textarea.value = 'But soft, what light through yonder airlock breaks?';
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
preventDefault: _.noop,
|
||||
keyCode: 13 // Enter
|
||||
});
|
||||
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 // Up arrow
|
||||
});
|
||||
expect(textarea.value).toBe('But soft, what light through yonder airlock breaks?');
|
||||
expect(view.model.messages.at(0).get('correcting')).toBe(true);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
|
||||
|
||||
spyOn(_converse.connection, 'send');
|
||||
textarea.value = 'But soft, what light through yonder window breaks?';
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
preventDefault: _.noop,
|
||||
keyCode: 13 // Enter
|
||||
});
|
||||
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='lounge@localhost' type='groupchat' 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('correcting')).toBe(false);
|
||||
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?');
|
||||
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
|
||||
|
||||
// Test that pressing the down arrow cancels message correction
|
||||
expect(textarea.value).toBe('');
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
keyCode: 38 // Up arrow
|
||||
});
|
||||
expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
|
||||
expect(view.model.messages.at(0).get('correcting')).toBe(true);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
|
||||
expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
keyCode: 40 // Down arrow
|
||||
});
|
||||
expect(textarea.value).toBe('');
|
||||
expect(view.model.messages.at(0).get('correcting')).toBe(false);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
|
||||
done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}));
|
||||
|
@ -365,7 +365,6 @@
|
||||
|
||||
return {
|
||||
'fullname': fullname,
|
||||
'replace': this.correction,
|
||||
'from': _converse.bare_jid,
|
||||
'sender': 'me',
|
||||
'time': moment().format(),
|
||||
@ -381,19 +380,18 @@
|
||||
* Parameters:
|
||||
* (Message) message - The chat message
|
||||
*/
|
||||
if (attrs.replace) {
|
||||
const message = this.messages.findWhere({'id': attrs.replace})
|
||||
const message = this.messages.findWhere('correcting')
|
||||
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
|
||||
'edited': true,
|
||||
'correcting': false
|
||||
});
|
||||
return this.sendMessageStanza(message);
|
||||
}
|
||||
}
|
||||
return this.sendMessageStanza(this.messages.create(attrs));
|
||||
},
|
||||
|
||||
|
@ -55,6 +55,7 @@
|
||||
const KEY = {
|
||||
ENTER: 13,
|
||||
UP_ARROW: 38,
|
||||
DOWN_ARROW: 40,
|
||||
FORWARD_SLASH: 47
|
||||
};
|
||||
|
||||
@ -334,7 +335,7 @@
|
||||
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
|
||||
'click .toggle-smiley': 'toggleEmojiMenu',
|
||||
'click .upload-file': 'toggleFileUpload',
|
||||
'keyup .chat-textarea': 'keyPressed',
|
||||
'keydown .chat-textarea': 'keyPressed',
|
||||
'input .chat-textarea': 'inputChanged'
|
||||
},
|
||||
|
||||
@ -802,7 +803,9 @@
|
||||
* (Object) message - The message Backbone object that was added.
|
||||
*/
|
||||
this.showMessage(message);
|
||||
|
||||
if (message.get('correcting')) {
|
||||
this.insertIntoTextArea(message.get('message'), true);
|
||||
}
|
||||
_converse.emit('messageAdded', {
|
||||
'message': message,
|
||||
'chatbox': this.model
|
||||
@ -848,7 +851,6 @@
|
||||
return;
|
||||
}
|
||||
const attrs = this.model.getOutgoingMessageAttributes(text, spoiler_hint);
|
||||
delete this.model.correction;
|
||||
this.model.sendMessage(attrs);
|
||||
},
|
||||
|
||||
@ -912,10 +914,14 @@
|
||||
keyPressed (ev) {
|
||||
/* Event handler for when a key is pressed in a chat box textarea.
|
||||
*/
|
||||
if (ev.keyCode === KEY.ENTER && !ev.shiftKey) {
|
||||
if (ev.shiftKey) { return; }
|
||||
|
||||
if (ev.keyCode === KEY.ENTER) {
|
||||
this.onFormSubmitted(ev);
|
||||
} else if (ev.keyCode === KEY.UP_ARROW && !ev.shiftKey) {
|
||||
} else if (ev.keyCode === KEY.UP_ARROW && !ev.target.selectionEnd) {
|
||||
this.editPreviousMessage();
|
||||
} else if (ev.keyCode === KEY.DOWN_ARROW && ev.target.selectionEnd === ev.target.value.length) {
|
||||
this.cancelMessageCorrection();
|
||||
} 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).
|
||||
@ -923,15 +929,18 @@
|
||||
}
|
||||
},
|
||||
|
||||
cancelMessageCorrection () {
|
||||
this.insertIntoTextArea('', true);
|
||||
this.model.messages.where('correcting').forEach(msg => msg.save('correcting', false));
|
||||
},
|
||||
|
||||
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()
|
||||
this.insertIntoTextArea(msg.get('message'), true);
|
||||
// We don't set "correcting" the Backbone-way, because
|
||||
// we don't want it to persist to storage.
|
||||
this.model.correction = msg.get('id');
|
||||
msg.save('correcting', true);
|
||||
}
|
||||
},
|
||||
|
||||
@ -951,13 +960,17 @@
|
||||
return this;
|
||||
},
|
||||
|
||||
insertIntoTextArea (value) {
|
||||
insertIntoTextArea (value, replace=false) {
|
||||
const textbox_el = this.el.querySelector('.chat-textarea');
|
||||
if (replace) {
|
||||
textbox_el.value = value;
|
||||
} else {
|
||||
let existing = textbox_el.value;
|
||||
if (existing && (existing[existing.length-1] !== ' ')) {
|
||||
existing = existing + ' ';
|
||||
}
|
||||
textbox_el.value = existing+value+' ';
|
||||
}
|
||||
textbox_el.focus()
|
||||
},
|
||||
|
||||
|
@ -91,10 +91,11 @@
|
||||
|
||||
initialize () {
|
||||
this.model.vcard.on('change', this.render, this);
|
||||
this.model.on('change:correcting', this.render, this);
|
||||
this.model.on('change:message', this.render, this);
|
||||
this.model.on('change:progress', this.renderFileUploadProgresBar, this);
|
||||
this.model.on('change:type', this.render, this);
|
||||
this.model.on('change:upload', this.render, this);
|
||||
this.model.on('change:message', this.render, this);
|
||||
this.model.on('destroy', this.remove, this);
|
||||
this.render();
|
||||
},
|
||||
@ -262,6 +263,9 @@
|
||||
extra_classes += ' mentioned';
|
||||
}
|
||||
}
|
||||
if (this.model.get('correcting')) {
|
||||
extra_classes += ' correcting';
|
||||
}
|
||||
return extra_classes;
|
||||
}
|
||||
});
|
||||
|
@ -529,7 +529,7 @@
|
||||
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
|
||||
'click .toggle-smiley': 'toggleEmojiMenu',
|
||||
'click .upload-file': 'toggleFileUpload',
|
||||
'keypress .chat-textarea': 'keyPressed',
|
||||
'keydown .chat-textarea': 'keyPressed',
|
||||
'input .chat-textarea': 'inputChanged'
|
||||
},
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
</span>
|
||||
<time timestamp="{{{o.isodate}}}" class="chat-msg-time">{{{o.pretty_time}}}</time>
|
||||
</span>
|
||||
{[ if (o.edited) { ]} <i title="{{{o.__('This message has been edited')}}}" class="fa fa-edit chat-msg-edited"></i> {[ } ]}
|
||||
<span class="chat-msg-text"></span>
|
||||
<div class="chat-msg-media"></div>
|
||||
{[ if (o.edited) { ]} <i title="{{{o.__('This message has been edited')}}}" class="fa fa-edit chat-msg-edited"></i> {[ } ]}
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user