Add method to determine references from message text

This commit is contained in:
JC Brand 2018-08-15 17:22:24 +02:00
parent e10d9b074e
commit f2b017ec89
6 changed files with 135 additions and 6 deletions

View File

@ -13,7 +13,7 @@
const Strophe = converse.env.Strophe;
const u = converse.env.utils;
return describe("A groupchat textarea", function () {
describe("The nickname autocomplete feature", function () {
it("shows all autocompletion options when the user presses @",
mock.initConverseWithPromises(
@ -146,7 +146,7 @@
'stopPropagation': _.noop,
'keyCode': 13 // Enter
});
expect(textarea.value).toBe('hello s some2 ');
expect(textarea.value).toBe('hello s @some2 ');
// Test that pressing tab twice selects
presence = $pres({
@ -166,7 +166,7 @@
view.keyPressed(tab_event);
view.keyUp(tab_event);
expect(textarea.value).toBe('hello z3r0 ');
expect(textarea.value).toBe('hello @z3r0 ');
done();
}).catch(_.partial(console.error, _));

View File

@ -1202,6 +1202,60 @@
});
}));
describe("in which someone is mentioned", function () {
it("includes XEP-0372 references to that person",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom')
.then(() => {
const view = _converse.chatboxviews.get('lounge@localhost');
['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
_converse.connection._dataRecv(test_utils.createRequest(
$pres({
'to': 'tom@localhost/resource',
'from': `lounge@localhost/${nick}`
})
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': `${nick}@localhost/resource`,
'role': 'participant'
})));
});
let [text, references] = view.model.parseForReferences('hello z3r0')
expect(references.length).toBe(0);
expect(text).toBe('hello z3r0');
[text, references] = view.model.parseForReferences('hello @z3r0')
expect(references.length).toBe(1);
expect(text).toBe('hello z3r0');
expect(JSON.stringify(references))
.toBe('[{"begin":6,"end":10,"type":"mention","uri":"xmpp:z3r0@localhost"}]');
[text, references] = view.model.parseForReferences('hello @some1 @z3r0 @gibson @mr.robot, how are you?')
expect(text).toBe('hello @some1 z3r0 gibson mr.robot, how are you?');
expect(JSON.stringify(references))
.toBe('[{"begin":13,"end":17,"type":"mention","uri":"xmpp:z3r0@localhost"},'+
'{"begin":18,"end":24,"type":"mention","uri":"xmpp:gibson@localhost"},'+
'{"begin":25,"end":33,"type":"mention","uri":"xmpp:mr.robot@localhost"}]');
[text, references] = view.model.parseForReferences('yo @gib')
expect(text).toBe('yo @gib');
expect(references.length).toBe(0);
[text, references] = view.model.parseForReferences('yo @gibsonian')
expect(text).toBe('yo @gibsonian');
expect(references.length).toBe(0);
done();
}).catch(_.partial(console.error, _));
}));
});
describe("when received from someone else", function () {
it("will open a chatbox and be displayed inside it",

View File

@ -237,7 +237,6 @@
} else {
selected = this.ul.children[this.index];
}
if (selected) {
const suggestion = this.suggestions[this.index];
this.insertValue(suggestion);

View File

@ -617,7 +617,7 @@
'min_chars': 1,
'match_current_word': true,
'match_on_tab': true,
'list': () => this.model.occupants.map(o => ({'label': o.get('nick'), 'value': o.get('nick')})),
'list': () => this.model.occupants.map(o => ({'label': o.get('nick'), 'value': `@${o.get('nick')}`})),
'filter': _converse.FILTER_STARTSWITH,
'trigger_on_at': true
});

View File

@ -308,14 +308,75 @@
_converse.connection.sendPresence(presence);
},
getReferenceForMention (mention, index) {
const longest_match = u.getLongestSubstring(mention, this.occupants.map(o => o.get('nick')));
if (!longest_match) {
return null;
}
if ((mention[longest_match.length] || '').match(/[A-Za-zäëïöüâêîôûáéíóúàèìòùÄËÏÖÜÂÊÎÔÛÁÉÍÓÚÀÈÌÒÙ]/i)) {
// avoid false positives, i.e. mentions that have
// further alphabetical characters than our longest
// match.
return null;
}
const occupant = this.occupants.findOccupant({'nick': longest_match});
if (!occupant) {
return null;
}
const obj = {
'begin': index,
'end': index + longest_match.length,
'type': 'mention'
};
if (occupant.get('jid')) {
obj.uri = `xmpp:${occupant.get('jid')}`
}
return obj;
},
extractReference (text, index) {
for (let i=index; i<text.length; i++) {
if (text[i] !== '@') {
continue
} else {
const match = text.slice(i+1),
ref = this.getReferenceForMention(match, i)
if (ref) {
return [text.slice(0, i) + match, ref, i]
}
}
}
return;
},
parseForReferences (text) {
const refs = [];
let index = 0;
while (index < (text || '').length) {
const result = this.extractReference(text, index);
if (result) {
text = result[0]; // @ gets filtered out
refs.push(result[1]);
index = result[2];
} else {
break;
}
}
return [text, refs];
},
getOutgoingMessageAttributes (text, spoiler_hint) {
const is_spoiler = this.get('composing_spoiler');
var references;
[text, references] = this.parseForReferences(text);
return {
'nick': this.get('nick'),
'from': `${this.get('jid')}/${this.get('nick')}`,
'fullname': this.get('nick'),
'is_spoiler': is_spoiler,
'message': text ? u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse) : undefined,
'nick': this.get('nick'),
'references': references,
'sender': 'me',
'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
'type': 'groupchat'

View File

@ -98,6 +98,21 @@
var u = {};
u.getLongestSubstring = function (string, candidates) {
function reducer (accumulator, current_value) {
if (string.startsWith(current_value)) {
if (current_value.length > accumulator.length) {
return current_value;
} else {
return accumulator;
}
} else {
return accumulator;
}
}
return candidates.reduce(reducer, '');
}
u.getNextElement = function (el, selector='*') {
let next_el = el.nextElementSibling;
while (!_.isNull(next_el) && !sizzle.matchesSelector(next_el, selector)) {