Add support for message corrections in MUC.

This commit is contained in:
JC Brand 2018-07-16 00:49:00 +02:00
parent 16faf62d3f
commit 6717315d05
5 changed files with 113 additions and 737 deletions

77
dist/converse.js vendored
View File

@ -68341,19 +68341,6 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
_converse.api.promises.add(['chatBoxesFetched', 'chatBoxesInitialized', 'privateChatsAutoJoined']);
function getMessageBody(stanza) {
/* Given a message stanza, return the text contained in its body.
*/
const type = stanza.getAttribute('type');
if (type === 'error') {
const error = stanza.querySelector('error');
return _.propertyOf(error.querySelector('text'))('textContent') || __('Sorry, an error occurred:') + ' ' + error.innerHTML;
} else {
return _.propertyOf(stanza.querySelector('body'))('textContent');
}
}
function openChat(jid) {
if (!utils.isValidJID(jid)) {
return _converse.log(`Invalid JID "${jid}" provided in URL fragment`, Strophe.LogLevel.WARN);
@ -68605,6 +68592,27 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return this.vcard.get('fullname') || this.get('jid');
},
handleMessageCorrection(stanza) {
const replace = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop();
if (replace) {
const msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
message = msgid && this.messages.findWhere({
msgid
}),
older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': _converse.chatboxes.getMessageBody(stanza),
'older_versions': older_versions,
'edited': true
});
return true;
}
return false;
},
createMessageStanza(message) {
/* Given a _converse.Message Backbone.Model, return the XML
* stanza that represents it.
@ -68786,7 +68794,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'is_archived': !_.isNil(archive),
'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler),
'message': getMessageBody(stanza) || undefined,
'message': _converse.chatboxes.getMessageBody(stanza) || undefined,
'msgid': stanza.getAttribute('id'),
'time': delay ? delay.getAttribute('stamp') : moment().format(),
'type': stanza.getAttribute('type')
@ -68941,6 +68949,19 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return true;
},
getMessageBody(stanza) {
/* Given a message stanza, return the text contained in its body.
*/
const type = stanza.getAttribute('type');
if (type === 'error') {
const error = stanza.querySelector('error');
return _.propertyOf(error.querySelector('text'))('textContent') || __('Sorry, an error occurred:') + ' ' + error.innerHTML;
} else {
return _.propertyOf(stanza.querySelector('body'))('textContent');
}
},
onMessage(stanza) {
/* Handler method for all incoming single-user chat "message"
* stanzas.
@ -69002,22 +69023,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
};
const chatbox = this.getChatBox(contact_jid, attrs, !_.isNull(stanza.querySelector('body')));
if (chatbox) {
const replace = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop(),
msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
const msgid = stanza.getAttribute('id'),
message = msgid && chatbox.messages.findWhere({
msgid
});
if (replace) {
const older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': getMessageBody(stanza),
'older_versions': older_versions,
'edited': true
});
} else if (!message) {
if (!message) {
// Only create the message when we're sure it's not a duplicate
chatbox.incrementUnreadMsgCounter(original_stanza);
chatbox.createMessage(stanza, original_stanza);
@ -78483,15 +78495,17 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
stanza = forwarded.querySelector('message');
}
const jid = stanza.getAttribute('from'),
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '',
subject = _.propertyOf(stanza.querySelector('subject'))('textContent');
if (this.isDuplicate(stanza, original_stanza)) {
return;
}
const jid = stanza.getAttribute('from'),
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '';
if (!this.handleMessageCorrection(stanza)) {
const subject = _.propertyOf(stanza.querySelector('subject'))('textContent');
if (subject) {
u.safeSave(this, {
'subject': {
@ -78507,6 +78521,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
this.incrementUnreadMsgCounter(original_stanza);
this.createMessage(stanza, original_stanza);
}
if (sender !== this.get('nick')) {
// We only emit an event if it's not our own message

View File

@ -1,643 +0,0 @@
(function (root, factory) {
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
"": {
"Project-Id-Version": "Converse.js 0.6.3",
"Report-Msgid-Bugs-To": "",
"POT-Creation-Date": "2013-09-15 21:55+0200",
"PO-Revision-Date": "2014-07-07 11:02+0200",
"Last-Translator": "Alan Meira <alan@engarte.com>",
"Language-Team": "Brazilian Portuguese",
"Language": "pt_BR",
"MIME-Version": "1.0",
"Content-Type": "text/plain; charset=UTF-8",
"Content-Transfer-Encoding": "8bit",
"Plural-Forms": "nplurals=2; plural=(n > 1);",
"domain": "converse",
"lang": "pt_BR",
"plural_forms": "nplurals=2; plural=(n != 1);"
},
"unencrypted": [
null,
"não-criptografado"
],
"unverified": [
null,
"não-verificado"
],
"verified": [
null,
"não-verificado"
],
"finished": [
null,
"finalizado"
],
"Disconnected": [
null,
"Desconectado"
],
"Error": [
null,
"Erro"
],
"Connecting": [
null,
"Conectando"
],
"Connection Failed": [
null,
"Falha de conexão"
],
"Authenticating": [
null,
"Autenticando"
],
"Authentication Failed": [
null,
"Falha de autenticação"
],
"Disconnecting": [
null,
"Desconectando"
],
"Re-establishing encrypted session": [
null,
"Reestabelecendo sessão criptografada"
],
"Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
null,
"Seu navegador precisa gerar uma chave-privada, que será usada em sua sessão criptografada de bate-papo. Isso pode levar até 30 segundos durante os quais seu navegador poderá se travar ou não responder."
],
"Private key generated.": [
null,
"Chave-privada gerada:"
],
"Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
null,
"Pedido de autenticação de %$s\n\nSeu contato está tentando verificar sua identidade, perguntando a questão abaixo.\n\n%2$s"
],
"Could not verify this user's identify.": [
null,
"Não foi possível verificar a identidade deste usuário."
],
"Personal message": [
null,
"Mensagem pessoal"
],
"Start encrypted conversation": [
null,
"Iniciar conversa criptografada"
],
"Refresh encrypted conversation": [
null,
"Atualizar conversa criptografada"
],
"End encrypted conversation": [
null,
"Finalizar conversa criptografada"
],
"Verify with SMP": [
null,
"Verificar com SMP"
],
"Verify with fingerprints": [
null,
"Verificar com assinatura digital"
],
"What's this?": [
null,
"O que é isso?"
],
"me": [
null,
"eu"
],
"Show this menu": [
null,
"Mostrar o menu"
],
"Write in the third person": [
null,
"Escrever em terceira pessoa"
],
"Remove messages": [
null,
"Remover mensagens"
],
"Your message could not be sent": [
null,
"Sua mensagem não pôde ser enviada"
],
"We received an unencrypted message": [
null,
"Recebemos uma mensagem não-criptografada"
],
"We received an unreadable encrypted message": [
null,
"Recebemos uma mensagem não-criptografada ilegível"
],
"This user has requested an encrypted session.": [
null,
"Usuário pediu uma sessão criptografada."
],
"Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
null,
"Aqui estão as assinaturas digitais, por favor confirme elas com %1$s, fora deste chat.\n\nAssinaturas para você, %2$s: %3$s\n\nAssinaturas para %1$s: %4$s\n\nSe você tiver confirmado que as assinaturas conferem, clique OK, caso contrário, clique Cancel."
],
"You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
null,
"Será solicitado que você informe uma pergunta de segurança e também uma resposta.\n\nNós iremos, então, transfeir a pergunta para seu contato e caso ele envie corretamente a mesma resposta (caso sensitivo), a identidade dele será verificada."
],
"What is your security question?": [
null,
"Qual é a sua pergunta de segurança?"
],
"What is the answer to the security question?": [
null,
"Qual é a resposta para a pergunta de segurança?"
],
"Invalid authentication scheme provided": [
null,
"Schema de autenticação fornecido é inválido"
],
"Your messages are not encrypted anymore": [
null,
"Suas mensagens não estão mais criptografadas"
],
"Your messages are now encrypted but your buddy's identity has not been verified.": [
null,
"Suas mensagens estão agora criptografadas mas a identidade do contato não foi confirmada."
],
"Your buddy's identify has been verified.": [
null,
"A identidade do contato foi verificada."
],
"Your buddy has ended encryption on their end, you should do the same.": [
null,
"Seu contato parou de usar criptografia, você deveria fazer o mesmo."
],
"Your messages are not encrypted. Click here to enable OTR encryption.": [
null,
"Suas mensagens não estão criptografadas. Clique aqui para habilitar criptografia OTR."
],
"Your messages are encrypted, but your buddy has not been verified.": [
null,
"Suas mensagens estão criptografadas, mas seu contato não foi verificado."
],
"Your messages are encrypted and your buddy verified.": [
null,
"Suas mensagens estão criptografadas e seu contato verificado."
],
"Your buddy has closed their end of the private session, you should do the same": [
null,
"Seu contato fechou a sessão privada, você deveria fazer o mesmo"
],
"Contacts": [
null,
"Contatos"
],
"Online": [
null,
"Online"
],
"Busy": [
null,
"Ocupado"
],
"Away": [
null,
"Ausente"
],
"Offline": [
null,
"Offline"
],
"Click to add new chat contacts": [
null,
"Clique para adicionar novos contatos ao chat"
],
"Add a contact": [
null,
"Adicionar contato"
],
"Contact username": [
null,
"Usuário do contatt"
],
"Add": [
null,
"Adicionar"
],
"Contact name": [
null,
"Nome do contato"
],
"Search": [
null,
"Procurar"
],
"No users found": [
null,
"Não foram encontrados usuários"
],
"Click to add as a chat contact": [
null,
"Clique para adicionar como um contato do chat"
],
"Click to open this room": [
null,
"CLique para abrir a sala"
],
"Show more information on this room": [
null,
"Mostrar mais informações nessa sala"
],
"Description:": [
null,
"Descrição:"
],
"Occupants:": [
null,
"Ocupantes:"
],
"Features:": [
null,
"Recursos:"
],
"Requires authentication": [
null,
"Requer autenticação"
],
"Hidden": [
null,
"Escondido"
],
"Requires an invitation": [
null,
"Requer um convite"
],
"Moderated": [
null,
"Moderado"
],
"Non-anonymous": [
null,
"Não anônimo"
],
"Open room": [
null,
"Sala aberta"
],
"Permanent room": [
null,
"Sala permanente"
],
"Public": [
null,
"Público"
],
"Semi-anonymous": [
null,
"Semi anônimo"
],
"Temporary room": [
null,
"Sala temporária"
],
"Unmoderated": [
null,
"Sem moderação"
],
"Rooms": [
null,
"Salas"
],
"Room name": [
null,
"Nome da sala"
],
"Nickname": [
null,
"Apelido"
],
"Server": [
null,
"Server"
],
"Join": [
null,
"Entrar"
],
"Show rooms": [
null,
"Mostrar salas"
],
"No rooms on %1$s": [
null,
"Sem salas em %1$s"
],
"Rooms on %1$s": [
null,
"Salas em %1$s"
],
"Set chatroom topic": [
null,
"Definir tópico do chat"
],
"Kick user from chatroom": [
null,
"Expulsar usuário do chat"
],
"Ban user from chatroom": [
null,
"Banir usuário do chat"
],
"Message": [
null,
"Mensagem"
],
"Save": [
null,
"Salvar"
],
"Cancel": [
null,
"Cancelar"
],
"An error occurred while trying to save the form.": [
null,
"Ocorreu um erro enquanto tentava salvar o formulário"
],
"This chatroom requires a password": [
null,
"Esse chat precisa de senha"
],
"Password: ": [
null,
"Senha: "
],
"Submit": [
null,
"Enviar"
],
"This room is not anonymous": [
null,
"Essa sala não é anônima"
],
"This room now shows unavailable members": [
null,
"Agora esta sala mostra membros indisponíveis"
],
"This room does not show unavailable members": [
null,
"Essa sala não mostra membros indisponíveis"
],
"Non-privacy-related room configuration has changed": [
null,
"Configuraçõs não relacionadas à privacidade mudaram"
],
"Room logging is now enabled": [
null,
"O log da sala está ativado"
],
"Room logging is now disabled": [
null,
"O log da sala está desativado"
],
"This room is now non-anonymous": [
null,
"Esse sala é não anônima"
],
"This room is now semi-anonymous": [
null,
"Essa sala agora é semi anônima"
],
"This room is now fully-anonymous": [
null,
"Essa sala agora é totalmente anônima"
],
"A new room has been created": [
null,
"Uma nova sala foi criada"
],
"Your nickname has been changed": [
null,
"Seu apelido foi mudado"
],
"<strong>%1$s</strong> has been banned": [
null,
"<strong>%1$s</strong> foi banido"
],
"<strong>%1$s</strong> has been kicked out": [
null,
"<strong>%1$s</strong> foi expulso"
],
"<strong>%1$s</strong> has been removed because of an affiliation change": [
null,
"<srtong>%1$s</strong> foi removido por causa de troca de associação"
],
"<strong>%1$s</strong> has been removed for not being a member": [
null,
"<strong>%1$s</strong> foi removido por não ser um membro"
],
"You have been banned from this room": [
null,
"Você foi banido dessa sala"
],
"You have been kicked from this room": [
null,
"Você foi expulso dessa sala"
],
"You have been removed from this room because of an affiliation change": [
null,
"Você foi removido da sala devido a uma mudança de associação"
],
"You have been removed from this room because the room has changed to members-only and you're not a member": [
null,
"Você foi removido da sala porque ela foi mudada para somente membrose você não é um membro"
],
"You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
null,
"Você foi removido da sala devido a MUC (Multi-user chat)o serviço está sendo desligado"
],
"You are not on the member list of this room": [
null,
"Você não é membro dessa sala"
],
"No nickname was specified": [
null,
"Você não escolheu um apelido "
],
"You are not allowed to create new rooms": [
null,
"Você não tem permitição de criar novas salas"
],
"Your nickname doesn't conform to this room's policies": [
null,
"Seu apelido não está de acordo com as regras da sala"
],
"Your nickname is already taken": [
null,
"Seu apelido já foi escolhido"
],
"This room does not (yet) exist": [
null,
"A sala não existe (ainda)"
],
"This room has reached it's maximum number of occupants": [
null,
"A sala atingiu o número máximo de ocupantes"
],
"Topic set by %1$s to: %2$s": [
null,
"Topico definido por %1$s para: %2$s"
],
"This user is a moderator": [
null,
"Esse usuário é o moderador"
],
"This user can send messages in this room": [
null,
"Esse usuário pode enviar mensagens nessa sala"
],
"This user can NOT send messages in this room": [
null,
"Esse usuário NÃO pode enviar mensagens nessa sala"
],
"Click to chat with this contact": [
null,
"Clique para conversar com o contato"
],
"Click to remove this contact": [
null,
"Clique para remover o contato"
],
"This contact is busy": [
null,
"Este contato está ocupado"
],
"This contact is online": [
null,
"Este contato está online"
],
"This contact is offline": [
null,
"Este contato está offline"
],
"This contact is unavailable": [
null,
"Este contato está indisponível"
],
"This contact is away for an extended period": [
null,
"Este contato está ausente por um longo período"
],
"This contact is away": [
null,
"Este contato está ausente"
],
"Contact requests": [
null,
"Solicitação de contatos"
],
"My contacts": [
null,
"Meus contatos"
],
"Pending contacts": [
null,
"Contados pendentes"
],
"Custom status": [
null,
"Status customizado"
],
"Click to change your chat status": [
null,
"Clique para mudar seu status no chat"
],
"Click here to write a custom status message": [
null,
"Clique aqui para customizar a mensagem de status"
],
"online": [
null,
"online"
],
"busy": [
null,
"ocupado"
],
"away for long": [
null,
"ausente a bastante tempo"
],
"away": [
null,
"ausente"
],
"I am %1$s": [
null,
"Estou %1$s"
],
"Sign in": [
null,
"Conectar-se"
],
"XMPP/Jabber Username:": [
null,
"Usuário XMPP/Jabber:"
],
"Password:": [
null,
"Senha:"
],
"Log In": [
null,
"Entrar"
],
"BOSH Service URL:": [
null,
"URL de serviço BOSH:"
],
"Online Contacts": [
null,
"Contatos online"
],
"%1$s is typing": [
null,
"%1$s está digitando"
],
"Connected": [
null,
"Conectado"
],
"Attached": [
null,
"Anexado"
]
,
"Type to filter": [
null,
"Escreva para filtrar"
]
}
}
};
if (typeof define === 'function' && define.amd) {
define("pt_BR", ['jed'], function () {
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.pt_BR = factory(new Jed(translations));
}
}(this, function (i18n) {
return i18n;
})
);

View File

@ -1328,8 +1328,7 @@
spyOn(view.model, 'sendMessage').and.callThrough();
test_utils.sendMessage(view, message);
test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length)
.then(() => {
test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length, 1000).then(() => {
expect(view.model.sendMessage).toHaveBeenCalled();
var msg = $(view.el).find('.chat-content .chat-msg').last().find('.chat-msg-text');
expect(msg.html().trim()).toEqual(
@ -1338,9 +1337,7 @@
' src="' + message + '"></a>');
message += "?param1=val1&param2=val2";
test_utils.sendMessage(view, message);
return test_utils.waitUntil(function () {
return view.el.querySelectorAll('.chat-content .chat-image').length === 2;
}, 1000);
return test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length === 2, 1000);
}).then(() => {
expect(view.model.sendMessage).toHaveBeenCalled();
var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg-text');
@ -1352,9 +1349,7 @@
// Test now with two images in one message
message += ' hello world '+base_url+"/logo/conversejs-filled.svg";
test_utils.sendMessage(view, message);
return test_utils.waitUntil(function () {
return view.el.querySelectorAll('.chat-content .chat-image').length === 4;
}, 1000);
return test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length === 4, 1000);
}).then(function () {
expect(view.model.sendMessage).toHaveBeenCalled();
var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg-text');
@ -1765,7 +1760,7 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza));
msg_id = u.getUniqueId();
_converse.chatboxes.onMessage($msg({
view.model.onMessage($msg({
'from': 'lounge@localhost/newguy',
'to': _converse.connection.jid,
'type': 'groupchat',
@ -1776,7 +1771,7 @@
expect(view.el.querySelector('.chat-msg-text').textContent)
.toBe('But soft, what light through yonder airlock breaks?');
_converse.chatboxes.onMessage($msg({
view.model.onMessage($msg({
'from': 'lounge@localhost/newguy',
'to': _converse.connection.jid,
'type': 'chat',
@ -1790,7 +1785,7 @@
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
expect(view.el.querySelectorAll('.chat-msg-content .fa-edit').length).toBe(1);
_converse.chatboxes.onMessage($msg({
view.model.onMessage($msg({
'from': 'lounge@localhost/newguy',
'to': _converse.connection.jid,
'type': 'chat',

View File

@ -62,19 +62,6 @@
'privateChatsAutoJoined'
]);
function getMessageBody (stanza) {
/* Given a message stanza, return the text contained in its body.
*/
const type = stanza.getAttribute('type');
if (type === 'error') {
const error = stanza.querySelector('error');
return _.propertyOf(error.querySelector('text'))('textContent') ||
__('Sorry, an error occurred:') + ' ' + error.innerHTML;
} else {
return _.propertyOf(stanza.querySelector('body'))('textContent');
}
}
function openChat (jid) {
if (!utils.isValidJID(jid)) {
return _converse.log(
@ -305,6 +292,23 @@
return this.vcard.get('fullname') || this.get('jid');
},
handleMessageCorrection (stanza) {
const replace = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop();
if (replace) {
const msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
message = msgid && this.messages.findWhere({msgid}),
older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': _converse.chatboxes.getMessageBody(stanza),
'older_versions': older_versions,
'edited': true
});
return true;
}
return false;
},
createMessageStanza (message) {
/* Given a _converse.Message Backbone.Model, return the XML
* stanza that represents it.
@ -473,7 +477,7 @@
'is_archived': !_.isNil(archive),
'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler),
'message': getMessageBody(stanza) || undefined,
'message': _converse.chatboxes.getMessageBody(stanza) || undefined,
'msgid': stanza.getAttribute('id'),
'time': delay ? delay.getAttribute('stamp') : moment().format(),
'type': stanza.getAttribute('type')
@ -614,6 +618,19 @@
return true;
},
getMessageBody (stanza) {
/* Given a message stanza, return the text contained in its body.
*/
const type = stanza.getAttribute('type');
if (type === 'error') {
const error = stanza.querySelector('error');
return _.propertyOf(error.querySelector('text'))('textContent') ||
__('Sorry, an error occurred:') + ' ' + error.innerHTML;
} else {
return _.propertyOf(stanza.querySelector('body'))('textContent');
}
},
onMessage (stanza) {
/* Handler method for all incoming single-user chat "message"
* stanzas.
@ -676,20 +693,10 @@
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')
}
const chatbox = this.getChatBox(contact_jid, attrs, !_.isNull(stanza.querySelector('body')));
if (chatbox) {
const replace = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop(),
msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
const msgid = stanza.getAttribute('id'),
message = msgid && chatbox.messages.findWhere({msgid});
if (replace) {
const older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': getMessageBody(stanza),
'older_versions': older_versions,
'edited': true
});
} else if (!message) {
if (!message) {
// Only create the message when we're sure it's not a duplicate
chatbox.incrementUnreadMsgCounter(original_stanza);
chatbox.createMessage(stanza, original_stanza);

View File

@ -849,14 +849,15 @@
if (!_.isNull(forwarded)) {
stanza = forwarded.querySelector('message');
}
const jid = stanza.getAttribute('from'),
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '',
subject = _.propertyOf(stanza.querySelector('subject'))('textContent');
if (this.isDuplicate(stanza, original_stanza)) {
return;
}
const jid = stanza.getAttribute('from'),
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '';
if (!this.handleMessageCorrection(stanza)) {
const subject = _.propertyOf(stanza.querySelector('subject'))('textContent');
if (subject) {
u.safeSave(this, {'subject': {'author': sender, 'text': subject}});
}
@ -865,6 +866,7 @@
}
this.incrementUnreadMsgCounter(original_stanza);
this.createMessage(stanza, original_stanza);
}
if (sender !== this.get('nick')) {
// We only emit an event if it's not our own message
_converse.emit('message', {'stanza': original_stanza, 'chatbox': this});