From 93403b0af63f8e04fd5f7c96419bb8b9f4e015c8 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 11 Nov 2016 14:13:19 -0500 Subject: [PATCH] Replace the share popup with a component --- .../discourse/components/share-popup.js.es6 | 157 ++++++++++++++++++ .../discourse/controllers/share.js.es6 | 56 ------- .../templates/components/share-popup.hbs | 24 +++ .../javascripts/discourse/templates/share.hbs | 26 --- .../javascripts/discourse/templates/topic.hbs | 2 +- .../javascripts/discourse/views/share.js.es6 | 116 ------------- test/javascripts/acceptance/topic-test.js.es6 | 18 ++ 7 files changed, 200 insertions(+), 199 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/share-popup.js.es6 delete mode 100644 app/assets/javascripts/discourse/controllers/share.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/share-popup.hbs delete mode 100644 app/assets/javascripts/discourse/templates/share.hbs delete mode 100644 app/assets/javascripts/discourse/views/share.js.es6 diff --git a/app/assets/javascripts/discourse/components/share-popup.js.es6 b/app/assets/javascripts/discourse/components/share-popup.js.es6 new file mode 100644 index 00000000000..ffd52c91bdc --- /dev/null +++ b/app/assets/javascripts/discourse/components/share-popup.js.es6 @@ -0,0 +1,157 @@ +import { wantsNewWindow } from 'discourse/lib/intercept-click'; +import { longDateNoYear } from 'discourse/lib/formatter'; +import computed from 'ember-addons/ember-computed-decorators'; +import Sharing from 'discourse/lib/sharing'; + +export default Ember.Component.extend({ + elementId: 'share-link', + classNameBindings: ['visible'], + link: null, + visible: null, + + @computed + sources() { + return Sharing.activeSources(this.siteSettings.share_links); + }, + + @computed('type', 'postNumber') + shareTitle(type, postNumber) { + if (type === 'topic') { return I18n.t('share.topic'); } + if (postNumber) { + return I18n.t('share.post', { postNumber }); + } + return I18n.t('share.topic'); + }, + + @computed('date') + displayDate(date) { + return longDateNoYear(new Date(date)); + }, + + _focusUrl() { + const link = this.get('link'); + if (!this.capabilities.touch) { + const $linkInput = $('#share-link input'); + $linkInput.val(link); + + // Wait for the fade-in transition to finish before selecting the link: + window.setTimeout(() => $linkInput.select().focus(), 160); + } else { + const $linkForTouch = $('#share-link .share-for-touch a'); + $linkForTouch.attr('href', link); + $linkForTouch.html(link); + const range = window.document.createRange(); + range.selectNode($linkForTouch[0]); + window.getSelection().addRange(range); + } + }, + + _showUrl($target, url) { + const $currentTargetOffset = $target.offset(); + const $this = this.$(); + + if (Ember.isEmpty(url)) { return; } + + // Relative urls + if (url.indexOf("/") === 0) { + url = window.location.protocol + "//" + window.location.host + url; + } + + const shareLinkWidth = $this.width(); + let x = $currentTargetOffset.left - (shareLinkWidth / 2); + if (x < 25) { x = 25; } + if (x + shareLinkWidth > $(window).width()) { + x -= shareLinkWidth / 2; + } + + const header = $('.d-header'); + let y = $currentTargetOffset.top - ($this.height() + 20); + if (y < header.offset().top + header.height()) { + y = $currentTargetOffset.top + 10; + } + + $this.css({top: "" + y + "px"}); + + if (!this.site.mobileView) { + $this.css({left: "" + x + "px"}); + } + this.set('link', url); + this.set('visible', true); + + Ember.run.scheduleOnce('afterRender', this, this._focusUrl); + }, + + didInsertElement() { + this._super(); + + const $html = $('html'); + $html.on('mousedown.outside-share-link', e => { + // Use mousedown instead of click so this event is handled before routing occurs when a + // link is clicked (which is a click event) while the share dialog is showing. + if (this.$().has(e.target).length !== 0) { return; } + this.send('close'); + return true; + }); + + $html.on('click.discoure-share-link', '[data-share-url]', e => { + // if they want to open in a new tab, let it so + if (wantsNewWindow(e)) { return true; } + + e.preventDefault(); + + const $currentTarget = $(e.currentTarget); + const url = $currentTarget.data('share-url'); + const postNumber = $currentTarget.data('post-number'); + const postId = $currentTarget.closest('article').data('post-id'); + const date = $currentTarget.children().data('time'); + + this.setProperties({ postNumber, date, postId }); + this._showUrl($currentTarget, url); + return false; + }); + + $html.on('keydown.share-view', e => { + if (e.keyCode === 27) { + this.send('close'); + } + }); + + this.appEvents.on('share:url', (url, $target) => this._showUrl($target, url)); + }, + + willDestroyElement() { + this._super(); + $('html').off('click.discoure-share-link') + .off('mousedown.outside-share-link') + .off('keydown.share-view'); + }, + + actions: { + replyAsNewTopic() { + const postStream = this.get("topic.postStream"); + const postId = this.get("postId") || postStream.findPostIdForPostNumber(1); + const post = postStream.findLoadedPost(postId); + this.sendAction('replyAsNewTopic', post); + this.send("close"); + }, + + close() { + this.setProperties({ + link: null, + postNumber: null, + postId: null, + visible: false + }); + }, + + share(source) { + var url = source.generateUrl(this.get('link'), this.get('title')); + if (source.shouldOpenInPopup) { + window.open(url, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=600,height=' + (source.popupHeight || 315)); + } else { + window.open(url, '_blank'); + } + } + } + +}); diff --git a/app/assets/javascripts/discourse/controllers/share.js.es6 b/app/assets/javascripts/discourse/controllers/share.js.es6 deleted file mode 100644 index 00dc5363648..00000000000 --- a/app/assets/javascripts/discourse/controllers/share.js.es6 +++ /dev/null @@ -1,56 +0,0 @@ -import Sharing from 'discourse/lib/sharing'; -import { longDateNoYear } from 'discourse/lib/formatter'; -import computed from 'ember-addons/ember-computed-decorators'; - -export default Ember.Controller.extend({ - topic: Ember.inject.controller(), - - title: Ember.computed.alias('topic.model.title'), - canReplyAsNewTopic: Ember.computed.alias('topic.model.details.can_reply_as_new_topic'), - - @computed('type', 'postNumber') - shareTitle(type, postNumber) { - if (type === 'topic') { return I18n.t('share.topic'); } - if (postNumber) { - return I18n.t('share.post', { postNumber }); - } else { - return I18n.t('share.topic'); - } - }, - - @computed('date') - displayDate(date) { - return longDateNoYear(new Date(date)); - }, - - // Close the share controller - actions: { - close() { - this.setProperties({ link: null, postNumber: null, postId: null }); - return false; - }, - - replyAsNewTopic() { - const topicController = this.get("topic"); - const postStream = topicController.get("model.postStream"); - const postId = this.get("postId") || postStream.findPostIdForPostNumber(1); - const post = postStream.findLoadedPost(postId); - topicController.send("replyAsNewTopic", post); - this.send("close"); - }, - - share(source) { - var url = source.generateUrl(this.get('link'), this.get('title')); - if (source.shouldOpenInPopup) { - window.open(url, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=600,height=' + (source.popupHeight || 315)); - } else { - window.open(url, '_blank'); - } - } - }, - - @computed - sources() { - return Sharing.activeSources(this.siteSettings.share_links); - } -}); diff --git a/app/assets/javascripts/discourse/templates/components/share-popup.hbs b/app/assets/javascripts/discourse/templates/components/share-popup.hbs new file mode 100644 index 00000000000..65e1c5d3c6e --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/share-popup.hbs @@ -0,0 +1,24 @@ +

{{shareTitle}}

+ +{{#if date}} + {{displayDate}} +{{/if}} + +
+ + +
+ +{{#each sources as |s|}} + {{share-source source=s title=model.title action="share"}} +{{/each}} + +{{#if topic.details.can_reply_as_new_topic}} +
+ {{fa-icon "plus"}}{{i18n 'topic.create'}} +
+{{/if}} + + diff --git a/app/assets/javascripts/discourse/templates/share.hbs b/app/assets/javascripts/discourse/templates/share.hbs deleted file mode 100644 index 65b33218845..00000000000 --- a/app/assets/javascripts/discourse/templates/share.hbs +++ /dev/null @@ -1,26 +0,0 @@ -{{#if link}} -

{{shareTitle}}

- - {{#if date}} - {{displayDate}} - {{/if}} - -
- - -
- - {{#each sources as |s|}} - {{share-source source=s title=title action="share"}} - {{/each}} - - {{#if canReplyAsNewTopic}} -
- {{fa-icon "plus"}}{{i18n 'topic.create'}} -
- {{/if}} - - -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index d77551341af..68fd6469bcf 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -221,7 +221,7 @@ {{/if}} -{{render "share"}} +{{share-popup topic=model replyAsNewTopic="replyAsNewTopic"}} {{#if currentUser.enable_quoting}} {{render "quote-button"}} diff --git a/app/assets/javascripts/discourse/views/share.js.es6 b/app/assets/javascripts/discourse/views/share.js.es6 deleted file mode 100644 index 39d743cbbca..00000000000 --- a/app/assets/javascripts/discourse/views/share.js.es6 +++ /dev/null @@ -1,116 +0,0 @@ -import computed from 'ember-addons/ember-computed-decorators'; -import { observes } from 'ember-addons/ember-computed-decorators'; -import { wantsNewWindow } from 'discourse/lib/intercept-click'; - -export default Ember.View.extend({ - templateName: 'share', - elementId: 'share-link', - classNameBindings: ['hasLink'], - - @computed('controller.link') - hasLink(link) { - return !Ember.isEmpty(link) ? 'visible' : null; - }, - - @observes('controller.link') - linkChanged() { - const link = this.get('controller.link'); - if (!Ember.isEmpty(link)) { - Ember.run.next(() => { - if (!this.capabilities.touch) { - const $linkInput = $('#share-link input'); - $linkInput.val(link); - - // Wait for the fade-in transition to finish before selecting the link: - window.setTimeout(() => $linkInput.select().focus(), 160); - } else { - const $linkForTouch = $('#share-link .share-for-touch a'); - $linkForTouch.attr('href', link); - $linkForTouch.html(link); - const range = window.document.createRange(); - range.selectNode($linkForTouch[0]); - window.getSelection().addRange(range); - } - }); - } - }, - - didInsertElement() { - const self = this; - const $html = $('html'); - - $html.on('mousedown.outside-share-link', e => { - // Use mousedown instead of click so this event is handled before routing occurs when a - // link is clicked (which is a click event) while the share dialog is showing. - if (this.$().has(e.target).length !== 0) { return; } - this.get('controller').send('close'); - return true; - }); - - function showPanel($target, url, postNumber, date, postId) { - const $currentTargetOffset = $target.offset(); - const $shareLink = $('#share-link'); - - // Relative urls - if (url.indexOf("/") === 0) { - url = window.location.protocol + "//" + window.location.host + url; - } - - const shareLinkWidth = $shareLink.width(); - let x = $currentTargetOffset.left - (shareLinkWidth / 2); - if (x < 25) { x = 25; } - if (x + shareLinkWidth > $(window).width()) { - x -= shareLinkWidth / 2; - } - - const header = $('.d-header'); - let y = $currentTargetOffset.top - ($shareLink.height() + 20); - if (y < header.offset().top + header.height()) { - y = $currentTargetOffset.top + 10; - } - - $shareLink.css({top: "" + y + "px"}); - - if (!self.site.mobileView) { - $shareLink.css({left: "" + x + "px"}); - } - - self.set('controller.link', url); - self.set('controller.postNumber', postNumber); - self.set('controller.postId', postId); - self.set('controller.date', date); - } - - this.appEvents.on('share:url', (url, $target) => showPanel($target, url)); - - $html.on('click.discoure-share-link', '[data-share-url]', e => { - // if they want to open in a new tab, let it so - if (wantsNewWindow(e)) { return true; } - - e.preventDefault(); - - const $currentTarget = $(e.currentTarget), - url = $currentTarget.data('share-url'), - postNumber = $currentTarget.data('post-number'), - postId = $currentTarget.closest('article').data('post-id'), - date = $currentTarget.children().data('time'); - showPanel($currentTarget, url, postNumber, date, postId); - return false; - }); - - $html.on('keydown.share-view', e => { - if (e.keyCode === 27) { - this.get('controller').send('close'); - } - }); - }, - - willDestroyElement() { - this.get('controller').send('close'); - - $('html').off('click.discoure-share-link') - .off('mousedown.outside-share-link') - .off('keydown.share-view'); - } - -}); diff --git a/test/javascripts/acceptance/topic-test.js.es6 b/test/javascripts/acceptance/topic-test.js.es6 index 56fcc173f42..b3141c129af 100644 --- a/test/javascripts/acceptance/topic-test.js.es6 +++ b/test/javascripts/acceptance/topic-test.js.es6 @@ -1,6 +1,24 @@ import { acceptance } from "helpers/qunit-helpers"; acceptance("Topic", { loggedIn: true }); +test("Share Popup", () => { + visit("/t/internationalization-localization/280"); + andThen(() => { + ok(!exists('#share-link.visible'), 'it is not visible'); + }); + + click("[data-share-url]:eq(0)"); + andThen(() => { + ok(exists('#share-link.visible'), 'it shows the popup'); + ok(find('input[type=text]').val().length, 'it has the URL in the input box'); + }); + + click('#share-link .close-share'); + andThen(() => { + ok(!exists('#share-link.visible'), 'it closes the popup'); + }); +}); + test("Showing and hiding the edit controls", () => { visit("/t/internationalization-localization/280");