diff --git a/Changelog.md b/Changelog.md index bf82471ba..9794eac14 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ * Make the mention syntax more flexible [#7305](https://github.com/diaspora/diaspora/pull/7305) * Display @ before mentions [#7324](https://github.com/diaspora/diaspora/pull/7324) +* Simplify mentions in the publisher [#7302](https://github.com/diaspora/diaspora/pull/7302) ## Bug fixes diff --git a/app/assets/javascripts/app/views/publisher/mention_view.js b/app/assets/javascripts/app/views/publisher/mention_view.js index 86b3b07aa..f30779c70 100644 --- a/app/assets/javascripts/app/views/publisher/mention_view.js +++ b/app/assets/javascripts/app/views/publisher/mention_view.js @@ -2,29 +2,19 @@ app.views.PublisherMention = app.views.SearchBase.extend({ triggerChar: "@", - invisibleChar: "\u200B", // zero width space mentionRegex: /@([^@\s]+)$/, - - templates: { - mentionItemSyntax: _.template("@{<%= name %> ; <%= handle %>}"), - mentionItemHighlight: _.template("<%= name %>") - }, + mentionSyntaxTemplate: function(person) { return "@{" + person.handle + "}"; }, events: { - "keydown #status_message_fake_text": "onInputBoxKeyDown", - "input #status_message_fake_text": "onInputBoxInput", - "click #status_message_fake_text": "onInputBoxClick", - "blur #status_message_fake_text": "onInputBoxBlur" + "keydown #status_message_text": "onInputBoxKeyDown", + "input #status_message_text": "updateTypeaheadInput", + "click #status_message_text": "onInputBoxClick", + "blur #status_message_text": "onInputBoxBlur" }, initialize: function() { this.mentionedPeople = []; - - // contains the 'fake text' displayed to the user - // also has a data-messageText attribute with the original text - this.inputBox = this.$("#status_message_fake_text"); - // contains the mentions displayed to the user - this.mentionsBox = this.$(".mentions-box"); + this.inputBox = this.$("#status_message_text"); this.typeaheadInput = this.$(".typeahead-mention-box"); this.bindTypeaheadEvents(); @@ -55,8 +45,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({ cleanMentionedPeople: function() { var inputText = this.inputBox.val(); 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; }); }, @@ -70,41 +60,16 @@ app.views.PublisherMention = app.views.SearchBase.extend({ this.addPersonToMentions(person); this.closeSuggestions(); - messageText = messageText.substring(0, triggerCharPosition) + - this.invisibleChar + person.name + messageText.substring(caretPosition); + var mentionText = this.mentionSyntaxTemplate(person); + + messageText = messageText.substring(0, triggerCharPosition) + mentionText + messageText.substring(caretPosition); this.inputBox.val(messageText); - this.updateMessageTexts(); - this.inputBox.focus(); - var newCaretPosition = triggerCharPosition + person.name.length + 1; + var newCaretPosition = triggerCharPosition + mentionText.length; 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 user1" in the element visible to the user. - */ - updateMessageTexts: function() { - var fakeMessageText = 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.mentionsBox.find(".mentions").html(mentionBoxText); - }, - updateTypeaheadInput: function() { var messageText = this.inputBox.val(); var caretPosition = this.inputBox[0].selectionStart; @@ -115,6 +80,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({ return; } + this.cleanMentionedPeople(); + // result[1] is the string between the last '@' and the current caret position this.typeaheadInput.typeahead("val", result[1]); this.typeaheadInput.typeahead("open"); @@ -128,12 +95,11 @@ app.views.PublisherMention = app.views.SearchBase.extend({ prefillMention: function(persons) { persons.forEach(function(person) { this.addPersonToMentions(person); - var text = this.invisibleChar + person.name; + var text = this.mentionSyntaxTemplate(person); if(this.inputBox.val().length !== 0) { text = this.inputBox.val() + " " + text; } this.inputBox.val(text); - this.updateMessageTexts(); }, this); }, @@ -154,15 +120,6 @@ app.views.PublisherMention = app.views.SearchBase.extend({ this.typeaheadInput.trigger($.Event("keydown", {keyCode: e.keyCode, which: e.which})); }, - /** - * Listens for user input and opens results dropdown when input contains the trigger char - */ - onInputBoxInput: function() { - this.cleanMentionedPeople(); - this.updateMessageTexts(); - this.updateTypeaheadInput(); - }, - onInputBoxKeyDown: function(e) { // This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT if(e.which === Keycodes.LEFT || e.which === Keycodes.RIGHT || @@ -205,7 +162,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ reset: function() { this.inputBox.val(""); - this.onInputBoxInput(); + this.updateTypeaheadInput(); }, closeSuggestions: function() { @@ -217,7 +174,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({ return this.$(".tt-menu").is(":visible"); }, - getTextForSubmit: function() { - return this.mentionedPeople.length ? this.inputBox.data("messageText") : this.inputBox.val(); + getMentionedPeople: function() { + this.cleanMentionedPeople(); + return this.mentionedPeople; } }); diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index de9245027..97bda7e12 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -18,11 +18,11 @@ app.views.Publisher = Backbone.View.extend({ el : "#publisher", events : { - "keydown #status_message_fake_text" : "keyDown", + "keydown #status_message_text": "keyDown", "focus textarea" : "open", "submit form" : "createStatusMessage", "click #submit" : "createStatusMessage", - "textchange #status_message_fake_text": "handleTextchange", + "textchange #status_message_text": "checkSubmitAvailability", "click #locator" : "showLocation", "click #poll_creator" : "togglePollCreator", "click #hide_location" : "destroyLocation", @@ -35,8 +35,7 @@ app.views.Publisher = Backbone.View.extend({ this.disabled = false; // init shortcut references to the various elements - this.inputEl = this.$("#status_message_fake_text"); - this.hiddenInputEl = this.$("#status_message_text"); + this.inputEl = this.$("#status_message_text"); this.wrapperEl = this.$("#publisher_textarea_wrapper"); this.submitEl = this.$("input[type=submit], button#submit"); this.photozoneEl = this.$("#photodropzone"); @@ -46,14 +45,6 @@ app.views.Publisher = Backbone.View.extend({ $(window).on("beforeunload", _.bind(this._beforeUnload, this)); $(window).unload(this.clear.bind(this)); - // sync textarea content - if( this.hiddenInputEl.val() === "" ) { - this.hiddenInputEl.val( this.inputEl.val() ); - } - if( this.inputEl.val() === "" ) { - this.inputEl.val( this.hiddenInputEl.val() ); - } - // hide close and preview buttons and manage services link // in case publisher is standalone // (e.g. bookmarklet, mentions popup) @@ -163,7 +154,7 @@ app.views.Publisher = Backbone.View.extend({ this.viewPollCreator.render(); if (this.prefillMention) { - this.handleTextchange(); + this.checkSubmitAvailability(); } }, @@ -175,12 +166,11 @@ app.views.Publisher = Backbone.View.extend({ // inject content into the publisher textarea setText: function(txt) { this.inputEl.val(txt); - this.hiddenInputEl.val(txt); this.prefillText = txt; this.inputEl.trigger("input"); autosize.update(this.inputEl); - this.handleTextchange(); + this.checkSubmitAvailability(); }, // show the "getting started" popups around the publisher @@ -202,9 +192,6 @@ app.views.Publisher = Backbone.View.extend({ // empty poll answer and failing validation. this.viewPollCreator.removeLastAnswer(); - //add missing mentions at end of post: - this.handleTextchange(); - var serializedForm = $(evt.target).closest("form").serializeObject(); // disable input while posting, must be after the form is serialized this.setInputEnabled(false); @@ -255,7 +242,7 @@ app.views.Publisher = Backbone.View.extend({ self.setButtonsEnabled(true); self.setInputEnabled(true); self.wrapperEl.removeClass("submitting"); - self.handleTextchange(); + self.checkSubmitAvailability(); autosize.update(self.inputEl); } }); @@ -330,13 +317,8 @@ app.views.Publisher = Backbone.View.extend({ }, createPostPreview: function() { - //add missing mentions at end of post: - this.handleTextchange(); - var serializedForm = $("#new_status_message").serializeObject(); - var text = this.mention.getTextForSubmit(); var photos = this.getUploadedPhotos(); - var mentionedPeople = this.mention.mentionedPeople; var poll = this.getPollData(serializedForm); var locationCoords = serializedForm["location[coords]"]; if(!locationCoords || locationCoords === "") { @@ -352,12 +334,12 @@ app.views.Publisher = Backbone.View.extend({ var previewMessage = { "id": 0, - "text": text, + "text": serializedForm["status_message[text]"], "public": serializedForm["aspect_ids[]"] === "public", "created_at": new Date().toISOString(), "interacted_at": new Date().toISOString(), "author": app.currentUser ? app.currentUser.attributes : {}, - "mentioned_people": mentionedPeople, + "mentioned_people": this.mention.getMentionedPeople(), "photos": photos, "title": serializedForm["status_message[text]"], "location": location, @@ -381,11 +363,10 @@ app.views.Publisher = Backbone.View.extend({ // remove mentions this.mention.reset(); - // clear text(s) + // clear text this.inputEl.val(""); - this.hiddenInputEl.val(""); this.inputEl.trigger("keyup") - .trigger("keydown"); + .trigger("keydown"); autosize.update(this.inputEl); // remove photos @@ -421,7 +402,6 @@ app.views.Publisher = Backbone.View.extend({ // force textchange plugin to update lastValue this.inputEl.data("lastValue", ""); - this.hiddenInputEl.data("lastValue", ""); return this; }, @@ -472,8 +452,7 @@ app.views.Publisher = Backbone.View.extend({ setEnabled: function(bool) { this.setInputEnabled(bool); this.disabled = !bool; - - this.handleTextchange(); + this.checkSubmitAvailability(); }, setButtonsEnabled: function(bool) { @@ -487,10 +466,8 @@ app.views.Publisher = Backbone.View.extend({ setInputEnabled: function(bool) { if (bool) { this.inputEl.removeAttr("disabled"); - this.hiddenInputEl.removeAttr("disabled"); } else { this.inputEl.prop("disabled", true); - this.hiddenInputEl.prop("disabled", true); } }, @@ -503,11 +480,6 @@ app.views.Publisher = Backbone.View.extend({ return (!onlyWhitespaces || isPhotoAttached) && isValidPoll && !this.disabled; }, - handleTextchange: function() { - this.checkSubmitAvailability(); - this.hiddenInputEl.val(this.mention.getTextForSubmit()); - }, - _beforeUnload: function(e) { if(this._submittable() && this.inputEl.val() !== this.prefillText){ var confirmationMessage = Diaspora.I18n.t("confirm_unload"); diff --git a/app/assets/stylesheets/mentions.scss b/app/assets/stylesheets/mentions.scss index a6920e7b3..c9af7be45 100644 --- a/app/assets/stylesheets/mentions.scss +++ b/app/assets/stylesheets/mentions.scss @@ -64,28 +64,4 @@ } } } - - .mentions-box { - position: absolute; - right: 0px; - bottom: 0px; - left: 0px; - top: 0px; - padding: $padding-base-vertical $padding-base-horizontal; - } - - .mentions { - color: transparent; - font-size: $font-size-base; - font-family: Arial, Helvetica, sans-serif; - overflow: hidden; - width: 100%; - white-space: pre-wrap; - word-wrap: break-word; - - > strong { - background: $background-blue; - font-weight: normal; - } - } } diff --git a/app/assets/stylesheets/publisher.scss b/app/assets/stylesheets/publisher.scss index 0ce5b462c..94510aed7 100644 --- a/app/assets/stylesheets/publisher.scss +++ b/app/assets/stylesheets/publisher.scss @@ -14,10 +14,6 @@ display: none !important; } - .mentions-box { - margin-top: 0; - } - #publisher_textarea_wrapper { border: 1px solid $border-grey !important; } } @@ -248,8 +244,6 @@ .locator { display: none; } } - &.submitting .mentions-box { display: none; } - .twitter-typeahead { left: -1px; // Override inline rule of Typeahead @@ -257,11 +251,6 @@ position: absolute !important; // scss-lint:enable ImportantRule } - - .mentions-box { - // Leave space for markdown editor header - margin-top: 42px; - } } .publisher-buttonbar { diff --git a/app/helpers/interim_stream_hackiness_helper.rb b/app/helpers/interim_stream_hackiness_helper.rb index b531871f2..4283a8126 100644 --- a/app/helpers/interim_stream_hackiness_helper.rb +++ b/app/helpers/interim_stream_hackiness_helper.rb @@ -21,16 +21,6 @@ module InterimStreamHackinessHelper end end - def publisher_hidden_text - if params[:prefill].present? - params[:prefill] - elsif defined?(@stream) - @stream.publisher.prefill - else - nil - end - end - def from_group(post) if defined?(@stream) && params[:controller] == 'multis' @stream.post_from_group(post) diff --git a/app/views/publisher/_publisher.html.haml b/app/views/publisher/_publisher.html.haml index 1304283a4..9ed2ae2ee 100644 --- a/app/views/publisher/_publisher.html.haml +++ b/app/views/publisher/_publisher.html.haml @@ -5,20 +5,17 @@ %params .publisher-textarea-wrapper#publisher_textarea_wrapper .mentions-input-box - .mentions-box - .mentions - if current_user.getting_started? - = status.text_area :fake_text, :rows => 2, :value => h(publisher_formatted_text), + = status.text_area :text, :rows => 2, :value => h(publisher_formatted_text), :tabindex => 1, :placeholder => "#{t('contacts.index.start_a_conversation')}...", "data-title" => popover_with_close_html("1. " + t("shared.public_explain.share")), "data-content" => t("shared.public_explain.new_user_welcome_message"), "class" => "form-control" - else - = status.text_area :fake_text, :rows => 2, :value => h(publisher_formatted_text), + = status.text_area :text, :rows => 2, :value => h(publisher_formatted_text), :tabindex => 1, :placeholder => "#{t('contacts.index.start_a_conversation')}...", "class" => "form-control" %input.typeahead-mention-box.hidden{type: "text"} - = status.hidden_field :text, value: h(publisher_hidden_text), class: "clear_on_submit" .container-fluid.photodropzone-container#photodropzone_container %ul#photodropzone diff --git a/features/desktop/follows_tags.feature b/features/desktop/follows_tags.feature index 78692e71c..fcdfbf3a5 100644 --- a/features/desktop/follows_tags.feature +++ b/features/desktop/follows_tags.feature @@ -16,7 +16,7 @@ Feature: posting Then I should see a ".tag-following-action .followed" Scenario: can post a message from the tag page - Then I should see "#boss" within "#publisher" + Then I should see "#boss " in the publisher When I click the publisher and post "#boss from the tag page" And I go to the tag page for "boss" Then I should see "#boss from the tag page" diff --git a/features/desktop/post_preview.feature b/features/desktop/post_preview.feature index 827b36d36..d2a69b687 100644 --- a/features/desktop/post_preview.feature +++ b/features/desktop/post_preview.feature @@ -44,7 +44,7 @@ Feature: preview posts in the stream Given I expand the publisher And I attach "spec/fixtures/button.png" to the publisher When I fill in the following: - | status_message_fake_text | Look at this dog | + | status_message_text | Look at this dog | And I preview the post Then I should see a "img" within ".md-preview .stream-element .photo_attachments" And I should see "Look at this dog" within ".md-preview .stream-element" @@ -63,7 +63,7 @@ Feature: preview posts in the stream Then I should see "Samuel Beckett" When I expand the publisher And I fill in the following: - | status_message_fake_text | This preview rocks | + | status_message_text | This preview rocks | And I preview the post Then I should see "This preview rocks" in the preview And I close the publisher @@ -71,11 +71,11 @@ Feature: preview posts in the stream Scenario: preview a post with the poll Given I expand the publisher When I fill in the following: - | status_message_fake_text | I am eating yogurt | + | status_message_text | I am eating yogurt | And I click on selector "#poll_creator" When I fill in the following: - | status_message_fake_text | I am eating yogurt | - | poll_question | What kind of yogurt do you like? | + | status_message_text | I am eating yogurt | + | poll_question | What kind of yogurt do you like? | And I fill in the following for the options: | normal | | not normal | @@ -87,12 +87,12 @@ Feature: preview posts in the stream Scenario: preview a post with location Given I expand the publisher When I fill in the following: - | status_message_fake_text | I am eating yogurt | + | status_message_text | I am eating yogurt | And I allow geolocation And I click on selector "#locator" When I fill in the following: - | status_message_fake_text | I am eating yogurt | - | location_address | Some cool place | + | status_message_text | I am eating yogurt | + | location_address | Some cool place | And I preview the post Then I should see a ".near-from" within ".md-preview .stream-element" And I should see "Some cool place" within ".md-preview .stream-element .near-from" diff --git a/features/desktop/post_with_a_poll.feature b/features/desktop/post_with_a_poll.feature index b5b3a0ff8..5a27fc7ee 100644 --- a/features/desktop/post_with_a_poll.feature +++ b/features/desktop/post_with_a_poll.feature @@ -40,8 +40,8 @@ Feature: posting with a poll Given I expand the publisher And I click on selector "#poll_creator" When I fill in the following: - | status_message_fake_text | I am eating yogurt | - | poll_question | What kind of yogurt do you like? | + | status_message_text | I am eating yogurt | + | poll_question | What kind of yogurt do you like? | And I fill in the following for the options: | normal | | not normal | @@ -53,8 +53,8 @@ Feature: posting with a poll Given I expand the publisher And I click on selector "#poll_creator" When I fill in the following: - | status_message_fake_text | I am eating yogurt | - | poll_question | What kind of yogurt do you like? | + | status_message_text | I am eating yogurt | + | poll_question | What kind of yogurt do you like? | And I fill in the following for the options: | normal | | not normal | @@ -70,8 +70,8 @@ Feature: posting with a poll Given I expand the publisher And I click on selector "#poll_creator" When I fill in the following: - | status_message_fake_text | I am eating yogurt | - | poll_question | What kind of yogurt do you like? | + | status_message_text | I am eating yogurt | + | poll_question | What kind of yogurt do you like? | And I fill in the following for the options: | normal | | not normal | @@ -83,8 +83,8 @@ Feature: posting with a poll Given I expand the publisher And I click on selector "#poll_creator" When I fill in the following: - | status_message_fake_text | I am eating yogurt | - | poll_question | What kind of yogurt do you like? | + | status_message_text | I am eating yogurt | + | poll_question | What kind of yogurt do you like? | And I fill in the following for the options: | normal | | | diff --git a/features/mobile/posts_from_main_page.feature b/features/mobile/posts_from_main_page.feature index 22fb6195a..9afee520d 100644 --- a/features/mobile/posts_from_main_page.feature +++ b/features/mobile/posts_from_main_page.feature @@ -20,7 +20,7 @@ Feature: posting from the mobile main page Scenario: post and delete some text Given I visit the mobile publisher page - And I append "I am eating yogurt" to the mobile publisher + And I append "I am eating yogurt" to the publisher And I select "Unicorns" from "aspect_ids_" And I press "Share" When I go to the stream page @@ -52,7 +52,7 @@ Feature: posting from the mobile main page Scenario: back out of uploading a picture when another has been attached Given I visit the mobile publisher page - And I append "I am eating yogurt" to the mobile publisher + And I append "I am eating yogurt" to the publisher And I attach the file "spec/fixtures/button.gif" to hidden "qqfile" within "#file-upload-publisher" And I attach the file "spec/fixtures/button.png" to hidden "qqfile" within "#file-upload-publisher" And I click to delete the first uploaded photo diff --git a/features/step_definitions/keyboard_navigation_steps.rb b/features/step_definitions/keyboard_navigation_steps.rb index e0109902d..32188aff3 100644 --- a/features/step_definitions/keyboard_navigation_steps.rb +++ b/features/step_definitions/keyboard_navigation_steps.rb @@ -5,7 +5,7 @@ When /^I press the "([^\"]*)" key somewhere$/ do |key| end When /^I press the "([^\"]*)" key in the publisher$/ do |key| - find("#status_message_fake_text").native.send_key(key) + find("#status_message_text").native.send_key(key) end Then /^post (\d+) should be highlighted$/ do |position| diff --git a/features/step_definitions/posts_steps.rb b/features/step_definitions/posts_steps.rb index b0b8e62b1..a13779a2c 100644 --- a/features/step_definitions/posts_steps.rb +++ b/features/step_definitions/posts_steps.rb @@ -32,6 +32,10 @@ Then /^I should not be able to submit the publisher$/ do expect(publisher_submittable?).to be false end +Then /^I should see "([^"]*)" in the publisher$/ do |text| + expect(page).to have_field("status_message[text]", with: text) +end + Given /^I have a limited post with text "([^\"]*)" in the aspect "([^"]*)"$/ do |text, aspect_name| @me.post :status_message, text: text, to: @me.aspects.where(name: aspect_name).first.id end @@ -114,10 +118,6 @@ When /^I append "([^"]*)" to the publisher$/ do |text| append_to_publisher(text) end -When /^I append "([^"]*)" to the mobile publisher$/ do |text| - append_to_publisher(text, '#status_message_text') -end - When /^I attach "([^"]*)" to the publisher$/ do |path| upload_file_with_publisher(path) end diff --git a/features/support/publishing_cuke_helpers.rb b/features/support/publishing_cuke_helpers.rb index 046f8e332..ff5f91d43 100644 --- a/features/support/publishing_cuke_helpers.rb +++ b/features/support/publishing_cuke_helpers.rb @@ -1,21 +1,12 @@ module PublishingCukeHelpers def write_in_publisher(txt) - fill_in 'status_message_fake_text', with: txt + fill_in "status_message_text", with: txt end - def append_to_publisher(txt, input_selector='#status_message_fake_text') - status_message_text = find("#status_message_text", visible: false).value - find(input_selector).native.send_key(" #{txt}") - - # make sure the other text field got the new contents - if input_selector == "#status_message_fake_text" - begin - expect(page).to have_selector("#status_message_text[value='#{status_message_text} #{txt}']", visible: false) - rescue RSpec::Expectations::ExpectationNotMetError - puts "Value was instead: #{find('#status_message_text', visible: false).value.inspect}" - raise - end - end + def append_to_publisher(txt) + status_message_text = find("#status_message_text").value + find("#status_message_text").native.send_key(" #{txt}") + expect(page).to have_field("status_message[text]", with: "#{status_message_text} #{txt}") end def upload_file_with_publisher(path) @@ -33,7 +24,7 @@ module PublishingCukeHelpers end def submit_publisher - txt = find("#publisher #status_message_fake_text").value + txt = find("#publisher #status_message_text").value find("#publisher .btn-primary").click # wait for the content to appear expect(find("#main_stream")).to have_content(txt) @@ -45,7 +36,7 @@ module PublishingCukeHelpers end def click_publisher - find("#status_message_fake_text").click + find("#status_message_text").click expect(find("#publisher")).to have_css(".publisher-textarea-wrapper.active") end diff --git a/spec/javascripts/app/views/bookmarklet_view_spec.js b/spec/javascripts/app/views/bookmarklet_view_spec.js index d5de57466..9d12f998c 100644 --- a/spec/javascripts/app/views/bookmarklet_view_spec.js +++ b/spec/javascripts/app/views/bookmarklet_view_spec.js @@ -29,23 +29,18 @@ describe("app.views.Bookmarklet", function() { it("prefills the publisher", function() { init_bookmarklet(test_data); - expect($.trim(app.publisher.inputEl.val())).not.toEqual(""); - expect($.trim(app.publisher.hiddenInputEl.val())).not.toEqual(""); }); it("handles dirty input well", function() { init_bookmarklet(evil_test_data); - expect($.trim(app.publisher.inputEl.val())).not.toEqual(""); - expect($.trim(app.publisher.hiddenInputEl.val())).not.toEqual(""); }); it("allows changing a prefilled publisher", function() { init_bookmarklet(test_data); app.publisher.setText(app.publisher.inputEl.val()+"A"); - - expect(app.publisher.hiddenInputEl.val()).toMatch(/.+A$/); + expect(app.publisher.inputEl.val()).toMatch(/.+A$/); }); it("keeps the publisher disabled after successful post creation", function() { diff --git a/spec/javascripts/app/views/publisher_mention_view_spec.js b/spec/javascripts/app/views/publisher_mention_view_spec.js index d4e9417e3..5f53c110f 100644 --- a/spec/javascripts/app/views/publisher_mention_view_spec.js +++ b/spec/javascripts/app/views/publisher_mention_view_spec.js @@ -7,7 +7,6 @@ describe("app.views.PublisherMention", function() { it("initializes object properties", function() { this.view = new app.views.PublisherMention({ el: "#publisher" }); expect(this.view.mentionedPeople).toEqual([]); - expect(this.view.invisibleChar).toBe("\u200B"); 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() { beforeEach(function() { 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() { - 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); this.view.cleanMentionedPeople(); expect(this.view.mentionedPeople.length).toBe(0); }); 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); this.view.cleanMentionedPeople(); expect(this.view.ignoreDiasporaIds.length).toBe(0); }); it("keeps mentioned persons", function() { - this.view.addPersonToMentions({name: "user1", handle: "user1@pod.tld"}); - this.view.inputBox.val("user1"); + this.view.addPersonToMentions({name: "User Name", handle: "user1@pod.tld"}); + this.view.inputBox.val("@{user1@pod.tld}"); expect(this.view.mentionedPeople.length).toBe(1); this.view.cleanMentionedPeople(); expect(this.view.mentionedPeople.length).toBe(1); }); it("keeps mentioned persons for ignored diaspora ids", function() { - this.view.addPersonToMentions({name: "user1", handle: "user1@pod.tld"}); - this.view.inputBox.val("user1"); + this.view.addPersonToMentions({name: "User Name", handle: "user1@pod.tld"}); + this.view.inputBox.val("@{user1@pod.tld}"); expect(this.view.ignoreDiasporaIds.length).toBe(1); this.view.cleanMentionedPeople(); expect(this.view.ignoreDiasporaIds.length).toBe(1); @@ -151,61 +161,29 @@ describe("app.views.PublisherMention", function() { it("correctly formats the text", function() { 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() { this.view.inputBox.val("@user1337 123 user2 @user2 456 @user3 789"); this.view.inputBox[0].setSelectionRange(26, 26); 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.onSuggestionSelection({name: "user1337", handle: "user1@pod.tld"}); - expect(this.view.inputBox.val()).toBe("\u200Buser1337 123 user2 \u200Buser23 456 @user3 789"); - this.view.inputBox[0].setSelectionRange(38, 38); + expect(this.view.inputBox.val()).toBe("@{user1@pod.tld} 123 user2 @{user2@pod.tld} 456 @user3 789"); + this.view.inputBox[0].setSelectionRange(54, 54); this.view.onSuggestionSelection({name: "user32", handle: "user3@pod.tld"}); - expect(this.view.inputBox.val()).toBe("\u200Buser1337 123 user2 \u200Buser23 456 \u200Buser32 789"); - }); - - it("calls updateMessageTexts", function() { - spyOn(this.view, "updateMessageTexts"); - this.view.onSuggestionSelection({name: "user1337", handle: "user1@pod.tld"}); - expect(this.view.updateMessageTexts).toHaveBeenCalled(); + expect(this.view.inputBox.val()).toBe("@{user1@pod.tld} 123 user2 @{user2@pod.tld} 456 @{user3@pod.tld} 789"); }); it("places the caret at the right position", function() { 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); }); }); - describe("updateMessageTexts", function() { - beforeEach(function() { - this.view = new app.views.PublisherMention({ el: "#publisher" }); - this.view.inputBox.val("@user1 Text before \u200Buser1\ntext after"); - this.view.mentionedPeople.push({"name": "user1", "handle": "user1@pod.tld"}); - }); - - it("sets the correct messageText", function() { - this.view.updateMessageTexts(); - expect(this.view.inputBox.data("messageText")).toBe("@user1 Text before @{user1 ; user1@pod.tld}\ntext after"); - }); - - it("formats overlay text to HTML", function() { - this.view.updateMessageTexts(); - expect(this.view.mentionsBox.find(".mentions").html()) - .toBe("@user1 Text before user1\ntext after"); - }); - - it("properly escapes the user input", function() { - this.view.inputBox.val(" @user1 Text before \u200Buser1\ntext after"); - this.view.updateMessageTexts(); - expect(this.view.mentionsBox.find(".mentions").html()) - .toBe("<img src=\"/default.png\"> @user1 Text before user1\ntext after"); - }); - }); - describe("updateTypeaheadInput", function() { beforeEach(function() { this.view = new app.views.PublisherMention({ el: "#publisher" }); @@ -251,20 +229,34 @@ describe("app.views.PublisherMention", function() { expect(this.view.closeSuggestions).not.toHaveBeenCalled(); expect(this.view.typeaheadInput.val()).toBe("user"); }); + + it("doesn't call 'cleanMentionedPeople' if there is no '@' in front of the caret", function() { + spyOn(this.view, "cleanMentionedPeople"); + this.view.inputBox.val("user1337 Text before @user1 text after"); + this.view.inputBox[0].setSelectionRange(9, 9); + this.view.updateTypeaheadInput(); + expect(this.view.cleanMentionedPeople).not.toHaveBeenCalled(); + }); + + it("calls 'cleanMentionedPeople' if there is an '@' in front of the caret", function() { + spyOn(this.view, "cleanMentionedPeople"); + this.view.inputBox.val("@user1337 Text before @user1 text after"); + this.view.inputBox[0].setSelectionRange(9, 9); + this.view.updateTypeaheadInput(); + expect(this.view.cleanMentionedPeople).toHaveBeenCalled(); + }); }); describe("prefillMention", function() { beforeEach(function() { this.view = new app.views.PublisherMention({ el: "#publisher" }); spyOn(this.view, "addPersonToMentions"); - spyOn(this.view, "updateMessageTexts"); }); it("prefills one mention", function() { this.view.prefillMention([{"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.inputBox.val()).toBe("\u200Buser1"); + expect(this.view.inputBox.val()).toBe("@{user1@pod.tld}"); }); it("prefills multiple mentions", function() { @@ -275,8 +267,7 @@ describe("app.views.PublisherMention", function() { 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.updateMessageTexts).toHaveBeenCalled(); - expect(this.view.inputBox.val()).toBe("\u200Buser1 \u200Buser2"); + expect(this.view.inputBox.val()).toBe("@{user1@pod.tld} @{user2@pod.tld}"); }); }); @@ -372,30 +363,6 @@ describe("app.views.PublisherMention", function() { }); }); - describe("onInputBoxInput", function() { - beforeEach(function() { - this.view = new app.views.PublisherMention({ el: "#publisher" }); - }); - - it("calls 'cleanMentionedPeople'", function() { - spyOn(this.view, "cleanMentionedPeople"); - this.view.onInputBoxInput(); - expect(this.view.cleanMentionedPeople).toHaveBeenCalled(); - }); - - it("calls 'updateMessageTexts'", function() { - spyOn(this.view, "updateMessageTexts"); - this.view.onInputBoxInput(); - expect(this.view.updateMessageTexts).toHaveBeenCalled(); - }); - - it("calls 'updateTypeaheadInput'", function() { - spyOn(this.view, "updateTypeaheadInput"); - this.view.onInputBoxInput(); - expect(this.view.updateTypeaheadInput).toHaveBeenCalled(); - }); - }); - describe("onInputBoxClick", function() { beforeEach(function() { this.view = new app.views.PublisherMention({ el: "#publisher" }); @@ -423,13 +390,13 @@ describe("app.views.PublisherMention", function() { describe("reset", function() { beforeEach(function() { this.view = new app.views.PublisherMention({ el: "#publisher" }); - spyOn(this.view, "onInputBoxInput"); + spyOn(this.view, "updateTypeaheadInput"); }); it("resets the mention box", function() { this.view.reset(); expect(this.view.inputBox.val()).toBe(""); - expect(this.view.onInputBoxInput).toHaveBeenCalled(); + expect(this.view.updateTypeaheadInput).toHaveBeenCalled(); }); }); @@ -454,27 +421,21 @@ describe("app.views.PublisherMention", function() { }); }); - describe("getTextForSubmit", function() { + describe("getMentionedPeople", function() { beforeEach(function() { - this.view = new app.views.PublisherMention({ el: "#publisher" }); - this.view.bloodhound.add([ - {person: true, name: "user1", handle: "user1@pod.tld"} - ]); + this.view = new app.views.PublisherMention({el: "#publisher"}); }); - it("returns text with mention if someone has been mentioned", function() { - this.view.inputBox.val("@user"); - this.view.inputBox[0].setSelectionRange(5, 5); - this.view.typeaheadInput.typeahead("val", "user"); - this.view.typeaheadInput.typeahead("open"); - this.view.$(".tt-suggestion").first().click(); - expect(this.view.getTextForSubmit()).toBe("@{user1 ; user1@pod.tld}"); + it("calls 'cleanMentionedPeople'", function() { + spyOn(this.view, "cleanMentionedPeople"); + this.view.getMentionedPeople(); + expect(this.view.cleanMentionedPeople).toHaveBeenCalled(); }); - it("returns normal text if nobody has been mentioned", function() { - this.view.inputBox.data("messageText", "Bad text"); - this.view.inputBox.val("Good text"); - expect(this.view.getTextForSubmit()).toBe("Good text"); + it("returns the cleaned mentionedPeople", function() { + this.view.inputBox.val("@{user1@pod.tld} user2@pod.tld"); + this.view.mentionedPeople = [{name: "user1", handle: "user1@pod.tld"}, {name: "user2", handle: "user2@pod.tld"}]; + expect(this.view.getMentionedPeople()).toEqual([{name: "user1", handle: "user1@pod.tld"}]); }); }); }); diff --git a/spec/javascripts/app/views/publisher_view_spec.js b/spec/javascripts/app/views/publisher_view_spec.js index c672b33fa..e540669b6 100644 --- a/spec/javascripts/app/views/publisher_view_spec.js +++ b/spec/javascripts/app/views/publisher_view_spec.js @@ -43,17 +43,11 @@ describe("app.views.Publisher", function() { }); describe("#initSubviews", function() { - it("calls handleTextchange if the publisher is prefilled with mentions", function() { - spyOn(this.view, "handleTextchange"); + it("calls checkSubmitAvailability if the publisher is prefilled with mentions", function() { + spyOn(this.view, "checkSubmitAvailability"); this.view.prefillMention = "user@example.org"; this.view.initSubviews(); - expect(this.view.handleTextchange).toHaveBeenCalled(); - }); - - it("doesn't call handleTextchange if there are no prefilled mentions", function() { - spyOn(this.view, "handleTextchange"); - this.view.initSubviews(); - expect(this.view.handleTextchange).not.toHaveBeenCalled(); + expect(this.view.checkSubmitAvailability).toHaveBeenCalled(); }); }); @@ -82,9 +76,9 @@ describe("app.views.Publisher", function() { }); it("resets the element's height", function() { - $(this.view.el).find("#status_message_fake_text").height(100); + $(this.view.el).find("#status_message_text").height(100); this.view.close($.Event()); - expect($(this.view.el).find("#status_message_fake_text").attr("style")).not.toContain("height"); + expect($(this.view.el).find("#status_message_text").attr("style")).not.toContain("height"); }); it("calls autosize.update", function() { @@ -203,12 +197,6 @@ describe("app.views.Publisher", function() { }); describe("createStatusMessage", function(){ - it("calls handleTextchange to complete missing mentions", function(){ - spyOn(this.view, "handleTextchange"); - this.view.createStatusMessage($.Event()); - expect(this.view.handleTextchange).toHaveBeenCalled(); - }); - it("adds the status message to the stream", function() { app.stream = { addNow: $.noop }; spyOn(app.stream, "addNow"); @@ -224,20 +212,10 @@ describe("app.views.Publisher", function() { }); }); - describe("createPostPreview", function(){ - it("calls handleTextchange to complete missing mentions", function(){ - spyOn(this.view, "handleTextchange"); - this.view.createPostPreview(); - expect(this.view.handleTextchange).toHaveBeenCalled(); - }); - }); - describe('#setText', function() { it("sets the content text", function() { this.view.setText("FOO bar"); - expect(this.view.inputEl.val()).toEqual("FOO bar"); - expect(this.view.hiddenInputEl.val()).toEqual("FOO bar"); }); }); @@ -248,7 +226,6 @@ describe("app.views.Publisher", function() { expect(this.view.disabled).toBeTruthy(); expect(this.view.inputEl.prop("disabled")).toBeTruthy(); - expect(this.view.hiddenInputEl.prop("disabled")).toBeTruthy(); }); it("disables submitting", function() {