Display mention syntax in publisher instead of username

This commit is contained in:
Steffen van Bergerem 2017-01-30 12:50:03 +01:00
parent 408645cccc
commit b3c412d38f
No known key found for this signature in database
GPG key ID: 315C9787D548DC6B
2 changed files with 39 additions and 51 deletions

View file

@ -2,13 +2,8 @@
app.views.PublisherMention = app.views.SearchBase.extend({ app.views.PublisherMention = app.views.SearchBase.extend({
triggerChar: "@", triggerChar: "@",
invisibleChar: "\u200B", // zero width space
mentionRegex: /@([^@\s]+)$/, mentionRegex: /@([^@\s]+)$/,
mentionSyntaxTemplate: function(person) { return "@{" + person.handle + "}"; },
templates: {
mentionItemSyntax: _.template("@{<%= handle %>}"),
mentionItemHighlight: _.template("<strong><span><%= name %></span></strong>")
},
events: { events: {
"keydown #status_message_fake_text": "onInputBoxKeyDown", "keydown #status_message_fake_text": "onInputBoxKeyDown",
@ -55,8 +50,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({
cleanMentionedPeople: function() { cleanMentionedPeople: function() {
var inputText = this.inputBox.val(); var inputText = this.inputBox.val();
this.mentionedPeople = this.mentionedPeople.filter(function(person) { this.mentionedPeople = this.mentionedPeople.filter(function(person) {
return person.name && inputText.indexOf(person.name) > -1; return person.handle && inputText.indexOf(this.mentionSyntaxTemplate(person)) > -1;
}); }.bind(this));
this.ignoreDiasporaIds = this.mentionedPeople.map(function(person) { return person.handle; }); this.ignoreDiasporaIds = this.mentionedPeople.map(function(person) { return person.handle; });
}, },
@ -70,39 +65,22 @@ app.views.PublisherMention = app.views.SearchBase.extend({
this.addPersonToMentions(person); this.addPersonToMentions(person);
this.closeSuggestions(); this.closeSuggestions();
messageText = messageText.substring(0, triggerCharPosition) + var mentionText = this.mentionSyntaxTemplate(person);
this.invisibleChar + person.name + messageText.substring(caretPosition);
messageText = messageText.substring(0, triggerCharPosition) + mentionText + messageText.substring(caretPosition);
this.inputBox.val(messageText); this.inputBox.val(messageText);
this.updateMessageTexts(); this.updateMessageTexts();
this.inputBox.focus(); this.inputBox.focus();
var newCaretPosition = triggerCharPosition + person.name.length + 1; var newCaretPosition = triggerCharPosition + mentionText.length;
this.inputBox[0].setSelectionRange(newCaretPosition, newCaretPosition); this.inputBox[0].setSelectionRange(newCaretPosition, newCaretPosition);
}, },
/**
* Replaces every combination of this.invisibleChar + mention.name by the
* correct syntax for both hidden text and visible one.
*
* For instance, the text "Hello \u200Buser1" will be tranformed to
* "Hello @{user1 ; user1@pod.tld}" in the hidden element and
* "Hello <strong><span>user1</span></strong>" in the element visible to the user.
*/
updateMessageTexts: function() { updateMessageTexts: function() {
var fakeMessageText = this.inputBox.val(), var messageText = this.inputBox.val();
mentionBoxText = _.escape(fakeMessageText),
messageText = fakeMessageText;
this.mentionedPeople.forEach(function(person) {
var mentionName = this.invisibleChar + person.name;
messageText = messageText.replace(mentionName, this.templates.mentionItemSyntax(person));
var textHighlight = this.templates.mentionItemHighlight({name: _.escape(person.name)});
mentionBoxText = mentionBoxText.replace(mentionName, textHighlight);
}, this);
this.inputBox.data("messageText", messageText); this.inputBox.data("messageText", messageText);
this.mentionsBox.find(".mentions").html(mentionBoxText); this.mentionsBox.find(".mentions").html(_.escape(messageText));
}, },
updateTypeaheadInput: function() { updateTypeaheadInput: function() {
@ -128,7 +106,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({
prefillMention: function(persons) { prefillMention: function(persons) {
persons.forEach(function(person) { persons.forEach(function(person) {
this.addPersonToMentions(person); this.addPersonToMentions(person);
var text = this.invisibleChar + person.name; var text = this.mentionSyntaxTemplate(person);
if(this.inputBox.val().length !== 0) { if(this.inputBox.val().length !== 0) {
text = this.inputBox.val() + " " + text; text = this.inputBox.val() + " " + text;
} }

View file

@ -7,7 +7,6 @@ describe("app.views.PublisherMention", function() {
it("initializes object properties", function() { it("initializes object properties", function() {
this.view = new app.views.PublisherMention({ el: "#publisher" }); this.view = new app.views.PublisherMention({ el: "#publisher" });
expect(this.view.mentionedPeople).toEqual([]); expect(this.view.mentionedPeople).toEqual([]);
expect(this.view.invisibleChar).toBe("\u200B");
expect(this.view.triggerChar).toBe("@"); expect(this.view.triggerChar).toBe("@");
}); });
@ -29,6 +28,17 @@ describe("app.views.PublisherMention", function() {
}); });
}); });
describe("mentionSyntaxTemplate", function() {
beforeEach(function() {
this.view = new app.views.PublisherMention({el: "#publisher"});
this.person = {handle: "alice@pod.tld", name: "Alice Awesome"};
});
it("returns the correct mention syntax", function() {
expect(this.view.mentionSyntaxTemplate(this.person)).toBe("@{alice@pod.tld}");
});
});
describe("bindTypeaheadEvents", function() { describe("bindTypeaheadEvents", function() {
beforeEach(function() { beforeEach(function() {
this.view = new app.views.PublisherMention({ el: "#publisher" }); this.view = new app.views.PublisherMention({ el: "#publisher" });
@ -92,30 +102,30 @@ describe("app.views.PublisherMention", function() {
}); });
it("removes person from mentioned people if not mentioned anymore", function() { it("removes person from mentioned people if not mentioned anymore", function() {
this.view.addPersonToMentions({name: "user1", handle: "user1@pod.tld"}); this.view.addPersonToMentions({name: "User Name", handle: "user1@pod.tld"});
expect(this.view.mentionedPeople.length).toBe(1); expect(this.view.mentionedPeople.length).toBe(1);
this.view.cleanMentionedPeople(); this.view.cleanMentionedPeople();
expect(this.view.mentionedPeople.length).toBe(0); expect(this.view.mentionedPeople.length).toBe(0);
}); });
it("removes person from ignored people if not mentioned anymore", function() { it("removes person from ignored people if not mentioned anymore", function() {
this.view.addPersonToMentions({name: "user1", handle: "user1@pod.tld"}); this.view.addPersonToMentions({name: "User Name", handle: "user1@pod.tld"});
expect(this.view.ignoreDiasporaIds.length).toBe(1); expect(this.view.ignoreDiasporaIds.length).toBe(1);
this.view.cleanMentionedPeople(); this.view.cleanMentionedPeople();
expect(this.view.ignoreDiasporaIds.length).toBe(0); expect(this.view.ignoreDiasporaIds.length).toBe(0);
}); });
it("keeps mentioned persons", function() { it("keeps mentioned persons", function() {
this.view.addPersonToMentions({name: "user1", handle: "user1@pod.tld"}); this.view.addPersonToMentions({name: "User Name", handle: "user1@pod.tld"});
this.view.inputBox.val("user1"); this.view.inputBox.val("@{user1@pod.tld}");
expect(this.view.mentionedPeople.length).toBe(1); expect(this.view.mentionedPeople.length).toBe(1);
this.view.cleanMentionedPeople(); this.view.cleanMentionedPeople();
expect(this.view.mentionedPeople.length).toBe(1); expect(this.view.mentionedPeople.length).toBe(1);
}); });
it("keeps mentioned persons for ignored diaspora ids", function() { it("keeps mentioned persons for ignored diaspora ids", function() {
this.view.addPersonToMentions({name: "user1", handle: "user1@pod.tld"}); this.view.addPersonToMentions({name: "User Name", handle: "user1@pod.tld"});
this.view.inputBox.val("user1"); this.view.inputBox.val("@{user1@pod.tld}");
expect(this.view.ignoreDiasporaIds.length).toBe(1); expect(this.view.ignoreDiasporaIds.length).toBe(1);
this.view.cleanMentionedPeople(); this.view.cleanMentionedPeople();
expect(this.view.ignoreDiasporaIds.length).toBe(1); expect(this.view.ignoreDiasporaIds.length).toBe(1);
@ -151,20 +161,20 @@ describe("app.views.PublisherMention", function() {
it("correctly formats the text", function() { it("correctly formats the text", function() {
this.view.onSuggestionSelection({name: "user1337", handle: "user1@pod.tld"}); this.view.onSuggestionSelection({name: "user1337", handle: "user1@pod.tld"});
expect(this.view.inputBox.val()).toBe("@user1337 Text before \u200Buser1337 text after"); expect(this.view.inputBox.val()).toBe("@user1337 Text before @{user1@pod.tld} text after");
}); });
it("replaces the correct mention", function() { it("replaces the correct mention", function() {
this.view.inputBox.val("@user1337 123 user2 @user2 456 @user3 789"); this.view.inputBox.val("@user1337 123 user2 @user2 456 @user3 789");
this.view.inputBox[0].setSelectionRange(26, 26); this.view.inputBox[0].setSelectionRange(26, 26);
this.view.onSuggestionSelection({name: "user23", handle: "user2@pod.tld"}); this.view.onSuggestionSelection({name: "user23", handle: "user2@pod.tld"});
expect(this.view.inputBox.val()).toBe("@user1337 123 user2 \u200Buser23 456 @user3 789"); expect(this.view.inputBox.val()).toBe("@user1337 123 user2 @{user2@pod.tld} 456 @user3 789");
this.view.inputBox[0].setSelectionRange(9, 9); this.view.inputBox[0].setSelectionRange(9, 9);
this.view.onSuggestionSelection({name: "user1337", handle: "user1@pod.tld"}); this.view.onSuggestionSelection({name: "user1337", handle: "user1@pod.tld"});
expect(this.view.inputBox.val()).toBe("\u200Buser1337 123 user2 \u200Buser23 456 @user3 789"); expect(this.view.inputBox.val()).toBe("@{user1@pod.tld} 123 user2 @{user2@pod.tld} 456 @user3 789");
this.view.inputBox[0].setSelectionRange(38, 38); this.view.inputBox[0].setSelectionRange(54, 54);
this.view.onSuggestionSelection({name: "user32", handle: "user3@pod.tld"}); this.view.onSuggestionSelection({name: "user32", handle: "user3@pod.tld"});
expect(this.view.inputBox.val()).toBe("\u200Buser1337 123 user2 \u200Buser23 456 \u200Buser32 789"); expect(this.view.inputBox.val()).toBe("@{user1@pod.tld} 123 user2 @{user2@pod.tld} 456 @{user3@pod.tld} 789");
}); });
it("calls updateMessageTexts", function() { it("calls updateMessageTexts", function() {
@ -175,7 +185,7 @@ describe("app.views.PublisherMention", function() {
it("places the caret at the right position", function() { it("places the caret at the right position", function() {
this.view.onSuggestionSelection({"name": "user1WithLongName", "handle": "user1@pod.tld"}); this.view.onSuggestionSelection({"name": "user1WithLongName", "handle": "user1@pod.tld"});
var expectedCaretPosition = ("@user1337 Text before \u200Buser1WithLongName").length; var expectedCaretPosition = ("@user1337 Text before @{user1@pod.tld}").length;
expect(this.view.inputBox[0].selectionStart).toBe(expectedCaretPosition); expect(this.view.inputBox[0].selectionStart).toBe(expectedCaretPosition);
}); });
}); });
@ -183,7 +193,7 @@ describe("app.views.PublisherMention", function() {
describe("updateMessageTexts", function() { describe("updateMessageTexts", function() {
beforeEach(function() { beforeEach(function() {
this.view = new app.views.PublisherMention({ el: "#publisher" }); this.view = new app.views.PublisherMention({ el: "#publisher" });
this.view.inputBox.val("@user1 Text before \u200Buser1\ntext after"); this.view.inputBox.val("@user1 Text before @{user1@pod.tld}\ntext after");
this.view.mentionedPeople.push({"name": "user1", "handle": "user1@pod.tld"}); this.view.mentionedPeople.push({"name": "user1", "handle": "user1@pod.tld"});
}); });
@ -195,14 +205,14 @@ describe("app.views.PublisherMention", function() {
it("formats overlay text to HTML", function() { it("formats overlay text to HTML", function() {
this.view.updateMessageTexts(); this.view.updateMessageTexts();
expect(this.view.mentionsBox.find(".mentions").html()) expect(this.view.mentionsBox.find(".mentions").html())
.toBe("@user1 Text before <strong><span>user1</span></strong>\ntext after"); .toBe("@user1 Text before @{user1@pod.tld}\ntext after");
}); });
it("properly escapes the user input", function() { it("properly escapes the user input", function() {
this.view.inputBox.val("<img src=\"/default.png\"> @user1 Text before \u200Buser1\ntext after"); this.view.inputBox.val("<img src=\"/default.png\"> @user1 Text before @{user1@pod.tld}\ntext after");
this.view.updateMessageTexts(); this.view.updateMessageTexts();
expect(this.view.mentionsBox.find(".mentions").html()) expect(this.view.mentionsBox.find(".mentions").html())
.toBe("&lt;img src=\"/default.png\"&gt; @user1 Text before <strong><span>user1</span></strong>\ntext after"); .toBe("&lt;img src=\"/default.png\"&gt; @user1 Text before @{user1@pod.tld}\ntext after");
}); });
}); });
@ -264,7 +274,7 @@ describe("app.views.PublisherMention", function() {
this.view.prefillMention([{"name": "user1", "handle": "user1@pod.tld"}]); this.view.prefillMention([{"name": "user1", "handle": "user1@pod.tld"}]);
expect(this.view.addPersonToMentions).toHaveBeenCalledWith({"name": "user1", "handle": "user1@pod.tld"}); expect(this.view.addPersonToMentions).toHaveBeenCalledWith({"name": "user1", "handle": "user1@pod.tld"});
expect(this.view.updateMessageTexts).toHaveBeenCalled(); expect(this.view.updateMessageTexts).toHaveBeenCalled();
expect(this.view.inputBox.val()).toBe("\u200Buser1"); expect(this.view.inputBox.val()).toBe("@{user1@pod.tld}");
}); });
it("prefills multiple mentions", function() { it("prefills multiple mentions", function() {
@ -276,7 +286,7 @@ describe("app.views.PublisherMention", function() {
expect(this.view.addPersonToMentions).toHaveBeenCalledWith({"name": "user1", "handle": "user1@pod.tld"}); expect(this.view.addPersonToMentions).toHaveBeenCalledWith({"name": "user1", "handle": "user1@pod.tld"});
expect(this.view.addPersonToMentions).toHaveBeenCalledWith({"name": "user2", "handle": "user2@pod.tld"}); expect(this.view.addPersonToMentions).toHaveBeenCalledWith({"name": "user2", "handle": "user2@pod.tld"});
expect(this.view.updateMessageTexts).toHaveBeenCalled(); expect(this.view.updateMessageTexts).toHaveBeenCalled();
expect(this.view.inputBox.val()).toBe("\u200Buser1 \u200Buser2"); expect(this.view.inputBox.val()).toBe("@{user1@pod.tld} @{user2@pod.tld}");
}); });
}); });