From b64b039d21999e88f8efbaaeea3b3b49968f68a4 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:04:16 +0100 Subject: [PATCH 01/12] Move tweet formatting into a separate helper class --- app/helpers/social_helper.rb | 4 +++ app/helpers/social_helper/twitter_methods.rb | 38 ++++++++++++++++++++ app/models/services/twitter.rb | 31 +--------------- 3 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 app/helpers/social_helper.rb create mode 100644 app/helpers/social_helper/twitter_methods.rb diff --git a/app/helpers/social_helper.rb b/app/helpers/social_helper.rb new file mode 100644 index 00000000..00447583 --- /dev/null +++ b/app/helpers/social_helper.rb @@ -0,0 +1,4 @@ +module SocialHelper + include SocialHelper::TwitterMethods + include SocialHelper::TumblrMethods +end \ No newline at end of file diff --git a/app/helpers/social_helper/twitter_methods.rb b/app/helpers/social_helper/twitter_methods.rb new file mode 100644 index 00000000..04579e28 --- /dev/null +++ b/app/helpers/social_helper/twitter_methods.rb @@ -0,0 +1,38 @@ +require 'cgi' + +module SocialHelper::TwitterMethods + include Rails.application.routes.url_helpers + include MarkdownHelper + + def prepare_tweet(answer) + question_content = twitter_markdown answer.question.content.gsub(/\@(\w+)/, '\1') + original_question_length = question_content.length + answer_content = twitter_markdown answer.content + original_answer_length = answer_content.length + answer_url = show_user_answer_url( + id: answer.id, + username: answer.user.screen_name, + host: APP_CONFIG['hostname'], + protocol: (APP_CONFIG['https'] ? :https : :http) + ) + + parsed_tweet = { :valid => false } + tweet_text = "" + + until parsed_tweet[:valid] + tweet_text = "#{question_content[0..122]}#{'…' if original_question_length > [123, question_content.length].min}" \ + " — #{answer_content[0..123]}#{'…' if original_answer_length > [124, answer_content.length].min} #{answer_url}" + + parsed_tweet = Twitter::TwitterText::Validation::parse_tweet(tweet_text) + + question_content = question_content[0..-2] + answer_content = answer_content[0..-2] + end + + tweet_text + end + + def twitter_share_url(answer) + "https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}" + end +end \ No newline at end of file diff --git a/app/models/services/twitter.rb b/app/models/services/twitter.rb index 0e090bcd..c605bc93 100644 --- a/app/models/services/twitter.rb +++ b/app/models/services/twitter.rb @@ -1,6 +1,5 @@ class Services::Twitter < Service - include Rails.application.routes.url_helpers - include MarkdownHelper + include SocialHelper::TwitterMethods def provider "twitter" @@ -25,32 +24,4 @@ class Services::Twitter < Service def post_tweet(answer) client.update! prepare_tweet(answer) end - - def prepare_tweet(answer) - question_content = twitter_markdown answer.question.content.gsub(/\@(\w+)/, '\1') - original_question_length = question_content.length - answer_content = twitter_markdown answer.content - original_answer_length = answer_content.length - answer_url = show_user_answer_url( - id: answer.id, - username: answer.user.screen_name, - host: APP_CONFIG['hostname'], - protocol: (APP_CONFIG['https'] ? :https : :http) - ) - - parsed_tweet = { :valid => false } - tweet_text = "" - - until parsed_tweet[:valid] - tweet_text = "#{question_content[0..122]}#{'…' if original_question_length > [123, question_content.length].min}" \ - " — #{answer_content[0..123]}#{'…' if original_answer_length > [124, answer_content.length].min} #{answer_url}" - - parsed_tweet = Twitter::TwitterText::Validation::parse_tweet(tweet_text) - - question_content = question_content[0..-2] - answer_content = answer_content[0..-2] - end - - tweet_text - end end From ed64a0990e8a2a99a599c92e6161073cf37c6ad4 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:04:46 +0100 Subject: [PATCH 02/12] Move tumblr post formatting into a separate helper class --- app/helpers/social_helper/tumblr_methods.rb | 37 +++++++++++++++++++++ app/models/services/tumblr.rb | 18 ++-------- 2 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 app/helpers/social_helper/tumblr_methods.rb diff --git a/app/helpers/social_helper/tumblr_methods.rb b/app/helpers/social_helper/tumblr_methods.rb new file mode 100644 index 00000000..b9c3cfc3 --- /dev/null +++ b/app/helpers/social_helper/tumblr_methods.rb @@ -0,0 +1,37 @@ +require 'cgi' + +module SocialHelper::TumblrMethods + include Rails.application.routes.url_helpers + + def tumblr_title(answer) + asker = if answer.question.author_is_anonymous? + APP_CONFIG['anonymous_name'] + else + answer.question.user.profile.display_name.blank? ? answer.question.user.screen_name : answer.question.user.profile.display_name + end + + "#{asker} asked: #{answer.question.content}" + end + + def tumblr_body(answer) + answer_url = show_user_answer_url( + id: answer.id, + username: answer.user.screen_name, + host: APP_CONFIG['hostname'], + protocol: (APP_CONFIG['https'] ? :https : :http) + ) + + "#{answer.content}\n\n[Smile or comment on the answer here](#{answer_url})" + end + + def tumblr_share_url(answer) + answer_url = show_user_answer_url( + id: answer.id, + username: answer.user.screen_name, + host: APP_CONFIG['hostname'], + protocol: (APP_CONFIG['https'] ? :https : :http) + ) + + "https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape(answer_url)}&caption=&content=#{CGI.escape(tumblr_body(answer))}" + end +end \ No newline at end of file diff --git a/app/models/services/tumblr.rb b/app/models/services/tumblr.rb index 8af15e1d..e99d8d56 100644 --- a/app/models/services/tumblr.rb +++ b/app/models/services/tumblr.rb @@ -1,6 +1,5 @@ class Services::Tumblr < Service - include Rails.application.routes.url_helpers - include MarkdownHelper + include SocialHelper::TumblrMethods def provider "tumblr" @@ -23,21 +22,10 @@ class Services::Tumblr < Service end def create_post(answer) - answer_url = show_user_answer_url( - id: answer.id, - username: answer.user.screen_name, - host: APP_CONFIG['hostname'], - protocol: (APP_CONFIG['https'] ? :https : :http) - ) - asker = if answer.question.author_is_anonymous? - APP_CONFIG['anonymous_name'] - else - answer.question.user.profile.display_name.blank? ? answer.question.user.screen_name : answer.question.user.profile.display_name - end client.text( self.uid, - title: "#{asker} asked: #{answer.question.content}", - body: "#{answer.content}\n\n[Smile or comment on the answer here](#{answer_url})", + title: tumblr_title(answer), + body: tumblr_body(answer), format: 'markdown', tweet: 'off' ) From fa37f5c8577071e133c06ead12f95e7b70b855cc Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:06:52 +0100 Subject: [PATCH 03/12] Add share links to answerboxes --- app/views/answerbox/_actions.haml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/views/answerbox/_actions.haml b/app/views/answerbox/_actions.haml index 3c8eadd3..e7a0c58a 100644 --- a/app/views/answerbox/_actions.haml +++ b/app/views/answerbox/_actions.haml @@ -16,8 +16,18 @@ %button.btn.btn-link.answerbox__action{ type: :button, name: 'ab-comments', data: { a_id: a.id, state: :hidden } } %i.fa.fa-fw.fa-comments %span{ id: "ab-comment-count-#{a.id}" }= a.comment_count -%button.btn.btn-link.answerbox__action{ type: :button, name: 'ab-share' } - %i.fa.fa-fw.fa-share-alt{ title: 'Share' } +.btn-group + %button.btn.btn-link.answerbox__action{ data: { toggle: :dropdown }, aria: { expanded: false } } + %i.fa.fa-fw.fa-share-alt{ title: 'Share' } + .dropdown-menu.dropdown-menu-right{ role: :menu } + %a.dropdown-item{ href: twitter_share_url(a) } + %i.fa.fa-fw.fa-twitter + Share on Twitter + %a.dropdown-item{ href: tumblr_share_url(a) } + %i.fa.fa-fw.fa-tumblr + Share on Tumblr + %a.dropdown-item{ href: '#', name: 'ab-share' } + Share on other apps... - if user_signed_in? .btn-group %button.btn.btn-default.btn-sm.dropdown-toggle{ data: { toggle: :dropdown }, aria: { expanded: false } } From b630baa870eb676e0d87559836615ae416fc61b4 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:07:17 +0100 Subject: [PATCH 04/12] Hide any element with `ab-share` as a name --- app/assets/stylesheets/components/_answerbox.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/components/_answerbox.scss b/app/assets/stylesheets/components/_answerbox.scss index 6ef19d72..74b33157 100644 --- a/app/assets/stylesheets/components/_answerbox.scss +++ b/app/assets/stylesheets/components/_answerbox.scss @@ -80,7 +80,7 @@ } body:not(.cap-web-share) { - .answerbox__action[name="ab-share"] { + [name="ab-share"] { display: none; } } From b202a1f7e77bcf7115835a919e2c0cadb02a2f6d Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:07:42 +0100 Subject: [PATCH 05/12] Add tests for `SocialHelper::TwitterMethods` --- .../social_helper/twitter_methods_spec.rb | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 spec/helpers/social_helper/twitter_methods_spec.rb diff --git a/spec/helpers/social_helper/twitter_methods_spec.rb b/spec/helpers/social_helper/twitter_methods_spec.rb new file mode 100644 index 00000000..4c334fc6 --- /dev/null +++ b/spec/helpers/social_helper/twitter_methods_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe SocialHelper::TwitterMethods, :type => :helper do + let(:user) { FactoryBot.create(:user) } + let(:answer) { FactoryBot.create(:answer, user: user, + content: 'a' * 255, + question_content: 'q' * 255) } + + describe '#prepare_tweet' do + context 'when the question and answer need to be shortened' do + subject { prepare_tweet(answer) } + + it 'should return a properly formatted tweet' do + expect(subject).to eq("#{'q' * 123}… — #{'a' * 124}… https://justask.rrerr.net/#{user.screen_name}/a/#{answer.id}") + end + end + + context 'when the question and answer are short' do + before do + answer.question.content = 'Why are raccoons so good?' + answer.question.save! + answer.content = 'Because they are good cunes.' + answer.save! + end + + subject { prepare_tweet(answer) } + + it 'should return a properly formatted tweet' do + expect(subject).to eq("#{answer.question.content} — #{answer.content} https://justask.rrerr.net/#{user.screen_name}/a/#{answer.id}") + end + end + end + + describe '#twitter_share_url' do + subject { twitter_share_url(answer) } + + it 'should return a proper share link' do + expect(subject).to eq("https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}") + end + end +end \ No newline at end of file From 4ddfba9a79ecf38ab3aa6d2ef5b07ea1712907e3 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:07:58 +0100 Subject: [PATCH 06/12] Add tests for `SocialHelper::TumblrMethods` --- .../social_helper/tumblr_methods_spec.rb | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 spec/helpers/social_helper/tumblr_methods_spec.rb diff --git a/spec/helpers/social_helper/tumblr_methods_spec.rb b/spec/helpers/social_helper/tumblr_methods_spec.rb new file mode 100644 index 00000000..da353770 --- /dev/null +++ b/spec/helpers/social_helper/tumblr_methods_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe SocialHelper::TumblrMethods, :type => :helper do + let(:user) { FactoryBot.create(:user) } + let(:answer) { FactoryBot.create(:answer, user: user, + content: 'aaaa', + question_content: 'q') } + + describe '#tumblr_title' do + context 'Asker is anonymous' do + subject { tumblr_title(answer) } + + it 'should return a proper title' do + expect(subject).to eq('Anonymous asked: q') + end + end + + context 'Asker is known' do + before do + @user = FactoryBot.create(:user) + answer.question.user = @user + answer.question.author_is_anonymous = false + end + + subject { tumblr_title(answer) } + + it 'should return a proper title' do + expect(subject).to eq("#{answer.question.user.profile.display_name} asked: q") + end + end + end + + describe '#tumblr_body' do + subject { tumblr_body(answer) } + + it 'should return a proper body' do + expect(subject).to eq("aaaa\n\n[Smile or comment on the answer here](https://justask.rrerr.net/#{answer.user.screen_name}/a/#{answer.id})") + end + end + + describe '#tumblr_share_url' do + subject { tumblr_share_url(answer) } + + it 'should return a proper share link' do + expect(subject).to eq("https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape("https://justask.rrerr.net/#{answer.user.screen_name}/a/#{answer.id}")}&caption=&content=#{CGI.escape(tumblr_body(answer))}") + end + end +end \ No newline at end of file From 7be52bb7f2822417bb8d9b57d945db69e523f70f Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:17:53 +0100 Subject: [PATCH 07/12] Include Rails URL helpers in `Services::Twitter` --- app/models/services/twitter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/services/twitter.rb b/app/models/services/twitter.rb index c605bc93..53ae7b2a 100644 --- a/app/models/services/twitter.rb +++ b/app/models/services/twitter.rb @@ -1,4 +1,5 @@ class Services::Twitter < Service + include Rails.application.routes.url_helpers include SocialHelper::TwitterMethods def provider From 08a0f2e1ca888c6607847151d6a69f9d704c6c95 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 22:40:27 +0100 Subject: [PATCH 08/12] Don't include `Rails.application.routes.url_helpers` in `SocialHelper::` It badly trips up things --- app/helpers/social_helper/tumblr_methods.rb | 2 -- app/helpers/social_helper/twitter_methods.rb | 1 - 2 files changed, 3 deletions(-) diff --git a/app/helpers/social_helper/tumblr_methods.rb b/app/helpers/social_helper/tumblr_methods.rb index b9c3cfc3..0a16f258 100644 --- a/app/helpers/social_helper/tumblr_methods.rb +++ b/app/helpers/social_helper/tumblr_methods.rb @@ -1,8 +1,6 @@ require 'cgi' module SocialHelper::TumblrMethods - include Rails.application.routes.url_helpers - def tumblr_title(answer) asker = if answer.question.author_is_anonymous? APP_CONFIG['anonymous_name'] diff --git a/app/helpers/social_helper/twitter_methods.rb b/app/helpers/social_helper/twitter_methods.rb index 04579e28..03a473e8 100644 --- a/app/helpers/social_helper/twitter_methods.rb +++ b/app/helpers/social_helper/twitter_methods.rb @@ -1,7 +1,6 @@ require 'cgi' module SocialHelper::TwitterMethods - include Rails.application.routes.url_helpers include MarkdownHelper def prepare_tweet(answer) From 21c4e0662f13602716332d6742e32b54fad33341 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 23:01:16 +0100 Subject: [PATCH 09/12] Make the share event work on any answer ...not just the initially loaded set --- .../retrospring/features/answerbox/index.ts | 13 +++---------- .../retrospring/features/answerbox/share.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/javascript/retrospring/features/answerbox/index.ts b/app/javascript/retrospring/features/answerbox/index.ts index 9f836842..846a1ca8 100644 --- a/app/javascript/retrospring/features/answerbox/index.ts +++ b/app/javascript/retrospring/features/answerbox/index.ts @@ -1,17 +1,10 @@ -import registerEvents from 'utilities/registerEvents'; -import { createShareEvent } from './share'; +import { on } from 'retrospring/utilities/on'; +import { shareEventHandler } from './share'; export default (): void => { if ('share' in navigator) { document.body.classList.add('cap-web-share'); - const entries: NodeList = document.querySelectorAll('.answerbox:not(.js-initialized)'); - entries.forEach((element: HTMLElement) => { - registerEvents([ - { type: 'click', target: element.querySelector('[name=ab-share]'), handler: createShareEvent(element) } - ]); - - element.classList.add('js-initialized'); - }); + on('click', '[name=ab-share]', shareEventHandler); } } diff --git a/app/javascript/retrospring/features/answerbox/share.ts b/app/javascript/retrospring/features/answerbox/share.ts index 34c2ba54..f623ec75 100644 --- a/app/javascript/retrospring/features/answerbox/share.ts +++ b/app/javascript/retrospring/features/answerbox/share.ts @@ -1,11 +1,11 @@ import noop from 'utilities/noop'; -export function createShareEvent(answerbox: HTMLElement): () => void { - return function (): void { - navigator.share({ - url: answerbox.querySelector('.answerbox__answer-date > a, a.answerbox__permalink').href - }) +export function shareEventHandler(event: Event): void { + const answerbox = (event.target as HTMLElement).closest('.answerbox'); + + navigator.share({ + url: answerbox.querySelector('.answerbox__answer-date > a, a.answerbox__permalink').href + }) .then(noop) .catch(noop) - } } From 5fcf24646ed23f53595e3b97475ff8010f75f28b Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 23:28:46 +0100 Subject: [PATCH 10/12] Open share widgets in new tab --- app/views/answerbox/_actions.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/answerbox/_actions.haml b/app/views/answerbox/_actions.haml index e7a0c58a..ed9b053f 100644 --- a/app/views/answerbox/_actions.haml +++ b/app/views/answerbox/_actions.haml @@ -20,10 +20,10 @@ %button.btn.btn-link.answerbox__action{ data: { toggle: :dropdown }, aria: { expanded: false } } %i.fa.fa-fw.fa-share-alt{ title: 'Share' } .dropdown-menu.dropdown-menu-right{ role: :menu } - %a.dropdown-item{ href: twitter_share_url(a) } + %a.dropdown-item{ href: twitter_share_url(a), target: '_blank' } %i.fa.fa-fw.fa-twitter Share on Twitter - %a.dropdown-item{ href: tumblr_share_url(a) } + %a.dropdown-item{ href: tumblr_share_url(a), target: '_blank' } %i.fa.fa-fw.fa-tumblr Share on Tumblr %a.dropdown-item{ href: '#', name: 'ab-share' } From a1ba1a80822aeec6d354d2bad160e266701b84c3 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 23:41:23 +0100 Subject: [PATCH 11/12] Apply review suggestion from @raccube Co-authored-by: Karina Kwiatek <6197148+raccube@users.noreply.github.com> --- app/helpers/social_helper/tumblr_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/social_helper/tumblr_methods.rb b/app/helpers/social_helper/tumblr_methods.rb index 0a16f258..f6e4710c 100644 --- a/app/helpers/social_helper/tumblr_methods.rb +++ b/app/helpers/social_helper/tumblr_methods.rb @@ -5,7 +5,7 @@ module SocialHelper::TumblrMethods asker = if answer.question.author_is_anonymous? APP_CONFIG['anonymous_name'] else - answer.question.user.profile.display_name.blank? ? answer.question.user.screen_name : answer.question.user.profile.display_name + answer.question.user.profile.safe_name end "#{asker} asked: #{answer.question.content}" From 453724be8ba9f4fbc53e06b72664d3ba134db0aa Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 26 Dec 2021 23:46:42 +0100 Subject: [PATCH 12/12] Prevent reloading of page on share link click --- app/javascript/retrospring/features/answerbox/share.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/retrospring/features/answerbox/share.ts b/app/javascript/retrospring/features/answerbox/share.ts index f623ec75..9018550e 100644 --- a/app/javascript/retrospring/features/answerbox/share.ts +++ b/app/javascript/retrospring/features/answerbox/share.ts @@ -1,6 +1,7 @@ import noop from 'utilities/noop'; export function shareEventHandler(event: Event): void { + event.preventDefault(); const answerbox = (event.target as HTMLElement).closest('.answerbox'); navigator.share({