From f77f6bbf77386c030760990927de9e7e5a405951 Mon Sep 17 00:00:00 2001 From: Ben Ubois Date: Mon, 10 Jul 2017 21:44:13 -0700 Subject: [PATCH] More link functionality. --- app/assets/images/error-sign.svg | 1 + app/assets/javascripts/_site.js.coffee | 48 ++++++-- app/assets/stylesheets/_site.scss | 103 ++++++++++++++---- app/controllers/entries_controller.rb | 8 +- app/helpers/application_helper.rb | 28 +++++ app/jobs/view_link_cache.rb | 11 ++ app/presenters/base_presenter.rb | 30 ----- app/presenters/mercury_parser_presenter.rb | 7 -- app/views/entries/_content_info.html.erb | 2 +- app/views/entries/view_link_contents.js.erb | 7 +- app/views/feeds/_favicon.html.erb | 3 - app/views/feeds/search.js.erb | 2 +- app/views/settings/appearance.html.erb | 8 +- .../shared/_keyboard_shortcuts_wrap.html.erb | 2 +- app/views/shared/_link_actions.html.erb | 2 +- app/views/shared/_settings_modal.html.erb | 2 +- app/views/shared/_view_link.html.erb | 8 +- app/views/shared/_view_link_contents.html.erb | 8 +- .../shared/_view_link_contents_error.html.erb | 9 ++ app/views/shared/_view_link_wrap.html.erb | 7 +- config/routes.rb | 1 + 21 files changed, 205 insertions(+), 92 deletions(-) create mode 100644 app/assets/images/error-sign.svg create mode 100644 app/jobs/view_link_cache.rb delete mode 100644 app/views/feeds/_favicon.html.erb create mode 100644 app/views/shared/_view_link_contents_error.html.erb diff --git a/app/assets/images/error-sign.svg b/app/assets/images/error-sign.svg new file mode 100644 index 0000000000..7b87b4f86b --- /dev/null +++ b/app/assets/images/error-sign.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/app/assets/javascripts/_site.js.coffee b/app/assets/javascripts/_site.js.coffee index 3526a41f47..641b127dc4 100644 --- a/app/assets/javascripts/_site.js.coffee +++ b/app/assets/javascripts/_site.js.coffee @@ -57,8 +57,8 @@ $.extend feedbin, $('[data-behavior~=entry_content_target] pre code').each (i, e) -> hljs.highlightBlock(e) - audioVideo: -> - $('[data-behavior~=entry_content_target] audio, [data-behavior~=entry_content_target] video').mediaelementplayer() + audioVideo: (selector = "entry_content_target") -> + $("[data-behavior~=#{selector}] audio, [data-behavior~=#{selector}] video").mediaelementplayer() footnotes: -> $.bigfoot @@ -188,12 +188,13 @@ $.extend feedbin, resetScroll: -> $('.entry-content').prop('scrollTop', 0) - fitVids: -> - $('[data-behavior~=entry_content_target]').fitVids({ customSelector: "iframe[src*='youtu.be'], iframe[src*='www.flickr.com'], iframe[src*='view.vzaar.com'], iframe[src*='embed-ssl.ted.com']"}); + fitVids: (selector = "entry_content_target") -> + target = $("[data-behavior~=#{selector}]") + target.fitVids({ customSelector: "iframe[src*='youtu.be'], iframe[src*='www.flickr.com'], iframe[src*='view.vzaar.com'], iframe[src*='embed-ssl.ted.com']"}); - formatTweets: -> + formatTweets: (selector = "entry_content_wrap") -> if typeof(twttr) != "undefined" && typeof(twttr.widgets) != "undefined" - target = $('[data-behavior~=entry_content_wrap]')[0] + target = $("[data-behavior~=#{selector}]")[0] result = twttr.widgets.load(target) formatInstagram: -> @@ -238,6 +239,16 @@ $.extend feedbin, if 'console' of window console.log error + formatLinkContents: -> + try + feedbin.audioVideo("view_link_markup_wrap") + feedbin.fitVids("view_link_markup_wrap") + feedbin.formatTweets("view_link_markup_wrap") + feedbin.formatInstagram() + catch error + if 'console' of window + console.log error + refresh: -> if feedbin.data $.get(feedbin.data.auto_update_path) @@ -614,7 +625,9 @@ $.extend feedbin, preloadedImageIds: [] - linkActionsTimer: [] + linkActionsTimer: null + + linkCacheTimer: null ONE_HOUR: 60 * 60 * 1000 @@ -736,7 +749,7 @@ $.extend feedbin, return entryLinks: -> - $(document).on 'click', '[data-behavior~=entry_content_wrap] a', -> + $(document).on 'click', '[data-behavior~=entry_content_wrap] a, [data-behavior~=view_link_markup_wrap] a', -> $(this).attr('target', '_blank').attr('rel', 'noopener noreferrer') return @@ -1496,6 +1509,7 @@ $.extend feedbin, linkActionsHover: -> $(document).on 'mouseenter mouseleave', '.entry-final-content a', (event) -> clearTimeout(feedbin.linkActionsTimer) + clearTimeout(feedbin.linkCacheTimer) $('.entry-final-content a [data-behavior~=link_actions]').remove() link = $(@) @@ -1503,9 +1517,14 @@ $.extend feedbin, contents = contents[0].outerHTML if event.type == "mouseenter" + feedbin.linkCacheTimer = setTimeout ( -> + form = $("[data-behavior~=view_link_cache_form]") + $("#url", form).val(link.attr('href')) + form.submit() + ), 100 feedbin.linkActionsTimer = setTimeout ( -> link.append(contents) - ), 300 + ), 400 linkActions: -> $(document).on 'click', '[data-behavior~=view_link]', (event) -> @@ -1519,7 +1538,16 @@ $.extend feedbin, event.preventDefault() $(document).on 'click', '[data-behavior~=link_actions]', (event) -> - $(@).addClass('open') + windowWidth = $(window).width() + offset = $(@).offset().left + width = $(".dropdown-content", @).outerWidth() + + if offset + width >= windowWidth + $(@).addClass('open dropdown-right') + else + $(@).addClass('open dropdown-left') + + event.preventDefault() $.each feedbin.preInit, (i, item) -> diff --git a/app/assets/stylesheets/_site.scss b/app/assets/stylesheets/_site.scss index d64405ff95..2b739e0e1a 100644 --- a/app/assets/stylesheets/_site.scss +++ b/app/assets/stylesheets/_site.scss @@ -2778,14 +2778,14 @@ $saved-search-control-height: 48px; content: ""; height: 18px; width: 18px; - border: 1px solid #dcdee0; + border: 1px solid $border-color; border-radius: 3px; cursor: pointer; margin-right: -18px; - background: #FFF image-url("icon-caret.svg") 50% 50% no-repeat; + background: $body-background-color image-url("icon-caret.svg") 50% 50% no-repeat; background-size: 80% 80%; bottom: -2px; - box-shadow: 0 0 1px 0 #dcdee0; + box-shadow: 0 0 1px 0 $border-color; animation-duration: .3s; animation-name: slidein; .dropdown-content { @@ -2794,6 +2794,16 @@ $saved-search-control-height: 48px; ul { margin: 0 !important; } + .theme-sunset & { + background-color: $body-background-color-sunset; + box-shadow: 0 0 1px 0 $border-color-sunset; + border-color: $border-color-sunset; + } + .theme-night & { + background-color: $body-background-color-night; + box-shadow: 0 0 1px 0 $border-color-night; + border-color: $border-color-night; + } } } } @@ -4581,6 +4591,8 @@ Buttons text-decoration: none; border-color: $selected-hover; background-color: $selected-hover; + color: #FFF; + cursor: pointer; } &:active { text-decoration: none; @@ -4619,6 +4631,9 @@ Buttons background-color: transparent; color: $selected-active; } + .theme-night & { + color: #fff; + } } .button-text { @@ -4899,6 +4914,11 @@ button.close { font-weight: bold; height: 40px; width: 40px; + svg { + position: absolute; + top: 26px; + right: 26px; + } } .modal-body .close { position: absolute; @@ -4908,6 +4928,11 @@ button.close { font-weight: bold; height: 46px; width: 46px; + svg { + position: absolute; + top: 33px; + right: 33px; + } } .modal-title { margin: 0; @@ -5041,6 +5066,18 @@ button.close { .modal-header, .modal-body, .modal-footer { padding-left: 30px; padding-right: 30px; + @media (max-width: 550px) { + padding-left: 15px; + padding-right: 15px; + } + } + .entry-inner { + padding: 0 !important; + @media (max-width: 550px) { + .favicon-wrap { + display: none; + } + } } } .modal-header:before, @@ -5074,6 +5111,14 @@ Link Viewer } #view_link_wrap .post-meta { + margin-bottom: 30px; + .url { + color: $text-color-light; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } position: relative; .favicon-wrap { left: -26px; @@ -5082,31 +5127,16 @@ Link Viewer } .link-loading h1:empty, .link-loading p:empty { - @keyframes pulse { - 0% { - opacity: .9; - } - 50% { - opacity: .6; - } - 100% { - opacity: .9; - } - } - animation: pulse 3s linear infinite; - background: $text-color-light; + background: rgba($text-color-light, .2); .theme-sunset & { - background: $text-color-light-sunset; + background: rgba($text-color-light-sunset, .2); } .theme-night & { - background: $text-color-light-night; + background: rgba($text-color-light-night, .2); } } -.link-loading h1:empty { - height: 35px; -} .link-loading p:empty { - height: 20px; + height: 14px; } .link-loading p:nth-child(2n) { width: 90%; @@ -5117,6 +5147,35 @@ Link Viewer .link-loading p:last-child { width: 30%; } +.spinner { + height: 50px; + background: image-url("spinner-day.svg") 50% 50% no-repeat; + .theme-sunset & { + background: image-url("spinner-sunset.svg") 50% 50% no-repeat; + } + .theme-night & { + background: image-url("spinner-night.svg") 50% 50% no-repeat; + } +} + +.view-link-error-container { + display: flex; + justify-content: center; + flex-direction: column; + .view-link-error { + margin-top: 40px; + font-size: 24px; + margin-bottom: 40px; + span { + background: image-url("error-sign.svg") 0 50% no-repeat; + padding-left: 40px; + } + } + p { + display: inline-block; + text-align: center; + } +} /*------------------------------------------------------------------------------ Helpers diff --git a/app/controllers/entries_controller.rb b/app/controllers/entries_controller.rb index 68194e5b78..aed14eff7b 100644 --- a/app/controllers/entries_controller.rb +++ b/app/controllers/entries_controller.rb @@ -122,7 +122,8 @@ def view_link def view_link_contents @user = current_user - @content_info = Rails.cache.fetch("content_view:#{Digest::SHA1.hexdigest(params[:url])}:v5") do + @url = params[:url] + @content_info = Rails.cache.fetch("content_view:#{Digest::SHA1.hexdigest(@url)}:v5") do Librato.increment 'readability.first_parse' MercuryParser.parse(params[:url]) end @@ -134,6 +135,11 @@ def view_link_contents end end + def view_link_cache + ViewLinkCache.perform_async(params[:url]) + head :ok + end + def preload @user = current_user ids = params[:ids].split(',').map {|i| i.to_i } diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4378b950c6..f228eca65d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -73,4 +73,32 @@ def branch_info branch_name = `git rev-parse --abbrev-ref HEAD` " [#{branch_name.chomp}]" end + + def favicon_with_host(host) + if record = Favicon.find_by(host: host) + favicon_template(record.cdn_url) + else + favicon_url = favicon_service_url(host) + favicon_template(favicon_url) + end + end + + def favicon_template(favicon_url) + content_tag :span, '', class: "favicon-wrap" do + content_tag(:span, '', class: "favicon", style: "background-image: url(#{favicon_url});") + end + end + + def favicon_service_url(host) + uri = URI::HTTP.build( + scheme: "https", + host: "www.google.com", + path: "/s2/favicons", + query: {domain: host}.to_query + ) + uri.scheme = "https" + uri.to_s + end + + end diff --git a/app/jobs/view_link_cache.rb b/app/jobs/view_link_cache.rb new file mode 100644 index 0000000000..0d3e741060 --- /dev/null +++ b/app/jobs/view_link_cache.rb @@ -0,0 +1,11 @@ +class ViewLinkCache + include Sidekiq::Worker + sidekiq_options queue: :critical, retry: false + + def perform(url) + Rails.cache.fetch("content_view:#{Digest::SHA1.hexdigest(url)}:v5") do + MercuryParser.parse(url) + end + end + +end diff --git a/app/presenters/base_presenter.rb b/app/presenters/base_presenter.rb index 77ffededfa..db0120aff0 100644 --- a/app/presenters/base_presenter.rb +++ b/app/presenters/base_presenter.rb @@ -31,36 +31,6 @@ def favicon(feed) end end - def favicon_with_url(host) - favicon_url = favicon_service_url(host) - favicon_template(favicon_url) - end - - def favicon_template(favicon_url) - @template.content_tag :span, '', class: "favicon-wrap" do - @template.content_tag(:span, '', class: "favicon", style: "background-image: url(#{favicon_url});") - end - end - - def favicon_with_fallback - if @object.favicon && @object.favicon.cdn_url - favicon(@object) - else - favicon_with_url(@object.host) - end - end - - def favicon_service_url(host) - uri = URI::HTTP.build( - scheme: "https", - host: "www.google.com", - path: "/s2/favicons", - query: {domain: host}.to_query - ) - uri.scheme = "https" - uri.to_s - end - private def self.presents(name) diff --git a/app/presenters/mercury_parser_presenter.rb b/app/presenters/mercury_parser_presenter.rb index 68bfa61cc0..e777178da3 100644 --- a/app/presenters/mercury_parser_presenter.rb +++ b/app/presenters/mercury_parser_presenter.rb @@ -2,12 +2,5 @@ class MercuryParserPresenter < BasePresenter presents :mercury_parser - def favicon - if record = Favicon.find_by(host: mercury_parser.domain) - favicon_template(record.cdn_url) - else - favicon_with_url(mercury_parser.domain) - end - end end \ No newline at end of file diff --git a/app/views/entries/_content_info.html.erb b/app/views/entries/_content_info.html.erb index 0a9b9ef797..5dd4d7c7ca 100644 --- a/app/views/entries/_content_info.html.erb +++ b/app/views/entries/_content_info.html.erb @@ -3,7 +3,7 @@ <% if content.present? %> <% if content_info.present? && !entry_presenter.feed_domain_matches?(content_info.domain) %>
- <%= entry_presenter.favicon_with_url(content_info.domain) %> + <%= favicon_with_host(content_info.domain) %> <% if content_info.title.present? %> <% end %> diff --git a/app/views/entries/view_link_contents.js.erb b/app/views/entries/view_link_contents.js.erb index 1b0adcb74c..0c22ecb3be 100644 --- a/app/views/entries/view_link_contents.js.erb +++ b/app/views/entries/view_link_contents.js.erb @@ -1,3 +1,8 @@ modal = $('#view_link_wrap') target = $('[data-behavior~=markup_target]', modal) -target.html("<%= j render partial: 'shared/view_link_contents', locals: {page: @content_info, content: @content, user: @user} %>") +<% if @content %> + target.html("<%= j render partial: 'shared/view_link_contents', locals: {page: @content_info, content: @content, user: @user} %>") +<% else %> + target.html("<%= j render partial: 'shared/view_link_contents_error', locals: {url: @url, user: @user} %>") +<% end %> +feedbin.formatLinkContents(); diff --git a/app/views/feeds/_favicon.html.erb b/app/views/feeds/_favicon.html.erb deleted file mode 100644 index 6b11454478..0000000000 --- a/app/views/feeds/_favicon.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -<% present feed do |feed_presenter| %> - <%= feed_presenter.favicon_with_fallback %> -<% end %> diff --git a/app/views/feeds/search.js.erb b/app/views/feeds/search.js.erb index e1bd60260e..1f54e59fdb 100644 --- a/app/views/feeds/search.js.erb +++ b/app/views/feeds/search.js.erb @@ -1,6 +1,6 @@ <% if @feeds.present? %> $('#add_form_modal [data-behavior~=subscribe_target]').html('<%= j render partial: "subscriptions/new", locals: {feeds: @feeds} %>'); - $('#add_form_modal [data-behavior~=feeds_search_favicon_target]').html('<%= j render partial: "feeds/favicon", locals: {feed: @feeds.first} %>'); + $('#add_form_modal [data-behavior~=feeds_search_favicon_target]').html('<%= j favicon_with_host(@feeds.first.host) %>'); $('#add_form_modal .feed-search-results').slideDown(200) $('#add_form_modal .modal-dialog').addClass('done'); $('#add_form_modal [data-behavior~=submit_add]').removeAttr('disabled'); diff --git a/app/views/settings/appearance.html.erb b/app/views/settings/appearance.html.erb index 3cbcdc2e5d..de9cb5740c 100644 --- a/app/views/settings/appearance.html.erb +++ b/app/views/settings/appearance.html.erb @@ -20,16 +20,12 @@
  • - - - + <%= favicon_with_host("foodinjars.com") %>

    Food in Jars

    - - - + <%= favicon_with_host("foodinjars.com") %> Spicy Peach and Yellow Tomato Jam

    I made this jam back before my trip to Portland and have been meaning to share it…

    diff --git a/app/views/shared/_keyboard_shortcuts_wrap.html.erb b/app/views/shared/_keyboard_shortcuts_wrap.html.erb index 6f52bee176..d3cbf63e46 100644 --- a/app/views/shared/_keyboard_shortcuts_wrap.html.erb +++ b/app/views/shared/_keyboard_shortcuts_wrap.html.erb @@ -2,7 +2,7 @@